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
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.
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 <firstname.lastname@example.org> 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:
But my change resulted in conflicts:
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:
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
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.