git cherry-drop
A simple way to drop a commit
Today at work, a coworker asked me over Slack how to delete a commit:
- So I need to exclude a previous commit
- Do I
git cherry-pick
and then the commit string?- So
git cherry-drop abc123
- Something like that?
Telling him that git cherry-drop
doesn't exist, I walked him through how to drop the commit with git rebase -i
.
But it got me thinking... why not have a git cherry-drop
command? A single command is always nicer than going through the whole interactive process.
I use git inject a lot in my workflow, so I thought I'd copy a bit of logic from that to create the cherry-drop
alias.
As I got going, I realized there was enough code that it was probably cleaner to split it out into a function in ~/.githelpers
, and have the alias load that.
After reviewing the --onto
option which I rarely use (Pivotal had a very helpful guide),
here's what I ended up with:
Added to ~/.githelpers:
cherry_drop() {
set -e
REF_TO_DROP=`git show $1 --pretty=format:%H -q`
HEAD=`git show HEAD --pretty=format:%H -q`
shift
# Stash changes if they exist
if ! git diff-index --quiet HEAD --; then
git stash && DIRTY=1
fi
if [[ $REF_TO_DROP == $HEAD ]]; then
# Easy way to undo the last commit:
git reset --hard HEAD
else
# Rebase the commit out:
git rebase --keep-empty --onto $REF_TO_DROP~1 $REF_TO_DROP
fi
# Unstash changes if they were stashed:
[ -n "$DIRTY" ] && git stash pop
# Great success:
exit 0
}
The alias:
[alias]
..
cherry-drop = "!. ~/.githelpers && cherry_drop"
In action
Say you're on a branch called foo
, and your history looks like this:
$ git log --graph --abbrev-commit --pretty=format:'%h - %d %s' -n4
* 5b5fa7ecf0 - (HEAD -> foo) Great commit
* 205cf5cf76 - Accidentally commit 1,000 node_modules
* 01f02a3d6a - Another good commit
* 0f0c83af7e - Good commit
To remove 205cf5cf76
, it's a single command:
$ git cherry-drop 205cf5cf76
First, rewinding head to replay your work on top of it...
[detached HEAD 4e798f3011] Great commit
Date: Thu Sep 28 16:54:51 2017 -0600
$ git log --graph --abbrev-commit --pretty=format:'%h - %d %s' -n4
* 4e798f3011 - (HEAD -> foo) Great commit
* 01f02a3d6a - Another good commit
* 0f0c83af7e - Good commit
* 077f18f7b3 - Some old Change
Warning
cherry-pick
should work to slice out simple commits that don't touch anything else, but...
- If you want to remove a commit that other commits depend on, you'll probably want to avoid this alias and rebase it the usual way.
- If you want to remove multiple commits, it's probably also better to rebase them all out in one go instead of cherry-drop
ing them one-by-one.
Closing
I've added that alias to my dotfiles.
I'll push any future cleanups/fixes there.
Enjoy!
I'm a Ruby/Rails developer currently working for UserTesting, living in Southern Utah. More about the author →