Skip to content

Commit

Permalink
Merge pull request #122 from nosarthur/shell
Browse files Browse the repository at this point in the history
implement shell subcommand
  • Loading branch information
nosarthur authored Jan 18, 2021
2 parents b88fe6e + f4d56d5 commit 559dedf
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 9 deletions.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ To see the pre-defined sub-commands, run `gita -h` or take a look at
[cmds.yml](https://github.com/nosarthur/gita/blob/master/gita/cmds.yml).
To add your own sub-commands, see the [customization section](#custom).
To run arbitrary `git` command, see the [superman mode section](#superman).
To run arbitrary shell command, see the [shell mode section](#shell).

The branch color distinguishes 5 situations between local and remote branches:

Expand Down Expand Up @@ -157,6 +158,21 @@ For example,
- `gita super frontend-repo backend-repo commit -am 'implement a new feature'`
executes `git commit -am 'implement a new feature'` for `frontend-repo` and `backend-repo`

## <a name='shell'></a> Shell mode

The shell mode delegates any shell command.
Usage:

```
gita shell [repo-name(s) or group-name(s)] <any-shell-command>
```

Here `repo-name(s)` or `group-name(s)` are optional, and their absence means all repos.
For example,

- `gita shell ll` lists contents for all repos
- `gita shell repo1 mkdir docs` create a new directory `docs` in repo1

## <a name='custom'></a> Customization

### user-defined sub-command using yaml file
Expand Down Expand Up @@ -195,6 +211,12 @@ comaster:
help: checkout the master branch
```

### customize the local/remote relationship coloring displayed by the `gita ll` command

You can see the default color scheme and the available colors via `gita color`.
To change the color coding, use `gita color set <situation> <color>`.
The configuration is saved in `$XDG_CONFIG_HOME/gita/color.yml`.

### customize information displayed by the `gita ll` command

You can customize the information displayed by `gita ll`.
Expand All @@ -208,12 +230,6 @@ For example, the default information items setting corresponds to
- commit_msg
```

### customize the local/remote relationship coloring displayed by the `gita ll` command

You can see the default color scheme and the available colors via `gita color`.
To change the color coding, use `gita color set <situation> <color>`.
The configuration is saved in `$XDG_CONFIG_HOME/gita/color.yml`.

## Requirements

Gita requires Python 3.6 or higher, due to the use of
Expand Down
51 changes: 51 additions & 0 deletions gita/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,42 @@ def f_git_cmd(args: argparse.Namespace):
subprocess.run(cmds, cwd=path)


def f_shell(args):
"""
Delegate shell command defined in `args.man`, which may or may not
contain repo names.
"""
names = []
repos = utils.get_repos()
groups = utils.get_groups()
ctx = utils.get_context()
for i, word in enumerate(args.man):
if word in repos or word in groups:
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 = args.man[i:]
for name, path in repos.items():
# TODO: pull this out as a function
got = subprocess.run(cmds, cwd=path, check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
print(utils.format_output(got.stdout.decode(), name))


def f_super(args):
"""
Delegate git command/alias defined in `args.man`, which may or may not
Expand Down Expand Up @@ -415,6 +451,21 @@ def main(argv=None):
"Another: gita super checkout master ")
p_super.set_defaults(func=f_super)

# shell mode
p_shell = subparsers.add_parser(
'shell',
description='shell mode: delegate any shell command in specified or '
'all repo(s).\n'
'Examples:\n \t gita shell pwd\n'
'\t gita shell repo1 repo2 repo3 touch xx')
p_shell.add_argument(
'man',
nargs=argparse.REMAINDER,
help="execute arbitrary shell command for specified or all repos "
"Example: gita shell myrepo1 ls"
"Another: gita shell git checkout master ")
p_shell.set_defaults(func=f_shell)

# sub-commands that fit boilerplate
cmds = utils.get_cmds_from_files()
for name, data in cmds.items():
Expand Down
4 changes: 2 additions & 2 deletions gita/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ async def run_async(repo_name: str, path: str, cmds: List[str]) -> Union[None, s
stdout, stderr = await process.communicate()
for pipe in (stdout, stderr):
if pipe:
print(format_output(pipe.decode(), f'{repo_name}: '))
print(format_output(pipe.decode(), repo_name))
# The existence of stderr is not good indicator since git sometimes write
# to stderr even if the execution is successful, e.g. git fetch
if process.returncode != 0:
Expand All @@ -178,7 +178,7 @@ def format_output(s: str, prefix: str):
"""
Prepends every line in given string with the given prefix.
"""
return ''.join([f'{prefix}{line}' for line in s.splitlines(keepends=True)])
return ''.join([f'{prefix}: {line}' for line in s.splitlines(keepends=True)])


def exec_async_tasks(tasks: List[Coroutine]) -> List[Union[None, str]]:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
setup(
name='gita',
packages=['gita'],
version='0.12.6',
version='0.12.7',
license='MIT',
description='Manage multiple git repos with sanity',
long_description=long_description,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ def test_superman(mock_run, _, input):
mock_run.assert_called_once_with(expected_cmds, cwd='path7')


@pytest.mark.parametrize('input', [
'diff --name-only --staged',
"commit -am 'lala kaka'",
])
@patch('gita.utils.get_repos', return_value={'repo7': 'path7'})
@patch('subprocess.run')
def test_shell(mock_run, _, input):
mock_run.reset_mock()
args = ['shell', 'repo7'] + shlex.split(input)
__main__.main(args)
expected_cmds = shlex.split(input)
mock_run.assert_called_once_with(expected_cmds, cwd='path7', check=True, stderr=-2, stdout=-1)


class TestContext:

@patch('gita.utils.get_context', return_value=None)
Expand Down

0 comments on commit 559dedf

Please sign in to comment.