written by Eric J. Ma on 2018-06-17 | tags: git version control code snippets
I learned a new thing this weekend: we apparently can apply a patch onto a branch/fork using git apply [patchfile].
There's a few things to unpack here. First off, what's a patchfile?
The long story cut short is that a patchfile is nothing more than a plain text file that contains all information about diffs between one commit and another. If you've ever used the git diff command, you'll know that it will output a diff between the current state of a repository, and the last committed state. Let's take a look at an example.
Say we have a file, called my_file.txt. In a real world example, this would be parallel to, say, a .py module that you've written. After a bunch of commits, I have a directory structure that looks like this:
$ ls total 8 drwxr-xr-x 4 ericmjl staff 128B Jun 17 10:26 ./ drwx------@ 19 ericmjl staff 608B Jun 17 10:26 ../ drwxr-xr-x 12 ericmjl staff 384B Jun 17 10:27 .git/ -rw-r--r-- 1 ericmjl staff 68B Jun 17 10:26 my_file.txt
The contents of my_file.txt are as follows:
$ cat my_file.txt Hello! This is a text file. I have some text written inside here.
Now, let's say I edit the text file by adding a new line and removing one line.
$ cat my_file.txt Hello! This is a text file. I have some text written inside here. This is a new line!
If I looked at the "diff" between the current state of the file and the previous committed state of the file:
$ git diff my_file.txt diff --git a/my_file.txt b/my_file.txt index a594a37..d8602e1 100644 --- a/my_file.txt +++ b/my_file.txt @@ -1,4 +1,4 @@ Hello! This is a text file. -I have some text written inside here. +This is a new line!
While this may look intimidating at first, the key thing that one needs to look at is the + and -. The + signals that there is an addition of one line, and the - signals the removal of one line.
Turns out, I can export this as a file.
$ git diff my_file.txt > /tmp/patch1.txt $ cat /tmp/patch1.txt diff --git a/my_file.txt b/my_file.txt index a594a37..d8602e1 100644 --- a/my_file.txt +++ b/my_file.txt @@ -1,4 +1,4 @@ Hello! This is a text file. -I have some text written inside here. +This is a new line!
Now, let's simulate the scenario where I accidentally discarded those changes in the repository. A real-world analogue happened to me while contributing to CuPy: I had a really weird commit history, and couldn't remember how to rebase, so I exported the patch from my GitHub pull request (more on this later) and applied it following the same conceptual steps below.
$ git checkout -- my_file.txt
Now, the repository is in a "cleaned" state -- there are no changes made:
$ git status On branch master nothing to commit, working tree clean
Since I have saved the diff as a file, I can apply it onto my project:
$ git apply /tmp/patch1.txt $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: my_file.txt no changes added to commit (use "git add" and/or "git commit -a")
Looking at the diff again, I've recovered the changes that were lost!
$ git diff diff --git a/my_file.txt b/my_file.txt index a594a37..d8602e1 100644 --- a/my_file.txt +++ b/my_file.txt @@ -1,4 +1,4 @@ Hello! This is a text file. -I have some text written inside here. +This is a new line!
Don't forget to commit and push!
I mentioned earlier that I had exported the patch file from GitHub and then applied it on a re-forked repository. How does one do that? It's not as hard as you think.
Here's the commands below with comments.
# Download the patch from the pull request URL. # Replace curly-braced elements with the appropriate names. # Export it to /tmp/patch.txt. $ wget https://github.com/{repo_owner}/{repo}/pull/{pr_number}.patch -O /tmp/patch.txt # Now, apply the patch to your project $ git apply /tmp/patch.txt
@article{
ericmjl-2018-git-tip-apply-a-patch,
author = {Eric J. Ma},
title = {Git Tip: Apply a Patch},
year = {2018},
month = {06},
day = {17},
howpublished = {\url{https://ericmjl.github.io}},
journal = {Eric J. Ma's Blog},
url = {https://ericmjl.github.io/blog/2018/6/17/git-tip-apply-a-patch},
}
I send out a newsletter with tips and tools for data scientists. Come check it out at Substack.
# TODO(eric): learn how to learn anything. feb 2027.
If you would like to sponsor the coffee that goes into making my posts, please consider GitHub Sponsors!