[![PyPi version](https://img.shields.io/pypi/v/gita.svg?color=blue)](https://pypi.org/project/gita/) [![Build Status](https://travis-ci.org/nosarthur/gita.svg?branch=master)](https://travis-ci.org/nosarthur/gita) [![codecov](https://codecov.io/gh/nosarthur/gita/branch/master/graph/badge.svg)](https://codecov.io/gh/nosarthur/gita) [![licence](https://img.shields.io/pypi/l/gita.svg)](https://github.com/nosarthur/gita/blob/master/LICENSE) [![PyPI - Downloads](https://img.shields.io/pypi/dm/gita.svg)](https://pypistats.org/packages/gita) [![Gitter](https://badges.gitter.im/nosarthur/gita.svg)](https://gitter.im/nosarthur/gita?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Chinese](https://img.shields.io/badge/-中文-lightgrey.svg)](https://github.com/nosarthur/gita/blob/master/doc/README_CN.md) ``` _______________________________ ( ____ \__ __|__ __( ___ ) | ( \/ ) ( ) ( | ( ) | | | | | | | | (___) | | | ____ | | | | | ___ | | | \_ ) | | | | | ( ) | | (___) |__) (___ | | | ) ( | (_______)_______/ )_( |/ \| v0.15 ``` # Gita: a command-line tool to manage multiple git repos This tool does two things - display the status of multiple git repos such as branch, modification, commit message side by side - (batch) delegate git commands/aliases from any working directory If several repos are related, it helps to see their status together. I also hate to change directories to execute git commands. ![gita screenshot](https://github.com/nosarthur/gita/raw/master/doc/screenshot.png) In this screenshot, the `gita ll` command displays the status of all repos. The `gita remote dotfiles` command translates to `git remote -v` for the `dotfiles` repo, even though we are not in the repo. The `gita fetch` command fetches from all repos and two of them have updates. To see the pre-defined commands, run `gita -h` or take a look at [cmds.json](https://github.com/nosarthur/gita/blob/master/gita/cmds.json). To add your own 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: color | meaning ---|--- white| local has no remote green| local is the same as remote red| local has diverged from remote purple| local is ahead of remote (good for push) yellow| local is behind remote (good for merge) The choice of purple for ahead and yellow for behind is motivated by [blueshift](https://en.wikipedia.org/wiki/Blueshift) and [redshift](https://en.wikipedia.org/wiki/Redshift), using green as baseline. You can change the color scheme using the `gita color` command. See the [customization section](#custom). The additional status symbols denote - `+`: staged changes - `*`: unstaged changes - `_`: untracked files/folders The bookkeeping sub-commands are - `gita add `: add repo(s) to `gita` - `gita add -a `: add repo(s) in recursively and automatically generate hierarchical groups. See the [customization section](#custom) for more details. - `gita add -b `: add bare repo(s) to `gita`. See the [customization section](#custom) for more details on setting custom worktree. - `gita add -m `: add main repo(s) to `gita`. See the [customization section](#custom) for more details. - `gita add -r `: add repo(s) in recursively - `gita clone `: clone repos in `config-file` (generated by `gita freeze`) to current directory. - `gita clone -p `: clone repos in `config-file` to prescribed paths. - `gita context`: context sub-command - `gita context`: show current context - `gita context none`: remove context - `gita context `: set context to `group-name`, all operations then only apply to repos in this group - `gita color`: color sub-command - `gita color [ll]`: Show available colors and the current coloring scheme - `gita color set `: Use the specified color for the local-remote situation - `gita flags`: flags sub-command - `gita flags set `: add custom `flags` to repo - `gita flags [ll]`: display repos with custom flags - `gita freeze`: print information of all repos such as URL, name, and path. - `gita group`: group sub-command - `gita group add -n `: add repo(s) to a new or existing group - `gita group [ll]`: display existing groups with repos - `gita group ls`: display existing group names - `gita group rename `: change group name - `gita group rm `: delete group(s) - `gita group rmrepo -n `: remove repo(s) from existing group - `gita info`: info sub-command - `gita info [ll]`: display the used and unused information items - `gita info add `: enable information item - `gita info rm `: disable information item - `gita ll`: display the status of all repos - `gita ll `: display the status of repos in a group - `gita ls`: display the names of all repos - `gita ls `: display the absolute path of one repo - `gita rename `: rename a repo - `gita rm `: remove repo(s) from `gita` (won't remove files on disk) - `gita -v`: display gita version The `git` delegating sub-commands are of two formats - `gita [repo-name(s) or group-name(s)]`: optional repo or group input, and **no input means all repos**. - `gita `: required repo name(s) or group name(s) input They translate to `git ` for the corresponding repos. By default, only `fetch` and `pull` take optional input. In other words, `gita fetch` and `gita pull` apply to all repos. To see the pre-defined sub-commands, run `gita -h` or take a look at [cmds.json](https://github.com/nosarthur/gita/blob/master/gita/cmds.json). To add your own sub-commands or override the default behaviors, see the [customization section](#custom). To run arbitrary `git` command, see the [superman mode section](#superman). If more than one repos are specified, the `git` command runs asynchronously, with the exception of `log`, `difftool` and `mergetool`, which require non-trivial user input. Repo configuration is saved in `$XDG_CONFIG_HOME/gita/repos.csv` (most likely `~/.config/gita/repos.csv`). ## Installation To install the latest version, run ``` pip3 install -U gita ``` If you prefer development mode, download the source code and run ``` pip3 install -e ``` In either case, calling `gita` in terminal may not work, then put the following line in the `.bashrc` file. ``` alias gita="python3 -m gita" ``` Windows users may need to enable the ANSI escape sequence in terminal for the branch color to work. See [this stackoverflow post](https://stackoverflow.com/questions/51680709/colored-text-output-in-powershell-console-using-ansi-vt100-codes) for details. ## Auto-completion Download [.gita-completion.bash](https://github.com/nosarthur/gita/blob/master/.gita-completion.bash) or [.gita-completion.zsh](https://github.com/nosarthur/gita/blob/master/.gita-completion.zsh) and source it in shell. ## Superman mode The superman mode delegates any `git` command or alias. Usage: ``` gita super [repo-name(s) or group-name(s)] ``` Here `repo-name(s)` or `group-name(s)` are optional, and their absence means all repos. For example, - `gita super checkout master` puts all repos on the master branch - `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` ## Shell mode The shell mode delegates any shell command. Usage: ``` gita shell [repo-name(s) or group-name(s)] ``` 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 repo2 mkdir docs` create a new directory `docs` in `repo1` and `repo2` - `gita shell "git describe --abbrev=0 --tags | xargs git checkout"`: check out the latest tag for all repos ## Customization ### define repo group and context When the project contains several independent but related repos, we can define a group and execute `gita` command on this group. For example, ``` gita group add repo1 repo2 -n my-group gita ll my-group gita pull my-group ``` To save more typing, one can set a group as context, then any `gita` command is scoped to the group ``` gita context my-group gita ll gita pull ``` It is also possible to recursively add repos within a directory and generate hierarchical groups automatically. For example, running ``` gita add -a src ``` on the following folder structure ``` src ├── project1 │   ├── repo1 │   └── repo2 ├── repo3 ├── project2 │   ├── repo4 │   └── repo5 └── repo6 ``` gives rise to ``` src:repo1,repo2,repo3,repo4,repo5,repo6 src-project1:repo1,repo2 src-project2:repo4,repo5 ``` ### define main repos and shadow the global configuration setting with local setting The so-called main repos contain `.gita` folder for local configurations. It works best for the repos-within-repo project structure, for example, ``` main-repo ├── sub-repo1 │   └── sub-sub-repo ├── sub-repo2 └── sub-repo3 ``` When executing `gita` commands within/relative to a main repo, local configurations are used. And only repos within the current main repos are in the scope. To add a main repo, run ``` gita add -m main-repo-path ``` Subordinate repos are added recursively to the local configuration. Only the main repo is saved to the global configuration. In the `gita ll` display, the main repos are underlined. ### add user-defined sub-command using json file Custom delegating sub-commands can be defined in `$XDG_CONFIG_HOME/gita/cmds.json` (most likely `~/.config/gita/cmds.json`) And they shadow the default ones if name collisions exist. Default delegating sub-commands are defined in [cmds.json](https://github.com/nosarthur/gita/blob/master/gita/cmds.json). For example, `gita stat ` is registered as ```json "stat":{ "cmd": "git diff --stat", "help": "show edit statistics" } ``` which executes `git diff --stat` for the specified repo(s). To disable asynchronous execution, set `disable_async` to be `true`. See the `difftool` example: ```json "difftool":{ "cmd": "git difftool", "disable_async": true, "help": "show differences using a tool" } ``` If you want a custom command to behave like `gita fetch`, i.e., to apply to all repos when no repo is specified, set `allow_all` to be `true`. For example, the following snippet creates a new command `gita comaster [repo-name(s)]` with optional repo name input. ```json "comaster":{ "cmd": "checkout master", "allow_all": true, "help": "checkout the master branch" } ``` Any command that runs in the [superman mode](#superman) mode or the [shell mode](#shell) can be defined in this json format. For example, the following command runs in shell mode and fetches only the current branch from upstream. ```json "fetchcrt":{ "cmd": "git rev-parse --abbrev-ref HEAD | xargs git fetch --prune upstream", "allow_all": true, "shell": true, "help": "fetch current branch only" } ``` ### 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 `. The configuration is saved in `$XDG_CONFIG_HOME/gita/color.csv`. ### customize information displayed by the `gita ll` command You can customize the information displayed by `gita ll`. The used and unused information items are shown with `gita info`, and the configuration is saved in `$XDG_CONFIG_HOME/gita/info.csv`. For example, the default setting corresponds to ```csv branch,commit_msg,commit_time ``` ### customize git command flags One can set custom flags to run `git` commands. For example ``` gita flags set my-repo --git-dir=$HOME/somefolder --work-tree=$HOME ``` Then any `git` command/alias triggered from `gita` on `my-repo` will use these flags. Note that the flags are applied immediately after `git`. For example, `gita st my-repo` translates to ``` git --git-dir=$HOME/somefolder --work-tree=$HOME status ``` running from the `my-repo` directory. ## Requirements Gita requires Python 3.6 or higher, due to the use of [f-string](https://www.python.org/dev/peps/pep-0498/) and [asyncio module](https://docs.python.org/3.6/library/asyncio.html). Under the hood, gita uses `subprocess` to run git commands/aliases. 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 their results agree. ## Contributing To contribute, you can - report/fix bugs - request/implement features - star/recommend this project Read [this article](https://www.dataschool.io/how-to-contribute-on-github/) if you have never contribute code to open source project before. Chat room is available on [![Join the chat at https://gitter.im/nosarthur/gita](https://badges.gitter.im/nosarthur/gita.svg)](https://gitter.im/nosarthur/gita?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) To run tests locally, simply `pytest` in the source code folder. Note that context should be set as `none`. More implementation details are in [design.md](https://github.com/nosarthur/gita/blob/master/doc/design.md). A step-by-step guide to reproduce this project is [here](https://nosarthur.github.io/side%20project/2019/05/27/gita-breakdown.html). You can also sponsor me on [GitHub](https://github.com/sponsors/nosarthur). Any amount is appreciated! ## Other multi-repo tools I haven't tried them but I heard good things about them. - [myrepos](https://myrepos.branchable.com/) - [repo](https://source.android.com/setup/develop/repo)