TopGit – A Different Patch Queue Manager

Overview

TopGit manages one or more "patch queue"s (aka "patch set"s) using Git.

Whereas a utility such as quilt maintains each individual patch as a "diff" file on disk, TopGit (the tg command) maintains each individual patch as two branches in Git – the patch's "branch" and the patch's "base branch". The patch itself is simply the "diff" from the patch's "base branch" to the patch's "branch".

Whereas a utilty such as quilt maintains the list of active patch "diff" files and the correct order to apply them in a file (the series file), TopGit maintains this information in a .topdeps file that is part of each patch's "branch".

TopGit maintains any desired "documentation" (aka the patch header) in a .topmsg file that is also part of each patch's "branch" and prepends it when generating a patch "diff".

Simplistic Quilt vs. TopGit Example

Consider the following files:

frabjous.tar.gz    # tarball of "frabjous" sources
0001-brillig.diff  # first patch to apply
0002-gimble.diff   # second patch to apply

With Quilt they might be used like so:

$ tar -xzf frabjous.tar.gz
$ cd frabjous
$ quilt import ../0001-brillig.diff && quilt push
$ quilt import ../0002-gimble.diff && quilt push
$ quilt applied
patches/0001-brillig.diff
patches/0002-gimble.diff

With TopGit they might be used like so:

$ tar -xzf frabjous.tar.gz
$ cd frabjous
$ git -c init.defaultBranch=master init && git add -A
$ git -c user.name=- -c user.email=- commit -qm frabjous
$ git checkout -b patches
$ git -c user.name=- -c user.email=- am ../0001-brillig.diff
$ git -c user.name=- -c user.email=- am ../0002-gimble.diff
$ git checkout master
$ git config topgit.top-bases heads # not the default...yet!
$ tg -c user.name=- -c user.email=- import ..patches
$ git branch
  master
  patches
  t/brillig
* t/gimble
  {top-bases}/t/brillig
  {top-bases}/t/gimble
$ tg summary
  t/brillig  [PATCH] brillig
> t/gimble   [PATCH] gimble
$ tg summary --rdeps --heads
t/gimble
  t/brillig
    master

Whereas with Quilt, the "0001-brillig.diff" patch is maintained as the original patch file (in patches/0001-brillig.diff), with TopGit, the "0001-brillig.diff" patch becomes two Git branches, {top-bases}/t/brillig which represents the sources before the "0001-brillig.diff" patch has been applied and t/brillig which represents the sources after the "0001-brillig.diff" patch has been applied.

The original "0001-brillig.diff" patch can be re-generated with this TopGit command:

$ tg patch t/brillig

And is roughly equivalent to the diff from the {top-bases}/t/brillig branch to the t/brillig branch. (It's the diff excluding the two TopGit metadata files with the patch header prepended to the result.)

To work on the "brillig" patch, one simply does a checkout of the t/brillig branch and makes and commits changes as normal using regular Git commands. After the changes have been made, the "gimble" patch may need updating if any of the changes made to the "brillig" patch modified the same files affected by the "gimble" patch. That can be accomplished with this command:

$ tg update --all

Finally, when a new set of ".diff" patch files needs to be generated, these commands can be used:

$ git checkout t/gimble
$ tg export --quilt --strip --numbered patchdir
$ ls -1 patchdir
0001-brillig.diff
0002-gimble.diff
series

TopGit Advantages

Patch Dependency Topology

Whereas the series file in Quilt is (obviously) limited to a "linear" topology, TopGit's .topdeps file is not.

For example, consider that you have three patches:

  1. add "feature A"
  2. add "feature B"
  3. add "feature C" that requires both feature A and feature B

If "feature A" and "feature B" are completely independent features, then the order that the "feature A" and "feature B" patches are applied does not matter as long as they are both applied before the "feature C" patch.

TopGit can represent this by listing both the "feature A" and "feature B" patches in the .topdeps file for the "feature C" patch.

Patch Change History

Whereas with Quilt there is no history of changes (aka edits) to a patch (unless explicit backups are made along the way), all changes to a patch in TopGit are made via regular Git commands.

This means all the standard Git introspection commands (e.g. "log", "diff", "bisect", "blame", etc.) are available to view the history of an individual patch.

Patch Updating

With Quilt when the "upstream" sources are updated (or even an early patch in the series is updated) in such a way as to cause a patch to no longer apply cleanly, the patch must be fixed by hand.

With TopGit, the full Git merge machinery is used to merge the "upstream" changes (or changes to an earlier patch) into the patches in dependency topological order. This can often avoid patch conflicts.

In the case where a patch must be fixed by hand (yes, this still can happen in TopGit), the Git rerere (reuse recorded resolution) mechanism can be used to record the hand-crafted fix and later automatically reuse that fix when needed.

Patch Identification

When there are a very large number of patches in a patch set, it may be difficult to remember (especially if you're not the original author) which individual patch of a "patch set" contains a particular change.

After using the various Git introspection commands (e.g. "log", "diff", "bisect", "blame", etc.) to locate a commit containing a particular change, the TopGit tg contains command can be used to identify the individual patch of a "patch set" that logically contains that commit.

In other words, tg contains identifies which TopGit branch would produce a patch "diff" file that makes the change introduced by the given commit.

Collaboration

Because TopGit stores all information directly in Git, all of Git's normal push/pull/remote functionality can be used to collaborate on and/or share TopGit "patch set"s with others. The tg push command facilitates pushing all branches in one or more TopGit "patch set"s at once.

TopGit Features

Multiple Patch Sets

With Quilt it is possible to use different series files to combine patch "diff" files into different arrangements (e.g. for different machine architectures or development branches).

TopGit also allows multiple "patch set"s to exist at the same time where a single "patch" may be used by more than one "patch set".

Patch Set History

As an alternative to maintaining two or more different versions of the same patch (e.g. for different development branches) under different names such as, for example, "patch1-oldstable", "patch1-stable" and "patch1-unstable", it's possible to use a TopGit tag to record the state of a "patch set" (via the tg tag command) and then later go back to that state (via the tg shell command) to, for example, produce an updated patch set for an older software version and record that new state of the older software version "patch set" without disturbing the current version of the "patch set".

The advantage of using this mechanism is that the branch names for the individual patches always remain the same no matter how many different versions are being kept.

Import and Export

TopGit can import and export "patch set"s to other formats. This includes the one "diff" file per patch and one "commit" per patch formats.

This makes it easy to produce a set of "diff" patch files on demand for external (e.g. non-Git) use for a "patch set".

TopGit Downsides

Forget about using git rebase on a TopGit branch (either a patch's "branch" or its "base branch"). With the exception being commits that have not yet been merged into any other patch's "branch" or "base branch". (TopGit does, however, have its own tgstash that provides a kind of "undo" after unfortunate update accidents.)

The merge history for a "patch set" can become rather complex as a "patch set" is maintained over time (via the tg update command) since nothing is ever thrown away.

TopGit has a learning curve and those unfamiliar with Git will need to become comfortable with Git too.

Additional Information

Full documentation is available including a fully formatted version of the manual (created from README_DOCS.rst which incorporates a few examples) and the TopGit changelog.