Working with Patches

I mentioned earlier that gitk would show you a listing of the changes in each commit.

These listings are called "patches" or "diffs".

The listings typically contain:

This is enough information that you could go through the files and make these changes using the listing.

Even if you work with files that are a little bit different from when the diff was created (and the line numbers are off), you can use the unchanged lines to find the correct location for each hunk.

A patch is not a simple report telling you what changed. It is a set of instructions that a computer (or person) can follow to make the same changes.

This is essential when collaborating with other people on the same project, and it can be occasionally useful for personal projects.

Suppose you have a change in your history that, for some reason, you don't like. Maybe it broke something. Maybe you know it's the wrong way to do what you wanted to do.

You can have Git undo that change by finding the commit id in the log and running:

$ git commit -a  #Commit any changes you've made first, just in case.
$ git revert -n commit-id

Git will make a patch from the old commit and apply it in reverse.

Like git checkout, git revert doesn't always work. If you have uncommitted changes to files that git revert would have to modify, it will fail so that you don't lose your data.

It can also fail because it cannot apply the patch. If, for example, some of the lines that it would have to remove have been changed, it cannot remove them, and the revert will fail.

When Git fails to apply a patch, it puts your tree in a state called a "conflict". Conflicts are meant to help you apply the changes yourself when Git fails, but they can be frustrating if you don't know how to deal with them. When your tree is in a state of conflict, Git will not let you commit anything, saying that some files "need merge".

If this happens to you, I suggest that you use "git reset --hard HEAD" to go back to your last committed state. If you've made changes to files, well, you can always make a copy of your modified version and put it back once the conflict is over. To avoid that situation, you should commit any changes you've made before running a git command that deals with patches.

Later, I'll explain how to resolve conflicts gracefully.

If you're curious, the -n in that command is the no-commit option. Normally, when you revert, Git will create a new commit with these changes.

I know you can handle changes to your working tree, but I thought you might be upset if you made a commit by accident when trying this out. Of course, when you're done with this tutorial, unwanted commits won't be a problem for you.

The opposite of a revert is a cherry-pick. The cherry-pick command makes a patch from a commit and applies that patch in the usual way. You might not see much use for that at the moment, but it'll make more sense when you've learned about alternate histories.

Patches are well-established in Unix, and you don't need Git to use them.

To generate a patch without Git, use the diff command:

$ diff -u file1 file2

This will generate a patch to update file1 to file2.

To capture the output of diff (or any command) to a file, use the > symbol:

$ diff -u file1 file2 > changes.patch

Now, you could send changes.patch to someone else who has a similar (but not necessarily the same) version of file1.

That person could apply your patch by running:

$ patch -p0 < changes.patch

The -p0 switch tells patch to use the filenames as they are written in changes.patch.

When Git generates patches, it uses paths like a/filename for the old version and b/filename for the new version. To apply these patches, use the -p1 switch. That tells patch to ignore the first directory in the path.

Patches are very common in open-source projects. Although Git provides other ways to collaborate, many Git-using projects, including Git itself, will ask contributors to send their contributions to the project as patch files.

To have Git create a patch containing your uncommitted changes, run:

$ git diff HEAD

Or to create a patch from a commit:

$ git show commit-id

Patches generated by git show also contain the commit message, date, and author. The git am command can create a new commit with the information from these patches.

Vincent Povirk