Creating a patch in GIT is a great way to share changes that you are not yet ready to push to a public branch of a project.
To better understand how we will create a patch, let’s first discuss a little about how GIT stores changes.
If you are new to GIT, install git and get a jumpstart from this GIT introduction article.
The first time a file is committed to a project in GIT, a copy is stored. For all commits after that, GIT essentially stores instructions telling it how to transform the previous version of the project to the newly committed version.
In GIT, these instructions are called “diffs” . Whenever you checkout a branch, GIT will basically start at the original state of the project, and apply all of these diffs in order, to to get to the desired state.
Knowing now how GIT stores commits, it is easy to see that a patch file will simply be a concatenation of the diffs for each of the commits that the patch will span.
For our example, let’s assume the following situation: We have a simple project with 2 branches: master and experimental.
$ git log --oneline --all * b36f227 (experimental) third commit -- added file3 * f39ebe8 second commit -- added file2 * 04a26e5 (HEAD, master) first commit -- committed file1
Master is currently at the first commit, while experimental is 2 commits ahead of it. In each commit, I added a file named file1, file2, and file3 respectively. Here is the current state of each branch:
On master, we only have file1:
$ git status On branch master nothing to commit, working directory clean $ ls file1
While on experimental, we have all 3 files:
$ git status On branch experimental nothing to commit, working directory clean $ ls file1 file2 file3
In this tutorial, we’ll explain how to create a patch of the changes on the experimental branch and apply them to the master.
Creating the GIT Patch
We will use the git diff command to create the diff output, then redirect it into a file. The form of the diff command we will use is as follows:
git diff from-commit to-commit > output-file
where:
- from-commit – the point at which we want the patch to start. (In our case, the point at which experimental diverges from master)
- to-commit – the patch will span the changes up to and including this point. (In our case, the most recent commit of experimental)
- output-file – the patch will be written here
Note: if either from-commit or to-commit are omitted, they will be assumed to be HEAD
We specify the two commits by their unique hash. Generally, you only have to specify enough of the commit hash to ensure its uniqueness (4 characters will probably do it).
$ git diff 04a2 b36f > patch.diff $ ls patch.diff file1 file2 file3
As you see from the above output, the patch file has been created.
In this special case, where we want to create a patch of the entire branch, we can let GIT do some of the work for us. We can let GIT determine the point at which our experimental branch diverged from the master branch using the git merge-base command:
git diff $(git merge-base <public branch> <experimental branch>) > <output file>
git merge-base will determine the most recent common commit between 2 branches. Notice also how this time we have omitted . It will be defaulted to HEAD, and since the experimental branch checked out, HEAD will be the most recent commit of the experimental branch.
$ git diff $(git merge-base master experimental) > anotherPatch.diff $ ls anotherPatch.diff patch.diff file1 file2 file3
Again, the patch file has been created. These patch files are identical.
Applying the GIT Patch
Once the patch file has been made, applying it is easy. Make sure that the branch you have checked out is the one that you want to apply the patch to (master in our case). Then you can apply the patch using the git apply command: git apply
$ git status On branch master ... (rest of output omitted) ... $ ls anotherPatch.diff patch.diff file1 $ git apply patch.diff $ ls anotherPatch.diff patch.diff file1 file2 file3
The changes from the experimental branch have now been replicated on master.
Warning: Although applying a patch in this way will exactly replicate content, no commit history will be replicated. This means that even if the patch you create spans several commits, it will appear as a single set of changes when applied. You will lose both the knowledge of how the commits were broken up and also the messages for each commit. Let’s compare the commit history for both branches:
On experimental, we had 3 commits, each with a meaningful commit message.
$ git branch * experimental master $ git log --oneline b36f227 third commit -- added file3 f39ebe8 second commit -- added file2 04a26e5 first commit -- committed file1
However, our patch simply applied the actual changes to the master branch.
$ git branch experimental * master $ git log --oneline 04a26e5 first commit -- committed file1
Applying the patch did not commit the changes, nor did it bring any of the commit history associated with these changes with it. Be cautious of this when using patches in GIT.
Comments on this entry are closed.
What about using git-format-patch ?
Hi,
How is apply patch different from merging the experimental branch to master? The merge action too will update the master with the same changes as that of experimental. Better yet the change history will be preserved.
Also , if possible please share under what situation you need to use patch?
Thanks
@Madhad,
applying a patch is no different from merging as you point out, however consider you want to send the changes to someone elsewhere. It is really usefull and pratical to send him/her a patch.
“…GIT essentially stores instructions telling it how to transform the previous version of the project to the newly committed version. … Whenever you checkout a branch, GIT will basically start at the original state of the project, and apply all of these diffs in order, to to get to the desired state.”
This is completely false; git stores *full* sets of files. Compression is used, and for remote operations (such as fetch and push) it’s true that “deltas” are used to send the objects more efficiently across a network—and it is true that git can pack up objects into an object pack to save space—but under the hood and under all the compression algorithms, you have *full* sets of files stored, *not* change sets.