diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2022-01-09 07:44:06 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2022-01-09 07:44:15 +0000 |
commit | 74e70d5613813fb731614dbdb4c9f722263a9cd0 (patch) | |
tree | efd3527249ffb15caf496a28d68fb792e988bd1b /gita | |
parent | Releasing debian version 0.15.9-1. (diff) | |
download | gita-74e70d5613813fb731614dbdb4c9f722263a9cd0.tar.xz gita-74e70d5613813fb731614dbdb4c9f722263a9cd0.zip |
Merging upstream version 0.16.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gita')
-rw-r--r-- | gita/__main__.py | 56 | ||||
-rw-r--r-- | gita/common.py | 15 | ||||
-rw-r--r-- | gita/utils.py | 100 |
3 files changed, 66 insertions, 105 deletions
diff --git a/gita/__main__.py b/gita/__main__.py index 7e5e6bd..b4057ec 100644 --- a/gita/__main__.py +++ b/gita/__main__.py @@ -59,34 +59,24 @@ def f_add(args: argparse.Namespace): repos = utils.get_repos() paths = args.paths groups = utils.get_groups() - if 0: - # add to global and tag as main - main_repos = utils.add_repos(repos, paths, repo_type='m') - # add sub-repo recursively and save to local config - for name, prop in main_repos.items(): - main_path = prop['path'] - print('Inside main repo:', name) - #sub_paths = Path(main_path).glob('**') - sub_paths = glob.glob(os.path.join(main_path,'**/'), recursive=True) - utils.add_repos({}, sub_paths, root=main_path) - else: - if args.recursive or args.auto_group: - paths = (p.rstrip(os.path.sep) for p in chain.from_iterable( - glob.glob(os.path.join(p, '**/'), recursive=True) - for p in args.paths)) - new_repos = utils.add_repos(repos, paths, is_bare=args.bare) - if new_repos and args.auto_group: - new_groups = utils.auto_group(new_repos, args.paths) - if new_groups: - print(f'Created {len(new_groups)} new group(s).') - utils.write_to_groups_file(new_groups, 'a+') - if new_repos and args.group: - gname = args.group - gname_repos = set(groups[gname]['repos']) - gname_repos.update(new_repos) - groups[gname]['repos'] = sorted(gname_repos) - print(f'Added {len(new_repos)} repos to the {gname} group') - utils.write_to_groups_file(groups, 'w') + if args.recursive or args.auto_group: + paths = (p.rstrip(os.path.sep) for p in chain.from_iterable( + glob.glob(os.path.join(p, '**/'), recursive=True) + for p in args.paths)) + new_repos = utils.add_repos(repos, paths, include_bare=args.bare, + exclude_submodule=args.skip_submodule) + if new_repos and args.auto_group: + new_groups = utils.auto_group(new_repos, args.paths) + if new_groups: + print(f'Created {len(new_groups)} new group(s).') + utils.write_to_groups_file(new_groups, 'a+') + if new_repos and args.group: + gname = args.group + gname_repos = set(groups[gname]['repos']) + gname_repos.update(new_repos) + groups[gname]['repos'] = sorted(gname_repos) + print(f'Added {len(new_repos)} repos to the {gname} group') + utils.write_to_groups_file(groups, 'w') def f_rename(args: argparse.Namespace): @@ -164,11 +154,11 @@ def f_freeze(_): seen = {''} for name, prop in repos.items(): path = prop['path'] - # TODO: What do we do with main repos? Maybe give an option to print - # their sub-repos too. url = '' - cp = subprocess.run(['git', 'remote', '-v'], cwd=path, capture_output=True) - lines = cp.stdout.decode('utf-8').split('\n') + # FIXME: capture_output is new in 3.7. Maybe drop support for 3.6 + cp = subprocess.run(['git', 'remote', '-v'], cwd=path, + universal_newlines=True, capture_output=True) + lines = cp.stdout.split('\n') if cp.returncode == 0 and len(lines) > 0: parts = lines[0].split() if len(parts)>1: @@ -402,6 +392,8 @@ def main(argv=None): p_add.add_argument('-g','--group', choices=utils.get_groups(), help="add repo(s) to the specified group") + p_add.add_argument('-s', '--skip-submodule', action='store_true', + help="skip submodule repo(s)") xgroup = p_add.add_mutually_exclusive_group() xgroup.add_argument('-r', '--recursive', action='store_true', help="recursively add repo(s) in the given path(s).") diff --git a/gita/common.py b/gita/common.py index abbef5f..e1a3dde 100644 --- a/gita/common.py +++ b/gita/common.py @@ -1,17 +1,14 @@ import os -def get_config_dir(root=None) -> str: - if root is None: - root = os.environ.get('XDG_CONFIG_HOME') or os.path.join( - os.path.expanduser('~'), '.config') - return os.path.join(root, "gita") - else: - return os.path.join(root, ".gita") +def get_config_dir() -> str: + root = os.environ.get('XDG_CONFIG_HOME') or os.path.join( + os.path.expanduser('~'), '.config') + return os.path.join(root, "gita") -def get_config_fname(fname: str, root=None) -> str: +def get_config_fname(fname: str) -> str: """ Return the file name that stores the repo locations. """ - return os.path.join(get_config_dir(root), fname) + return os.path.join(get_config_dir(), fname) diff --git a/gita/utils.py b/gita/utils.py index 2431fde..8e7d9c4 100644 --- a/gita/utils.py +++ b/gita/utils.py @@ -38,14 +38,12 @@ def get_relative_path(kid: str, parent: str) -> Union[List[str], None]: @lru_cache() -def get_repos(root=None) -> Dict[str, Dict[str, str]]: +def get_repos() -> Dict[str, Dict[str, str]]: """ Return a `dict` of repo name to repo absolute path and repo type - @param root: Use local config if set. If None, use either global or local - config depending on cwd. """ - path_file = common.get_config_fname('repos.csv', root) + path_file = common.get_config_fname('repos.csv') repos = {} if os.path.isfile(path_file) and os.stat(path_file).st_size > 0: with open(path_file) as f: @@ -54,13 +52,7 @@ def get_repos(root=None) -> Dict[str, Dict[str, str]]: repos = {r['name']: {'path': r['path'], 'type': r['type'], 'flags': r['flags'].split()} - for r in rows if is_git(r['path'], is_bare=True)} - if root is None: # detect if inside a main path - cwd = os.getcwd() - for prop in repos.values(): - path = prop['path'] - if prop['type'] == 'm' and get_relative_path(cwd, path) != MAX_INT: - return get_repos(path) + for r in rows if is_git(r['path'], include_bare=True)} return repos @@ -82,7 +74,6 @@ def get_context() -> Union[Path, None]: ctx = matches[0] if ctx.stem == 'auto': cwd = str(Path.cwd()) - repos = get_repos() # The context is set to be the group with minimal distance to cwd candidate = None min_dist = MAX_INT @@ -102,7 +93,7 @@ def get_context() -> Union[Path, None]: @lru_cache() -def get_groups() -> Dict[str, Dict]: +def get_groups() -> Dict[str, Dict[str, Union[str, List]]]: """ Return a `dict` of group name to group properties such as repo names and group path. @@ -152,7 +143,7 @@ def replace_context(old: Union[Path, None], new: str): # ctx.rename(ctx.with_stem(new_name)) # only works in py3.9 old.rename(old.with_name(f'{new}.context')) else: - open(auto.with_name(f'{new}.context'), 'w').close() + Path(auto.with_name(f'{new}.context')).write_text('') def get_choices() -> List[Union[str, None]]: @@ -170,7 +161,16 @@ def get_choices() -> List[Union[str, None]]: return choices -def is_git(path: str, is_bare=False) -> bool: +def is_submodule_repo(p: Path) -> bool: + """ + + """ + if p.is_file() and '.git/modules' in p.read_text(): + return True + return False + + +def is_git(path: str, include_bare=False, exclude_submodule=False) -> bool: """ Return True if the path is a git repo. """ @@ -178,16 +178,18 @@ def is_git(path: str, is_bare=False) -> bool: return False # An alternative is to call `git rev-parse --is-inside-work-tree` # I don't see why that one is better yet. - # For a regular git repo, .git is a folder, for a worktree repo, .git is a file. - # However, git submodule repo also has .git as a file. + # For a regular git repo, .git is a folder. For a worktree repo and + # submodule repo, .git is a file. # A more reliable way to differentiable regular and worktree repos is to # compare the result of `git rev-parse --git-dir` and # `git rev-parse --git-common-dir` loc = os.path.join(path, '.git') # TODO: we can display the worktree repos in a different font. if os.path.exists(loc): + if exclude_submodule and is_submodule_repo(Path(loc)): + return False return True - if not is_bare: + if not include_bare: return False # detect bare repo got = subprocess.run('git rev-parse --is-bare-repository'.split(), @@ -209,37 +211,26 @@ def rename_repo(repos: Dict[str, Dict[str, str]], repo: str, new_name: str): prop = repos[repo] del repos[repo] repos[new_name] = prop - # write to local config if inside a main path - main_paths = (prop['path'] for prop in repos.values() if prop['type'] == 'm') - cwd = os.getcwd() - is_local_config = True - # TODO: delete - for p in main_paths: - if get_relative_path(cwd, p) != MAX_INT: - write_to_repo_file(repos, 'w', p) - break - else: # global config - write_to_repo_file(repos, 'w') - is_local_config = False - # update groups only when outside any main repos - if is_local_config: - return + write_to_repo_file(repos, 'w') + groups = get_groups() - for g, members in groups.items(): + for g, values in groups.items(): + members = values['repos'] if repo in members: members.remove(repo) members.append(new_name) - groups[g] = sorted(members) + groups[g]['repos'] = sorted(members) write_to_groups_file(groups, 'w') -def write_to_repo_file(repos: Dict[str, Dict[str, str]], mode: str, root=None): +def write_to_repo_file(repos: Dict[str, Dict[str, str]], mode: str): """ @param repos: each repo is {name: {properties}} """ - data = [(prop['path'], name, prop['type'], ' '.join(prop['flags'])) + # The 3rd column is repo type; unused field + data = [(prop['path'], name, '', ' '.join(prop['flags'])) for name, prop in repos.items()] - fname = common.get_config_fname('repos.csv', root) + fname = common.get_config_fname('repos.csv') os.makedirs(os.path.dirname(fname), exist_ok=True) with open(fname, mode, newline='') as f: writer = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) @@ -254,7 +245,7 @@ def write_to_groups_file(groups: Dict[str, Dict], mode: str): fname = common.get_config_fname('groups.csv') os.makedirs(os.path.dirname(fname), exist_ok=True) if not groups: # all groups are deleted - open(fname, 'w').close() + Path(fname).write_text('') else: # delete the group if there are no repos for name in list(groups): @@ -285,27 +276,17 @@ def _make_name(path: str, repos: Dict[str, Dict[str, str]], return name -# TODO: delete -def _get_repo_type(path, repo_type, root) -> str: - """ - - """ - if repo_type != '': # explicitly set - return repo_type - if root is not None and os.path.normpath(root) == os.path.normpath(path): - return 'm' - return '' - - def add_repos(repos: Dict[str, Dict[str, str]], new_paths: List[str], - repo_type='', root=None, is_bare=False) -> Dict[str, Dict[str, str]]: + include_bare=False, + exclude_submodule=False, + ) -> Dict[str, Dict[str, str]]: """ Write new repo paths to file; return the added repos. @param repos: name -> path """ existing_paths = {prop['path'] for prop in repos.values()} - new_paths = {p for p in new_paths if is_git(p, is_bare)} + new_paths = {p for p in new_paths if is_git(p, include_bare, exclude_submodule)} new_paths = new_paths - existing_paths new_repos = {} if new_paths: @@ -315,12 +296,9 @@ def add_repos(repos: Dict[str, Dict[str, str]], new_paths: List[str], ) new_repos = {_make_name(path, repos, name_counts): { 'path': path, - 'type': _get_repo_type(path, repo_type, root), 'flags': '', } for path in new_paths} - # When root is not None, we could optionally set its type to 'm', i.e., - # main repo. - write_to_repo_file(new_repos, 'a+', root) + write_to_repo_file(new_repos, 'a+') else: print('No new repos found!') return new_repos @@ -442,13 +420,7 @@ def describe(repos: Dict[str, Dict[str, str]], no_colors: bool = False) -> str: for name in sorted(repos): info_items = ' '.join(f(repos[name]) for f in funcs) - if repos[name]['type'] == 'm': - # ANSI color code also takes length in Python - name = f'{info.Color.underline}{name}{info.Color.end}' - width = name_width + 8 - yield f'{name:<{width}}{info_items}' - else: - yield f'{name:<{name_width}}{info_items}' + yield f'{name:<{name_width}}{info_items}' def get_cmds_from_files() -> Dict[str, Dict[str, str]]: |