Adding upstream version 0.15.8.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
6204f01115
commit
495df48c0a
6 changed files with 91 additions and 72 deletions
|
@ -361,6 +361,13 @@ Thus the installed git version may matter.
|
||||||
I have git `1.8.3.1`, `2.17.2`, and `2.20.1` on my machines, and
|
I have git `1.8.3.1`, `2.17.2`, and `2.20.1` on my machines, and
|
||||||
their results agree.
|
their results agree.
|
||||||
|
|
||||||
|
## tips
|
||||||
|
|
||||||
|
effect | shell command
|
||||||
|
---|---
|
||||||
|
enter `<repo>` directory|`` cd `gita ls <repo>` ``
|
||||||
|
delete repos in `<group>` | `gita group ll <group> \| xargs gita rm`
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
To contribute, you can
|
To contribute, you can
|
||||||
|
|
|
@ -55,7 +55,6 @@
|
||||||
- `gita add -a <repo-parent-path(s)>`: 递归添加路径下的所有库并自动产生层级分组,见
|
- `gita add -a <repo-parent-path(s)>`: 递归添加路径下的所有库并自动产生层级分组,见
|
||||||
[customization section](#custom)
|
[customization section](#custom)
|
||||||
- `gita add -b <bare-repo-path(s)>`: 添加bare库
|
- `gita add -b <bare-repo-path(s)>`: 添加bare库
|
||||||
- `gita add -m <main-repo-path(s)>`: 添加main库, main库的定义见
|
|
||||||
[customization section](#custom)
|
[customization section](#custom)
|
||||||
- `gita add -r <repo-parent-path(s)>`: 递归添加路径下的所有库
|
- `gita add -r <repo-parent-path(s)>`: 递归添加路径下的所有库
|
||||||
- `gita clone <config-file>`: 克隆`<config-file>` (由`gita freeze`生成)里的库
|
- `gita clone <config-file>`: 克隆`<config-file>` (由`gita freeze`生成)里的库
|
||||||
|
|
101
gita/__main__.py
101
gita/__main__.py
|
@ -186,9 +186,9 @@ def f_ll(args: argparse.Namespace):
|
||||||
for line in utils.describe(repos, no_colors=args.no_colors):
|
for line in utils.describe(repos, no_colors=args.no_colors):
|
||||||
print(' ', line)
|
print(' ', line)
|
||||||
else:
|
else:
|
||||||
for g, g_repos in utils.get_groups().items():
|
for g, prop in utils.get_groups().items():
|
||||||
print(f'{g}:')
|
print(f'{g}:')
|
||||||
g_repos = {k: repos[k] for k in g_repos if k in repos}
|
g_repos = {k: repos[k] for k in prop['repos'] if k in repos}
|
||||||
for line in utils.describe(g_repos, no_colors=args.no_colors):
|
for line in utils.describe(g_repos, no_colors=args.no_colors):
|
||||||
print(' ', line)
|
print(' ', line)
|
||||||
else:
|
else:
|
||||||
|
@ -286,26 +286,16 @@ def f_rm(args: argparse.Namespace):
|
||||||
path_file = common.get_config_fname('repos.csv')
|
path_file = common.get_config_fname('repos.csv')
|
||||||
if os.path.isfile(path_file):
|
if os.path.isfile(path_file):
|
||||||
repos = utils.get_repos()
|
repos = utils.get_repos()
|
||||||
main_paths = [prop['path'] for prop in repos.values() if prop['type'] == 'm']
|
|
||||||
# TODO: add test case to delete main repo from main repo
|
|
||||||
# only local setting should be affected instead of the global one
|
|
||||||
group_updated = False
|
group_updated = False
|
||||||
|
groups = utils.get_groups()
|
||||||
for repo in args.repo:
|
for repo in args.repo:
|
||||||
del repos[repo]
|
del repos[repo]
|
||||||
groups = utils.get_groups()
|
up = utils.delete_repo_from_groups(repo, groups)
|
||||||
group_updated = group_updated or utils.delete_repo_from_groups(repo, groups)
|
group_updated = group_updated or up
|
||||||
if group_updated:
|
if group_updated:
|
||||||
utils.write_to_groups_file(groups, 'w')
|
utils.write_to_groups_file(groups, 'w')
|
||||||
|
|
||||||
# If cwd is relative to any main repo, write to local config
|
utils.write_to_repo_file(repos, 'w')
|
||||||
cwd = os.getcwd()
|
|
||||||
# TODO: delete main path mechanism
|
|
||||||
for p in main_paths:
|
|
||||||
if utils.get_relative_path(cwd, p) is not None:
|
|
||||||
utils.write_to_repo_file(repos, 'w', p)
|
|
||||||
break
|
|
||||||
else: # global config
|
|
||||||
utils.write_to_repo_file(repos, 'w')
|
|
||||||
|
|
||||||
|
|
||||||
def f_git_cmd(args: argparse.Namespace):
|
def f_git_cmd(args: argparse.Namespace):
|
||||||
|
@ -313,20 +303,11 @@ def f_git_cmd(args: argparse.Namespace):
|
||||||
Delegate git command/alias defined in `args.cmd`. Asynchronous execution is
|
Delegate git command/alias defined in `args.cmd`. Asynchronous execution is
|
||||||
disabled for commands in the `args.async_blacklist`.
|
disabled for commands in the `args.async_blacklist`.
|
||||||
"""
|
"""
|
||||||
repos = utils.get_repos()
|
if '_parsed_repos' in args:
|
||||||
groups = utils.get_groups()
|
repos = args._parsed_repos
|
||||||
ctx = utils.get_context()
|
else:
|
||||||
if not args.repo and ctx:
|
repos, _ = utils.parse_repos_and_rest(args.repo)
|
||||||
args.repo = [ctx.stem]
|
|
||||||
if args.repo: # with user specified repo(s) or group(s)
|
|
||||||
chosen = {}
|
|
||||||
for k in args.repo:
|
|
||||||
if k in repos:
|
|
||||||
chosen[k] = repos[k]
|
|
||||||
if k in groups:
|
|
||||||
for r in groups[k]['repos']:
|
|
||||||
chosen[r] = repos[r]
|
|
||||||
repos = chosen
|
|
||||||
per_repo_cmds = []
|
per_repo_cmds = []
|
||||||
for prop in repos.values():
|
for prop in repos.values():
|
||||||
cmds = args.cmd.copy()
|
cmds = args.cmd.copy()
|
||||||
|
@ -361,29 +342,12 @@ def f_shell(args):
|
||||||
Delegate shell command defined in `args.man`, which may or may not
|
Delegate shell command defined in `args.man`, which may or may not
|
||||||
contain repo names.
|
contain repo names.
|
||||||
"""
|
"""
|
||||||
names = []
|
repos, cmds = utils.parse_repos_and_rest(args.man)
|
||||||
repos = utils.get_repos()
|
if not cmds:
|
||||||
groups = utils.get_groups()
|
print('Missing commands')
|
||||||
ctx = utils.get_context()
|
sys.exit(2)
|
||||||
for i, word in enumerate(args.man):
|
|
||||||
if word in repos or word in groups:
|
cmds = ' '.join(cmds) # join the shell command into a single string
|
||||||
names.append(word)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
args.repo = names
|
|
||||||
# TODO: redundant with f_git_cmd
|
|
||||||
if not args.repo and ctx:
|
|
||||||
args.repo = [ctx.stem]
|
|
||||||
if args.repo: # with user specified repo(s) or group(s)
|
|
||||||
chosen = {}
|
|
||||||
for k in args.repo:
|
|
||||||
if k in repos:
|
|
||||||
chosen[k] = repos[k]
|
|
||||||
if k in groups:
|
|
||||||
for r in groups[k]:
|
|
||||||
chosen[r] = repos[r]
|
|
||||||
repos = chosen
|
|
||||||
cmds = ' '.join(args.man[i:]) # join the shell command into a single string
|
|
||||||
for name, prop in repos.items():
|
for name, prop in repos.items():
|
||||||
# TODO: pull this out as a function
|
# TODO: pull this out as a function
|
||||||
got = subprocess.run(cmds, cwd=prop['path'], shell=True,
|
got = subprocess.run(cmds, cwd=prop['path'], shell=True,
|
||||||
|
@ -397,16 +361,13 @@ def f_super(args):
|
||||||
Delegate git command/alias defined in `args.man`, which may or may not
|
Delegate git command/alias defined in `args.man`, which may or may not
|
||||||
contain repo names.
|
contain repo names.
|
||||||
"""
|
"""
|
||||||
names = []
|
repos, cmds = utils.parse_repos_and_rest(args.man)
|
||||||
repos = utils.get_repos()
|
if not cmds:
|
||||||
groups = utils.get_groups()
|
print('Missing commands')
|
||||||
for i, word in enumerate(args.man):
|
sys.exit(2)
|
||||||
if word in repos or word in groups:
|
|
||||||
names.append(word)
|
args.cmd = ['git'] + cmds
|
||||||
else:
|
args._parsed_repos = repos
|
||||||
break
|
|
||||||
args.cmd = ['git'] + args.man[i:]
|
|
||||||
args.repo = names
|
|
||||||
args.shell = False
|
args.shell = False
|
||||||
f_git_cmd(args)
|
f_git_cmd(args)
|
||||||
|
|
||||||
|
@ -625,15 +586,15 @@ def main(argv=None):
|
||||||
p_super = subparsers.add_parser(
|
p_super = subparsers.add_parser(
|
||||||
'super',
|
'super',
|
||||||
help='run any git command/alias',
|
help='run any git command/alias',
|
||||||
description='Superman mode: delegate any git command/alias in specified or '
|
description='Superman mode: delegate any git command/alias in specified repo(s), group(s), or '
|
||||||
'all repo(s).\n'
|
'all repo(s).\n'
|
||||||
'Examples:\n \t gita super myrepo1 commit -am "fix a bug"\n'
|
'Examples:\n \t gita super myrepo1 commit -am "fix a bug"\n'
|
||||||
'\t gita super repo1 repo2 repo3 checkout new-feature')
|
'\t gita super repo1 repo2 repo3 checkout new-feature')
|
||||||
p_super.add_argument(
|
p_super.add_argument(
|
||||||
'man',
|
'man',
|
||||||
nargs=argparse.REMAINDER,
|
nargs=argparse.REMAINDER,
|
||||||
help="execute arbitrary git command/alias for specified or all repos\n"
|
help="execute arbitrary git command/alias for specified repo(s), group(s), or all repos.\n"
|
||||||
"Example: gita super myrepo1 diff --name-only --staged "
|
"Example: gita super myrepo1 diff --name-only --staged\n"
|
||||||
"Another: gita super checkout master ")
|
"Another: gita super checkout master ")
|
||||||
p_super.set_defaults(func=f_super)
|
p_super.set_defaults(func=f_super)
|
||||||
|
|
||||||
|
@ -641,15 +602,15 @@ def main(argv=None):
|
||||||
p_shell = subparsers.add_parser(
|
p_shell = subparsers.add_parser(
|
||||||
'shell',
|
'shell',
|
||||||
help='run any shell command',
|
help='run any shell command',
|
||||||
description='shell mode: delegate any shell command in specified or '
|
description='shell mode: delegate any shell command in specified repo(s), group(s), or '
|
||||||
'all repo(s).\n'
|
'all repo(s).\n'
|
||||||
'Examples:\n \t gita shell pwd\n'
|
'Examples:\n \t gita shell pwd; \n'
|
||||||
'\t gita shell repo1 repo2 repo3 touch xx')
|
'\t gita shell repo1 repo2 repo3 touch xx')
|
||||||
p_shell.add_argument(
|
p_shell.add_argument(
|
||||||
'man',
|
'man',
|
||||||
nargs=argparse.REMAINDER,
|
nargs=argparse.REMAINDER,
|
||||||
help="execute arbitrary shell command for specified or all repos "
|
help="execute arbitrary shell command for specified repo(s), group(s), or all repos.\n"
|
||||||
"Example: gita shell myrepo1 ls"
|
"Example: gita shell myrepo1 ls\n"
|
||||||
"Another: gita shell git checkout master ")
|
"Another: gita shell git checkout master ")
|
||||||
p_shell.set_defaults(func=f_shell)
|
p_shell.set_defaults(func=f_shell)
|
||||||
|
|
||||||
|
|
|
@ -256,6 +256,10 @@ def write_to_groups_file(groups: Dict[str, Dict], mode: str):
|
||||||
if not groups: # all groups are deleted
|
if not groups: # all groups are deleted
|
||||||
open(fname, 'w').close()
|
open(fname, 'w').close()
|
||||||
else:
|
else:
|
||||||
|
# delete the group if there are no repos
|
||||||
|
for name in list(groups):
|
||||||
|
if not groups[name]['repos']:
|
||||||
|
del groups[name]
|
||||||
with open(fname, mode, newline='') as f:
|
with open(fname, mode, newline='') as f:
|
||||||
data = [
|
data = [
|
||||||
(group, ' '.join(prop['repos']), prop['path'])
|
(group, ' '.join(prop['repos']), prop['path'])
|
||||||
|
@ -475,3 +479,38 @@ def get_cmds_from_files() -> Dict[str, Dict[str, str]]:
|
||||||
# custom commands shadow default ones
|
# custom commands shadow default ones
|
||||||
cmds.update(custom_cmds)
|
cmds.update(custom_cmds)
|
||||||
return cmds
|
return cmds
|
||||||
|
|
||||||
|
|
||||||
|
def parse_repos_and_rest(input: List[str]
|
||||||
|
) -> Tuple[Dict[str, Dict[str, str]], List[str]]:
|
||||||
|
"""
|
||||||
|
Parse gita input arguments
|
||||||
|
|
||||||
|
@return: repos and the rest (e.g., gita shell and super commands)
|
||||||
|
"""
|
||||||
|
i = None
|
||||||
|
names = []
|
||||||
|
repos = get_repos()
|
||||||
|
groups = get_groups()
|
||||||
|
ctx = get_context()
|
||||||
|
for i, word in enumerate(input):
|
||||||
|
if word in repos or word in groups:
|
||||||
|
names.append(word)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else: # all input is repos and groups, shift the index once more
|
||||||
|
if i is not None:
|
||||||
|
i += 1
|
||||||
|
if not names and ctx:
|
||||||
|
names = [ctx.stem]
|
||||||
|
if names:
|
||||||
|
chosen = {}
|
||||||
|
for k in names:
|
||||||
|
if k in repos:
|
||||||
|
chosen[k] = repos[k]
|
||||||
|
if k in groups:
|
||||||
|
for r in groups[k]['repos']:
|
||||||
|
chosen[r] = repos[r]
|
||||||
|
# if not set here, all repos are chosen
|
||||||
|
repos = chosen
|
||||||
|
return repos, input[i:]
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -7,7 +7,7 @@ with open('README.md', encoding='utf-8') as f:
|
||||||
setup(
|
setup(
|
||||||
name='gita',
|
name='gita',
|
||||||
packages=['gita'],
|
packages=['gita'],
|
||||||
version='0.15.7',
|
version='0.15.7.3',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
description='Manage multiple git repos with sanity',
|
description='Manage multiple git repos with sanity',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
|
|
|
@ -10,6 +10,19 @@ from conftest import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('input, expected', [
|
||||||
|
([], ({'repo1': {'path': '/a/bcd/repo1', 'type': '', 'flags': []}, 'xxx': {'path': '/a/b/c/repo3', 'type': '', 'flags': []}, 'repo2': {'path': '/e/fgh/repo2', 'type': '', 'flags': []}}, [])),
|
||||||
|
(['st'], ({'repo1': {'path': '/a/bcd/repo1', 'type': '', 'flags': []}, 'xxx': {'path': '/a/b/c/repo3', 'type': '', 'flags': []}, 'repo2': {'path': '/e/fgh/repo2', 'type': '', 'flags': []}}, ['st'])),
|
||||||
|
(['repo1', 'st'], ({'repo1': {'flags': [], 'path': '/a/bcd/repo1', 'type': ''}}, ['st'])),
|
||||||
|
(['repo1'], ({'repo1': {'flags': [], 'path': '/a/bcd/repo1', 'type': ''}}, [])),
|
||||||
|
])
|
||||||
|
@patch('gita.utils.is_git', return_value=True)
|
||||||
|
@patch('gita.common.get_config_fname', return_value=PATH_FNAME)
|
||||||
|
def test_parse_repos_and_rest(mock_path_fname, _, input, expected):
|
||||||
|
got = utils.parse_repos_and_rest(input)
|
||||||
|
assert got == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('repo_path, paths, expected', [
|
@pytest.mark.parametrize('repo_path, paths, expected', [
|
||||||
('/a/b/c/repo', ['/a/b'], (('b', 'c'), '/a')),
|
('/a/b/c/repo', ['/a/b'], (('b', 'c'), '/a')),
|
||||||
])
|
])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue