Git: Multirepo patch set
In the context of Alejandro Colomar stepping down as a man-pages maintainer :(, I learned from him that
it is possible with git
to create an email patch set that spans multiple target repositories.
Specifically, he pointed to a review thread by Jiri Olsa where this was done, and whose outline takes a similar shape to this (simplified):
- [PATCH proj v3 0/3] foobar: Add transmogrifier
- [PATCH proj v3 1/3] foobar: Prepare flux compensator
- [PATCH proj v3 2/3] foobar: Add transmogrifier feature
- [PATCH man v3 3/3] foobar.1: Document transmogrification
Other than in normal review threads, every mail subject is prefixed with an additional tag here, to indicate which project it belongs to.
It was not obvious to me how to best achieve it with the existing tools, so I attempted to reconstruct how he had done it, and ended up with the workflow that I will describe below. This is not the same workflow that Jiri Olsa has been using after all, but it’s close, and unusual enough that it’s maybe worth documenting here.
1. Pull both original repositories in the same local repository
We first make sure that all necessary commits are stored in the same local repository, by pulling in a second remote repository with git fetch
.
This is unusual, but it is possible. As a result, your local repository contains two unrelated commit histories, for instance one for the linux kernel and one for the man pages:
You can simplify your life here a bit by using git worktrees – with these, your can keep doing your Linux work in a separate directory to your man page work, but still keep all of the changes in the same repository.
In the example above, we have prepared two commits in the linux-feature branch, based on the linux-base branch from upstream, as well as one commit in the man-feature branch, based on man-base.
Example
Starting from two repositories /tmp/git/linux-origin
and /tmp/git/man-origin
, let’s fetch both repositories into the same local repository and create a worktree for each:
gnoack:/tmp/git$ git clone /tmp/git/linux-origin linux
Cloning into 'linux'...
done.
gnoack:/tmp/git$ cd linux
gnoack:/tmp/git/linux(main)$ git tag linux-base
gnoack:/tmp/git/linux(main)$ git remote add man-origin /tmp/git/man-origin
gnoack:/tmp/git/linux(main)$ git fetch man-origin
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 12 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (12/12), 948 bytes | 948.00 KiB/s, done.
From /tmp/git/man-origin
* [new branch] main -> man-origin/main
gnoack:/tmp/git/linux(main)$ git worktree add /tmp/git/man man-origin/main
Preparing worktree (detached HEAD efbfa45)
HEAD is now at efbfa45 4
We now have a worktree for the man pages at /tmp/git/man
and one for Linux at /tmp/git/linux
.
We now prepare the Linux and man page commits on top of these git repositories in the usual way, and make sure that we create branches for the pristine states called man-base
and linux-base
, and call our work branches man-feature
and linux-feature
.
2. Format a patch set that spans two otherwise unrelated branches
Create a giant patch set that spans two branches. Indicating the desired commit range by using dotted range notation twice.
This invocation is compatible with common flags like -v3
for the patch set version and --cover-letter
.
We use --subject-prefix
so that the mail subject is additionally qualified with an abbreviation for the main project.
git format-patch -v3 --cover-letter \
--subject-prefix="PATCH proj" \
linux-base..linux-feature \
man-base..man-feature
Remember from the gitrevisions(7) man page that a revision range in Git parlance is a set of commits, which are not necessarily ordered in a single linear chain. (Somewhat surprisingly, they are also not all connected here, as you would think from reading the man page.)
git format-patch
includes some additional features for controlling the ordering, but in practice it seems that commits are ordered by date if they are not connected.
3. Final hand-editing
Hand-edit the subject prefix in the man page commit’s email, so that the man page commit stands out among the others for the reviewers.
And sure enough, after git send-email
, the mail thread has the expected form (in mutt
):
[PATCH proj v3 0/3] foobar: Add transmogrifier
├─>[PATCH proj v3 1/3] foobar: Prepare flux compensator
├─>[PATCH proj v3 2/3] foobar: Add transmogrifier feature
└─>[PATCH man v3 3/3] foobar.1: Document transmogrification