Itās commonplace in the open source world to submit pull requests with clean, logical commits appropriate to the thing you are changing. The git history of major projects is an important tool to track down why a change was made via detailed commit messages, who originally wrote it, etc. Generally git history in an open project is much cleaner than a closed one - much less committing of ālolz change disā and āf***ing s**t why doesnāt this work!?!?!?ā, anyway.
When you get feedback on your pull request and have to make changes to changes (so meta), this can be either really easy or fairly complicated, depending on your experience with git:
-
One commit: Often youāre just submitting a single change, or commits are asked to be āsquashedā when review of a pull request is complete anyway. This means all changes are in a single commit and you can just amend your previous commit and push again.
-
Multiple commits: Other times you might be changing enough that multiple commits make sense. So unless the tweak you need to make happens to be on the very last commit, youāll need to edit a commit back further in your git history.
At this point, if you donāt know how to use git commit --amend
or git rebase
-i
, I need you to go check out the documentation. No point in
regurgitating it here. Go ahead! Iāll wait right here.
ā¦got it? The main thing you should have gotten from that is that you can
āEDITā a particular commit. Git will replay commits on your branch until you
reach that point, at which point you can then amend it and recommit it, git
rebase --continue
and be on your way.
I have to do that every time?
git rebase -i
isnāt bad. Doing it often enough you can do anything fairly
quickly. But the pattern of āI have a slight tweak I need to make in a PR, so
now I have to look up which commit itās in, go through rebase -i
and change
itā¦ā is so, so common.
Fortunately, git provides a neat shortcut for rebase
to amend previous commits called --autosquash
:
When the commit log message begins with "squash! ..."
(or "fixup! ..."), and there is a commit whose title begins with the
same ..., automatically modify the todo list of rebase -i so that the
commit marked for squashing comes right after the commit to be
modified, and change the action of the moved commit from pick to
squash (or fixup).
What these means is that I have a commit named āChanged stuffsā and apply
another commit called āfixup! Changed stuffsā, running this option will
automatically combine these two commits. Thereās also a git commit --fixup=<ref>
to automatically name the new commit properly based on the SHA youāve passed.
Butā¦Iām very lazy.
But even that solution requires you to look up the SHA of the change and pass it around. And so I present two tools Iāve utilized to make rebasing changes back into a PR (or anything) completely stupid quick and easy.
- git autofixup: A custom command to commit the staged change as a fixup
for the last commit that touched the changed line. This is crazy useful for
preparing fixups for
git rebase --autosquash
ORā¦ - git autosquash [<ref>]: A custom command compliment to
autofixup
. Initiates an autosquash rebase on the commits in your fork (commits since the common ancestor between HEAD and master). You can pass in a branch or SHA to find the fork point with something other than the master branch.
With these tools you can now do the following, shown in the gif at the beginning:
# (Make a change, add the file to your staged changes), then...
# Commits changes as a fixup for the proper previous commit
$ git autofixup
# (Optionally, keep doing the same thing with other changes...)
$ git autofixup
# Now autosquash all the things from your fork point!
$ git autosquash
Done! Quick. Easy. Awesome.
You can find these custom commands shared with love in my dotfiles