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 forgit 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.
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
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.
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!
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.