Submitting Patches to Upstream

In disconnected environments, the best way to submit your changes to an upstream project is to use a patch workflow. Patch files are a set of instructions for making changes to a document: i.e.,

go to line 107, strike the word "is," and replace it with the word "was."

These instructions are both human- and machine-readable, which makes them an ideal way to review patches. Patch files are a text-only format, so they can even be printed on paper.

Git can create patches with the format-patch command and apply them with the dubiously-named am command. "am" stands for "apply mailbox." (Patch files are frequently sent over email.)

More complete information on this topic can be found in Distributed Git - Contributing to a Project: see the "Public Project over Email" section. This document summarizes the most important points.

Preparing a Patch

Let's assume you have:

  • A feature branch named feature/mycoolthing, which has been fully tested and is ready to submit; and

  • A remote named upstream which tracks a git-teleport Receive Mirror.

Prior to submitting a patch, you must ensure that your work is rebased on a commit that the receiving side has. If you have forked the project, you might have your own integration branch and a great number of commits which are either not ready, or not intended, for submission. If necessary, run something like:

git fetch --all
git checkout feature/mycoolthing
git checkout -b submit/mycoolthing
git rebase upstream/develop   # or upstream/master

We use the git-flow develop branch in our example, but your target branch may be different. Very often, a project's CONTRIBUTING.md will tell you which branch to use. If it doesn't, and you're not sure, upstream/master is probably a safe bet. As an alternative to rebasing, you can also create your submission branch with git cherry-pick.

Now is a good time to review your work. Did you touch only the lines of code that you intended? Did you include anything in your commits that shouldn't be there?

git diff --histogram upstream/develop
  • The --histogram diff algorithm can often make the output of git-diff easier to read.

  • Use --word-diff or --color-words for prose text

  • Use --ignore-all-space if you changed a bunch of indents

  • Use --find-copies-harder -M -B to tweak rename detection. Both -M and -B take optional thresholds.

  • If you need more context…

    • Use -Ux to see x lines of context.

    • Use --inter-hunk-context=x to merge adjacent diff hunks.

    • Use --function-context to see an entire file or function as context lines.

There are many other diff options that you can use to get a better view into your changes. Experiment!

Check your branch for whitespace errors:

git diff --check upstream/develop   # should print nothing

Whitespace errors introduce needless churn and conflicts, and most projects won't appreciate them. Make your patches look professional by eliminating the trailing whitespace from your code.

Recompile and re-test if necessary.

Now you are ready to create a patch.

git format-patch -M --histogram --base=upstream/develop upstream/develop
  • This command produces one file per commit. You can roll them all into one file with --stdout and an output redirection.

  • If you are submitting changes to binary files, you must add --binary.

  • You can add many other diff options, like --histogram, to make the patch file itself smaller. --find-copies-harder -M -B can be particularly useful.

Always REVIEW YOUR OWN PATCHES before submitting them. Read the patch files and make sure they do what you want.

Transmitting a Patch

Patches are just files, and you can transmit them via any utf8-safe medium.

Via Email

When emailing patches, be advised that many email clients and servers will "helpfully" alter (read: destroy) the content of your messages. Exchange, for example, is particularly bad about altering line endings. Thunderbird will re-wrap lines in your message body. git send-email and git imap-send can both prepare email messages for you if you have SMTP or IMAP server access.

On Paper

Sometimes, you may want to print a patch file for purposes of review. You might also need to apply the patch by hand. When printing, use an editor which supports syntax highlighting for patch files. Print in color. Use a readable, monospaced font.

To reduce the size of the printed document, try generating a patch with options like:

git format-patch -U2 -M -D --ignore-all-space

to reduce context, make deletes irreversible, and ignore indent changes. This patch cannot be applied with Git, but that's okay.

Applying a Patch

To apply a patch file to your project, first create a branch off of your project's default integration branch.

git fetch --all
git checkout origin/develop
git checkout -b feature/mycoolthing

Then run

git am /path/to/patch/files/*.patch

This applies the patch(es) to the current branch. If the patch does not apply, try

git am --3way /path/to/patch/files/*.patch

to perform a three-way merge. This only works if the patch records a base-commit: line. If the submitter followed the instructions above, it should.

You can then review the branch just as you would any other topic branch. If the changes are acceptable, you can merge them.

If you are using git-teleport to mirror your repositories, consider sending out a fresh batch of bundles after you merge.

Monitoring the Status of your Patches

Patches create fresh commits when applied. This means that, although the commit messages and the changes will be identical, the resulting Git commits will have different hashes.

The git cherry command is useful for determining which of your patches have been merged into the upstream repository.

Alternatives to Patch Workflows

Git-teleport only works in one direction at a time. With that said, you can book a round-trip flight for your data by purchasing two separate one-way tickets.

If you have forks of a repository that you receive via git-teleport, you can also use git-teleport to send your fork back to the maintainers. Simply create a Mirror Set which includes your fork and send your bundles off.

Although this approach works, it has serious limitations:

  • git-teleport bundles are typically "point to point" links. If you submit your changes this way, only one destination can receive them. Patches, due to their self-contained nature, can be sent to many recipients at once.

  • git-teleport will send everything in your fork by default. This is not always desirable. Sending large repositories this way can use a lot of storage and transfer space.

  • Bundling can add sometimes more work for the maintainer.

    • Branches from bundles might need rebasing and other work before they are useful. A patch process, with a pre-submission review, can be cleaner.

    • For a large repository with many branches, the sender must communicate which branches are "merge requests" (and which aren't).

  • Bundles require a binary-safe transport mechanism.

With that said, both patches and bundles are workable solutions. With practice, and a commitment to authoring good commits, either route can lead to success.