React Native

How to become a master of git amends on any past commit

As developers, we've all stumbled upon these disconcerting situations while using Git:

  • "Oops, I forgot to update my test along with my feature I implemented three commits ago! I should have this fix in the same commit!"
  • "Oh no, I missed an occurrence of a rename three commits ago! This should go in this commit"

Many people will just commit ++code>git commit -m "fix tests"++/code> (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!

How will our git alias work?

Here's how you can use our command:

  1. Add changes to commit (use ++code>git add -p++/code> or the '+' option on VSCode).
  2. Run the ++code>git autofixup++/code> command.
  3. Select the past commit you want to incorporate the changes into.
  4. Et voila! 🎉

If you are not interested by the implementation, you can jump right off at the end of the article!

Let’s implement git autofixup!

Prerequisite: fzf, a convenient CLI tool to select a value in a list

The only prerequisite is ++code>fzf++/code>.

++code>fzf++/code> 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 ++code>stdin++/code> (usually it means you’ll be using ++code>|++/code> in a shell), and returns the result through ++code>stdout++/code> (means ++code>|++/code> again as an output).

The implementation steps

Here are the steps:

  • We'll be selecting a commit thanks to ++code>fzf++/code>, which we'll store in ++code>$COMMIT_HASH++/code>. This is where our fixup will be sent. Let’s see how we can get our commit hash in ++code>$COMMIT_HASH++/code>.

    ++pre>++code class="language-bash hljs">
    # 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}')
    ++/code>++/pre>
  • Next, we'll use the ++code>git commit --fixup $COMMIT_HASH++/code> command. Git will create a commit named automatically after the other targeted commit.
  • Once we have our fixup commit ready, we’ll have to run ++code>git rebase -i --autosquash++/code>. This will fire off an interactive rebase with a fixup already set up for the right commit.
  • To run this, we'll initiate the interactive rebase on the commit right before the fixup using the ++code>^++/code> suffix on our commit hash.

++pre>++code class="language-bash hljs">git rebase --autosquash -i $COMMIT_HASH^++/code>++/pre>

  • By default, the command will still launch your editor (Vim by default). You’ll want to quit it without changing anything, so it is pointless. To prevent this, we add the environment variable ++code>GIT_SEQUENCE_EDITOR++/code> set to ++code>: ++/code>to tell Git that it should not open an editor:

++pre>++code class="language-bash hljs">GIT_SEQUENCE_EDITOR=: git rebase --autosquash -i $COMMIT_HASH^++/code>++/pre>

  • And finally, for added comfort, we add ++code>autostash++/code> to avoid having to stash our other changes before firing off the rebase:

++pre>++code class="language-bash hljs">GIT_SEQUENCE_EDITOR=: git rebase --autostash --autosquash -i $COMMIT_HASH^++/code>++/pre>

The final ready-to-use implementation of git autofixup

Add the following alias to your ++code>~/.gitconfig++/code> and that’s it!

++pre>++code>[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"++/pre>++/code>

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!

You can do more!

On a final note, you can implement many interactive commands using ++code>fzf++/code>. 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 ++code>git show $COMMIT_HASH++/code>). Be creative 🙂

Développeur mobile ?

Rejoins nos équipes