This is a GNU Makefile aimed to work with Git repositories consisting of multiple branches.
It is pretty often, in my experience, to have a Git repository where every branch is dedicated
to things unrelated to each other. Typically, this repo has nothing in the master, and most
files are placed in some branch. Examples are:
- the repo with Dockerfiles for CI, where each branch is dedicated to a particular OS/distro. I.e., CentOS, Ubuntu, Fedora, etc.;
- the repo with RPM/Deb spec files -- one per (third-party) package;
- the repo with configuration files for various hosts/hardware -- i.e., one per branch;
- etc.
As soon as the count of branches increases, it's going to be a pain to rule them all -- e.g., to execute the same command in all branches. To address that issue, this Makefile has appeared.
Git has a neat feature called a working tree. It allows having multiple branches checked out
in the file system simultaneously. I like to name my branches as paths in a filesystem. E.g.,
release/1.2.3, bug/JIRA-123-blah-blah-regression, feature/cool-stuff. Same for the
mentioned types of repositories: config/host/ci-agent, centos/7, ubuntu/18.04, etc.
I usually clone my repositories with the following command:
~ $ git clone github:repo repo/masterSo, the project's root resides in the repo/master directory after cloning, and to work with
a branch, I do the following:
repo/master$ git worktree add --checkout ../feature/cool-stuff feature/cool-stuffNo, I don't use these commands exactly ;-) I have pretty convenient Git aliases configured
;-) As the result, I'll have this in my working repo/ directory:
repo
├── feature
│ └── cool-stuff
└── masterFor the configuration-storage-like repositories with a lot of branches, the master has this
Makefile only (and the README).
There is nothing to "build" in terms of Make. So, the default target prints the help screen:
Examples:
-
Add a new branch:
demo/master$ make add-branch name=hardware/laptop/system76-oryp3 git worktree add --force --checkout -b hardware/laptop/system76-oryp3 \ ../hardware/laptop/system76-oryp3 $(git rev-list --max-parents=0 HEAD) Preparing worktree (new branch 'hardware/laptop/system76-oryp3') HEAD is now at b07071c Demo
In my experience, all those branches do not need a common history, so I use the very first commit in the repo as a branch point. That is why better not to have many files in the repo at the very first commit ;-)
-
Check the result:
demo/master$ make show-branches-as-tree . ├── config │ └── host │ └── my.vpn ├── feature │ └── demo ├── hardware │ └── laptop │ └── system76-oryp3 └── master
-
The
for-each-working-treecan be used to execute arbitrary commands within the working trees:demo/master$ make for-each-working-tree exec='git status' ---[ config/host/my.vpn ] --- On branch config/host/my.vpn nothing to commit, working tree clean ---[ feature/demo ] --- On branch feature/demo nothing to commit, working tree clean ---[ hardware/laptop/system76-oryp3 ] --- On branch hardware/laptop/system76-oryp3 nothing to commit, working tree clean
One can use
match=<regex>parameter to perform the command only for sub-set of branches (working trees). -
Finally, as one may guess, the
update-alltarget would add work trees for all branches currently missed in the filesystem.
