As developers, we've all stumbled upon these disconcerting situations while using Git:
Many people will just commit git commit -m "fix tests"
(don’t worry we all do it).
But there’s a cleaner way to do so using fixup commits and a rebase interactive. And even better: we can automate it all!
You can jump at the end of the article if you only want the final command, but I’d recommend walking through it to understand what we’re using. It’s better to abstract once you know what’s going it!
Here's how you can use our command:
git add -p
or the '+' option on VSCode).git autofixup
command.If you are not interested by the implementation, you can jump right off at the end of the article!
The only prerequisite is fzf
.
fzf
is an incredible tool that has one single purpose: it takes a list as input and returns an item you selected as output (or multiple items). We'll be using it to interactively select a commit.
It takes the input through stdin
(usually it means you’ll be using |
in a shell), and returns the result through stdout
(means |
again as an output).
Here are the steps:
fzf
, which we'll store in $COMMIT_HASH
. This is where our fixup will be sent. Let’s see how we can get our commit hash in $COMMIT_HASH
.
# We want git to print its log with one line per commit.
# Let’s use git log --pretty=oneline
git log --pretty=oneline
# output
#
# 2a3c7c my commit B
# 2f3cab my commit A
#
# after piping it through fzf, we keep only one line
git log --pretty=oneline | fzf
# output
#
# 2f3cab my commit A
#
# now we want to get only the hash "2f3cab". We can use `awk '{print $1}'` to do so!
git log --pretty=oneline | fzf | awk '{print $1}'
# output
#
# 2f3cab
#
# Now, we put this all in a variable!
COMMIT_HASH=$(git log --pretty=oneline | fzf | awk '{print $1}')
git commit --fixup $COMMIT_HASH
command. Git will create a commit named automatically after the other targeted commit.git rebase -i --autosquash
. This will fire off an interactive rebase with a fixup already set up for the right commit.^
suffix on our commit hash.git rebase --autosquash -i $COMMIT_HASH^
GIT_SEQUENCE_EDITOR
set to :
to tell Git that it should not open an editor:GIT_SEQUENCE_EDITOR=: git rebase --autosquash -i $COMMIT_HASH^
autostash
to avoid having to stash our other changes before firing off the rebase:GIT_SEQUENCE_EDITOR=: git rebase --autostash --autosquash -i $COMMIT_HASH^
Add the following alias to your ~/.gitconfig
and that’s it!
[alias]
# Amend into a past commit using fzf
# Stage your changes `git add -p`, then run `git autofixup` and choose the target commit
autofixup = "!f() { \
COMMIT_HASH=$(git log --pretty=oneline | fzf | awk '{print $1}'); \
git commit --fixup $COMMIT_HASH; \
GIT_SEQUENCE_EDITOR=: git rebase --autostash --autosquash -i $COMMIT_HASH^; \
}; f"
This alias is a very valuable tool to help you having clean commits. You can modify past commits so easily that it becomes a natural thing. It just takes a second. It's a tool that many of us adopted at BAM!
I hope it will be useful to you too!
On a final note, you can implement many interactive commands using fzf
. Selecting a commit to do something is a classic, you can think of many other commands (like selecting a commit to show its diff, using git show $COMMIT_HASH
). Be creative 🙂