Git ... oh my
In recent projects I’ve been using a lot of git and I love it. As a distributed source control tool it’s brilliant. This is particularly true when you need to gather and manage a wide variety of disparate patches and commits. On the projects I work on we get patches via a lot of paths:
- Diffs attached to tickets
- Diffs sent via email
- Git branches and cherry picks
With the former two paths we (mostly me) have been cutting and pasting patch
diffs into files or using wget. We then use the patch command to apply
the diffs to various branches and then commit the results. With this approach
we often lose track of the patches ownership and author. This is problematic
from two perspectives - we can’t allocate credit where it is due and when
something goes wrong with a commit it’s often hard to track down the original
author. We obviously don’t have this issue with Git repositories and merging
branches or cherry picking specific commits. With the latter it is easy to
track patch authors and ownership - even through multiple merges and rebasing.
So I like it when people fork the Github repo and provide a commit or
feature/ticket branch when supplying code. But for the others I’m trying to
bring our process a little closer to Git by using the git am and git apply
commands (also git-am and git-apply - though the git-command syntax is
being deprecated) to pull in diffs and patches. The git am command
processes mailboxes (mbox and Maildir), mail messages or RFC 2822 formatted
messages provided via standard input. The git apply command applies a
unified diff file to the current working tree. Let’s start with the easy to
use git apply command. Let’s take one of our use cases: downloading a patch
from our tracker and applying it. First, we wget our patch file: $ cd /tmp $ wget http://projects.redmine.com/project/tickets/2222/patch2222.diff
We
then change directory into our Git repo and check what branch we’re in. $ cd ~/src/puppet $ git branch * master
We can now feed our patch file into git
apply using something like cat and a pipe. $ cat /tmp/patch2222.diff | git apply
This will add the patch to the current branch and commit it. Another
useful trick is the –amend option. If your current commit is not yet
pushed you can amend it. Just make your required edits, git add the updated
files: $ git add _filename_
And then run: $ git commit --amend
Git will
populate your editor with the last commit message and you can update the
commit. We could also create a separate branch for our new commit. $ git checkout -b tickets/master/2222 $ git apply < /tmp/patch2222.diff
Here we’ve
directed the file straight into git apply rather than piped the output of the
cat command. We can then merge this into the appropriate branch when ready.
$ git merge tickets/master/2222
Don’t forget you can also use the git
rebase command to ensure your branch is rebased against the branch you’re
merging into, to squash multiple commits or to redo the commits - git rebase
(especially the –interactive switch) is the business. :) Using our second
command, git am, we can also pull patches straight from a mail client. For
example, on Thunderbird, I open up the message I want to import then (on OSX -
on PC it’s basically the same commands prefixed with Ctrl not Command):
- Command-U to show the full message including headers
- Command-A to select all of the message
- Command-C to copy the selection
I can then go to the command line and do: $ cat | git am
Followed by a
Command-V to paste the content, and then Control-D to end the cat and submit
my patch to be committed. The git am command is quite clever. The author of
the commit will be pulled from the From line of the message, the date and time
of the commit from the date and time the message was sent and the Subject and
Body are used as the title and body of the commit message. We can also add the
-s switch to the git am command. This adds a “Signed-off-by” line to the
commit message using your details (usually name and email address). $ cat | git am -s
It’s not perfect model yet - I probably should be using git am on a
Maildir or mbox file which contains our patches but in our small development
team I have the luxury of just selecting the patches and emails I want.