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
upstreamwhich 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
--histogramdiff algorithm can often make the output ofgit-diffeasier to read. -
Use
--word-diffor--color-wordsfor prose text -
Use
--ignore-all-spaceif you changed a bunch of indents -
Use
--find-copies-harder -M -Bto tweak rename detection. Both-Mand-Btake optional thresholds. -
If you need more context…
-
Use
-Uxto seexlines of context. -
Use
--inter-hunk-context=xto merge adjacent diff hunks. -
Use
--function-contextto 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
--stdoutand 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 -Bcan 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.