Attaching patches to Pull Requests

This might sound strange, but sometimes I prefer patches to pull requests. The main scenario is when I’m reviewing someone else’s code and I want to propose an alternative implementation.

I could just create a new branch and pull request with my change, but then the conversation is split between two PRs, and there’s a new branch that you have to clean up.

When the change is small enough, or I’m not sure if it will be accepted, I’d rather send a patch. So far I’ve been doing git diff, uploading the result to gist, and posting the link as a comment in the PR. This has a few shortcomings:

  • No binary support.
  • If the original author wants to use it, authorship is usually lost, unless they use the --author option for git commit, and even then there’s room for typos.

I know there’s a better way, as Git was originally designed to share patches, not pull requests. I think I’ve been avoiding it because it’s not as common and the original author might not know what to do with the patch. So I’m writing this as a quick tutorial.

Creating a patch

Before creating a patch, you have to commit your changes. git format-patch will create a patch file for each commit, so your history can be preserved. Once you have a commit, your branch is ahead of origin, so we can use that to tell format-patch which commits to pick

branch=git rev-parse --abbrev-ref HEAD
origin="origin/$branch"
git format-patch $origin

This will leave one or more .patch files in your project directory:

$ ls *.patch
0001-Store-relative-paths-for-reader-topics.patch

Upload those to Gist and leave a comment with the link on the PR:

$ gist -co *.patch

Applying a patch

For a single patch, you can copy the Raw link in the Gist and download it

$ curl -sLO https://gist.github.com/koke/1b30d861e6bb9d366f69bc186d0e9525/raw/8cc27f3e589a7823b2e9f1746aa921b92da14187/0001-Store-relative-paths-for-reader-topics.patch

If there are multiple files, make sure you use the Download Zip link (or download all the files one by one):

$ curl -sLo patches.zip https://gist.github.com/koke/ab100907c17c4ef6a977350494679091/archive/3fb0136a21a6bc499bff2511750c62ae6dc41630.zip
$ unzip -j patches.zip 
Archive:  patches.zip
3fb0136a21a6bc499bff2511750c62ae6dc41630
  inflating: 0001-Store-relative-paths-for-reader-topics.patch  
  inflating: 0002-Whitespace-changes.patch  

Once you have the patch file(s) in your project directory, just run git am -s *.patch:

$ git am -s *.patch
Applying: Store relative paths for reader topics
Applying: Whitespace changes

Review the changes, and if you’re happy with them, git push them. Otherwise, you can reset your branch to point at the pushed changes:

branch=git rev-parse --abbrev-ref HEAD
origin="origin/$branch"
git reset --hard $origin

Finally, run git clean -df, or manually remove the downloaded files.

4 thoughts on “Attaching patches to Pull Requests

  1. Just out of curiosity, why don’t you just commit to the branch in the PR? You still produce a patch that the original author can see, and that author can still choose to accept or reject your changes. It seems like the stuff written here is just a verbose and painful way of adding to the PR

    1. I think the main difference is that there is more cleanup involved in the PR. If the author rejects the changes you still have to close the PR, remove the remote branch, and then remove the local branch. If you want to discard a patch, you just have to ignore it.

      1. Interesting. I might be misreading this here but it still seems easier to commit to the PR directly. Are you mainly working in repositories where you aren’t a member/user?

        Also, are you familiar with git rebase -i and the ease with which you can ignore commits in a branch?

        To contribute: git commit -a
        To reject: git rebase -i (delete the line(s) with the rejected commits)
        To cleanup: git branch -d branch-name

        Anyway, not trying to argumentative here – I just thought it seemed strange how much effort it looks like you put into creating and passing around these patches and wondered if you were familiar with what to me feels like a much simpler route. Thanks for the post!

        1. Yes, I’m a member on the repo, but we usually have only one author per branch. It would feel a bit rude (with our current flow) to push an alternate implementation to someone else’s branch, and say “revert if you don’t like it”.

          If it’s something obvious like a typo I’d push a commit directly. If it’s a full alternate implementation I’ll do a new PR. This is more for those “How about this instead?” cases.

          I know about git rebase -i, but that means rewriting the history and I don’t think you should do it on branches that are already pushed.

Leave a Reply to Dennis SnellCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.