summaryrefslogtreecommitdiffstats
path: root/gita/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'gita/utils.py')
-rw-r--r--gita/utils.py100
1 files changed, 36 insertions, 64 deletions
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]]: