Confirm your changes at the end of a git rebase

Jonas Meinerz

Problem

Long-lived feature branches are not ideal, but they are sometimes unavoidable. When working with big teams and large codebases, it is reasonable for feature development, dependency upgrades, and refactorings to happen simultaneously.

I recently found myself trying to rebase a feature on top of a large piece of refactoring. I did not feel confident after the rebase was applied, and decided to compare the current version of my branch against how it was before the rebasing began. The diff I got was not what I wanted, so I reverted the rebase and tried again.

Saving the SHA of my not-yet-rebased branch to a variable so I could run git diff $before_rebase_sha after rebasing quickly became a pattern for me while developing that feature. The issue was I didn’t always remember to export before_rebase_sha=$(git rev-parse HEAD) before I started rebasing, which made so I had to spend quite a lot of time looking staring at the output of git reflog.

What I was missing was something that showed me the changes caused by my rebasing and reverted them when I wasn’t happy. So I built that.

Solution

git log --oneline on my current repository returns the following:

ec7bfbb (HEAD -> master) Third
9d2b628 Second
2fc8388 First

But I’m not quite happy with my second commit, which looks like this:

commit 9d2b6284957c0506cc975c4a473b2704db0a8581
Author: Jonas Meinerz <jonas@thoughtbot.com>
Date:   Fri Apr 24 11:11:41 2020 +0100

    Second

diff --git a/test.txt b/test.txt
index 9daeafb..e4e595b 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-test
+testy
diff --git a/test2.txt b/test2.txt
new file mode 100644
index 0000000..6f2a9ef
--- /dev/null
+++ b/test2.txt
@@ -0,0 +1 @@
+secondy

I want to get rid of those ‘y’ at the end of the first lines of both my files. In order to do that, I will run

git rebase -i HEAD~2

And I will choose to edit that specific commit:

Editing commit during interactive rebase

But my change resulted in conflicts:

Conflicts during interactive rebase

After solving those conflicts and running git rebase --continue comes the part that used to bother me. Now, thanks to a small amount of automation, I get a diff displayed to me in a friendly way. I can also reset my branch to how it was before I started rebasing if I so wish:

Confirmation dialogue after rebase

How I built it

Using git hooks, I created a pre-rebase hook to write the SHA into a file I could read later on:

#!/bin/sh

rebasing_file="$PWD"/.git/rebase_in_progress
echo $(git rev-parse HEAD) > $rebasing_file

And a post-rewrite hook to show me the diff and ask whether I want to keep them:

#!/usr/bin/env bash

# post-rewrite is called after amend and rebase but we only want to run
# this particular hook after a rebase
if [[ $1 == 'amend' ]]; then
  exit 0
fi

rebasing_file="$PWD"/.git/rebase_in_progress

if [[ -f $rebasing_file ]]; then
  sha_pre_rebase=$(cat $rebasing_file)

  if [[ $(git diff $sha_pre_rebase) ]]; then
    echo "Your rebase resulted in code changes"
    git diff $sha_pre_rebase

    read -p "Do you want to keep the changes? [Y/n] " -n 1 -r < /dev/tty
    echo ""

    if [[ $REPLY =~ ^[Yy]$ ]]; then
      echo "Cool! All changes kept"
    else
      echo "Discarding changes..."
      echo ""
      git reset --hard $sha_pre_rebase
    fi
  else
    echo "Your rebase caused no code changes, good for you"
  fi

  rm $rebasing_file
fi

Try it yourself

Would you like to try it out? I have created a GitHub repository with the code and instructions on how to add it to your projects.