A flexible and simple command to deal with sets of git
repositories.
As opposed to working on many git
repositories one after another, reposet
runs common operations
on given sets of repositories.
Reposet
provides convenience commands for the most common git
-related tasks, like push
and
pull
.
Reposet
also allows to run any command with the repositories' directories as the working
directories.
Reposet
can handle several user-defined sets of repositories. It also supports a default set.
Reposet
is written in bash
and does not require additional dependencies besides git
and some
default Unix
tools.
Reposet
supports following commands:
pull
push
sync
(combinedpull
/push
)status
list
list-sets
apply
(any givenbash
command or series of commands)
After you set up your first reposet, call reposet <command> [<reposet>...]
, to trigger a command.
See below for further information.
To get help with the commands, you can call reposet <command> --help
.
The project has following structure:
.
├── README.md You are now here.
├── res Additional resources.
│ └── example.reposet Example reposet definition for reference.
├── src Contains the sources.
│ └── reposet The reposet command.
├── setup.sh Copies the reposet command into your environment.
├── uninstall.sh Removes the reposet command from your environment.
└── util Contains utility scripts.
reposet
needs no prerequisites except bash
and git
. It should run on all Unix
-like systems.
To install reposet
, clone the git
repository anywhere onto your system:
git clone [email protected]:langenhagen/reposet.git
Then, execute the script setup.sh
:
bash setup.sh
This copies the scripts into the directory /usr/local/bin
.
To verify, run:
reposet --version
To uninstall, call:
bash uninstall.sh
This deletes the scripts from the directory /usr/local/bin
.
You may also delete the git
repository.
Calling the reposet
command has the general form: reposet <subcommand> [<reposet>...]
.
For example, calling reposet pull
, pulls changes from the default remote branches to the default
local branches on all repositories in the default set of repositories.
Calling reposet pull my
pulls from the remote branches on the repos listed in a file
$HOME/.config/.reposets/my.reposet
(see below in section Reposet Files).
Calling reposet pull my work
pulls from repos listed in the files my.reposet
and
work.reposet
.
A reposet is a bash
file with the extension *.reposet
that defines an array whose elements
describe an existing git
repository.
A reposet
subcommand may source a *.reposet
file in order to load repository information.
A *.reposet
file must be located in the directory $HOME/.config/.reposets
.
The name of a reposet is equal of the name of the file, without the suffix .reposet
.
For instance, a reposet called "my" would be defined in the file
$HOME/.config/.reposets/my.reposet
.
The special default reposet .reposet
does not have a name and may be used when calling a reposet
subcommand with no reposets specified.
The array inside the *.reposet
-files that defines which repositories belong to the reposet has to
have the name repos
.
One element in the array repos
specifies 6 important attributes of one git repository, each
attribute separated by a colon :
.
The form of one element that describes a git repository in the array repos
is:
"<local-path>:<local-default-branch>:[<remote-pull-repo>]:[<remote-pull-branch>]:[<remote-push-repo>]:[<remote-push-branch>]"
Specification of remotes and remote branches is optional. If they are missing, push and pull
operations are disabled for the respective repository. Even in this case, the colon-delimeters :
are required.
As an example, the following array would have valid form:
repos=(
"${HOME}/my projects/project1:master:origin:master:origin:master"
"${HOME}/my projects/project1:dev::origin:staging:origin:staging"
"${HOME}/my projects/my-gerrit-project:master:origin:master:origin:refs/for/master"
"${HOME}/my projects/pull-only-project:master:origin:master::"
"${HOME}/my projects/local-only-project:master::::"
"${HOME}/dotfiles:master:origin:master:origin:master"
)
For a complete example, review the file res/example.reposet
.
If a reposet contains the same repo specification several times, a call to reposet
will execute
the specified action on the repository repeatedly. The same holds when two or more reposets with
overlapping repository specifications are given to a reposet
command. Strictly speaking, reposets
rather define lists of repos than sets.
An easy way to create a reposet, is to copy the file res/example.reposet
into the directory
$HOME/.config/.reposets
and modify the copy:
mkdir -p ~/.config/.reposets
cp res/example.reposet ~/.config/.reposet/NAME.reposet # adjust NAME
vim ~/.config/.reposets/NAME.reposet # adjust NAME
Preferrably, reposets have simple and short names and start with a letter or a number.
Well suited names are for example "all", "my" or "work".
Naming the new reposet file .reposet
creates the default reposet. This reposet will be used when
calls to the command reposet
do not specify which reposets are to be used.
Since a *.reposet
file is simply a bash
file that is sourced into the program, it can do all
kinds of things. For example, a reposet file all.reposet
may aggregate the repos from other
reposets dynamically. Get creative!
Calling the reposet
command has the general form: reposet <subcommand> [<reposet>...]
.
The available commands are:
reposet apply
- apply a given bash command on all git repositoriesreposet list-sets
- list all reposetsreposet list
- list all git repositories in the given reposetsreposet pull
- callgit pull --rebase
on the reposreposet push
- callgit push
on the reposreposet status
- callgit status
on the reposreposet sync
- callgit pull --rebase
and thengit push
on each repo
Calling reposet <subcommand> --help
provides a usage description for each subcommand.
reposet
commands take an arbitrary number of reposets (see above in section Reposet Files) as
arguments.
If no reposet is given as an argument, the default reposet is used.
If several reposets are given, their items are chained.
The default reposet can not be chained with other reposets.
To circumvent this limitation, create a named reposet that you want to use as default and source
its *.reposet
file in the default reposet file .reposet
.
For example, the file .reposet
could source a named reposet "default":
source ~/.config/.reposets/default.reposet
Some subcommands have synonyms:
reposet down
is synonym forreposet pull
reposet ls
is synonym forreposet list
reposet sets
is synonym forreposet list-sets
reposet up
is synonym forreposet push
reposet
provides a command that is convenient and simple without impairing flexibility.
It comes with minimal dependencies and requires little learning.
Reposet
is agnostic to the location the git
repositories it acts on.
There are plenty tools that do a similar job, albeit with different slightly approaches.
gita
(https://github.com/nosarthur/gita) can delegate git
commands/aliases to one set of git
repos and show the git
status.
gita
can only handle one set of repositories.
gita
is written in Python
.
reposet
is more flexible due to its capability to handle several sets of repositories.
Also, reposet
is not limited to the execution of git
commands on the repositories.
myrepos
(https://myrepos.branchable.com/) claims to manage your version control repositories.
It also claims support all kinds of version control systems, including git
and lesser known ones
like Darks
.
myrepo
is highly configurable and can also automate certain tasks.
reposet
is rather lightweight compared to myrepos
.
repo
(https://source.android.com/setup/develop/repo) unifies git
repositories and is meant to
aid the Android
development workflow.
repo
needs the git
repositories to lie in the same directory tree.
repo
is written in Python
.
reposet
is more lightweight than repo
and more flexible, due to reposet
being agnostic to the
paths where the repositories reside in.
vcstool
(https://github.com/dirk-thomas/vcstool) aims to make work with several repositories
easier.
It can handle repositories of the version control the systems git
, Mercurial
, Subversion
and
Bazaar
.
It is written in Python
.
reposet
is rather lightweight compared to myrepos
.
The sources lie in the subdirectory src/
.
Calling reposet <subcommand>
runs the script reposet
which triggers another appropriate script
that follows the naming convention reposet-*
.
E.g. reposet-pull
is triggered when calling reposet pull
.
Common functions and variables are stored in the file reposet.inc.sh
which is sourced by most
scripts.
After sourcing the reposet.inc.sh
and loading the reposets via the helper functions, the
repository-specifications are stored in the array _repos
.
Likely, you want to iterate over this array.
There are common variables that store the current repository's traits. Some functions assume these
variables to be set. Those variables can be set by calling the function set_common_repo_variables <repo-definition>
and the function n_current_repo++
.
A typical idiomatic procedure to load all repo definitions and iterate over them looks like this:
# load common sources
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=reposet.inc.sh
source "${script_dir}/reposet.inc.sh"
# load the repository-definitions into the array _repos
load_reposets_or_die "$<REPOSET-NAMES>"
# [...]
for repo in "${_repos[@]}"; do
set_common_repo_variables "$repo"
n_current_repo++
cd_to_repo_or_die 1
print_current_repo_and_progress
# [...]
done
reposet
has colored output enabled by default.
To change or disable this, overwrite the variables defined in the common function
define_color_codes
.
There is a script that triggers linting with the project's preferences in util/run-shellcheck.sh
.
Call it from the root of the project like:
bash util/run-shellcheck.sh
At the moment, I am content with the features that reposet
provides.
I intend to add more command line arguments for the commands when the need arises.
I already thought of some additions and improvements:
Create a new subcommand reposet-add [<reposet>] [<path>...] [...]
to add a given git repository to
a given reposet via the command line.
Create a command reposet init [<path>...]
that finds git-repositories under the pwd or under the
given paths and attempts to create a reposet on the given checked out branches and tracked branches.
Implement command line parameters to optionally add or override paths to the *.reposet
files/paths
in order to provide flexibility and enable sharing of *.reposet
files.
Add bash
-completion, fish
-completion, and completion for other shells.
Disable colored output via command line argument.
Work on your stuff locally, branch, commit and modify to your heart's content. If there is anything you can extend, fix or improve, please do so! Happy coding!
- E.g., `! [remote rejected] master -> refs/for/master (no new changes)` should omit spilling a
warning. However, that would need to catch `stderr`. Also, `stderr` should ideally still be
printed to the console as `stderr`, in line with `stdout`, so, `... 2>&1 | tee 1>&2` may not
be ideal.