The solution is to have two separate scripts: the one that kicks off git rebase --interactive (the “wrapper script”) and one that will be run at the end of the rebase (the “post-rebase script”). You can insert an exec line into the rebase todo list to ensure that the latter is run automatically at the end of the rebase. Furthermore, you can set the GIT_SEQUENCE_EDITOR environment variable to automate the addition of the exec line itself.
The post-rebase script
Take the logic that should be run at the end of a rebase and put it into a separate script. (Note that this script will not be run if the rebase is aborted or quit.) If the script needs any context other than what’s available in the environment during a rebase, it should accept this information as command-line arguments.
For example, my shell script is called check_git_diff and it looks like
#!/usr/bin/env zsh
if [[ $# -ne 1 ]]; then
print >&2 "usage: check_git_diff ORIGINAL_HEAD"
exit 1
fi
readonly original_head=$1
if ! git diff --quiet $original_head HEAD; then
kind='non-whitespace'
if git diff --quiet --ignore-all-space $original_head HEAD; then
kind='whitespace'
fi
print -P >&2 "%F{red}Warning:%f There are $kind differences from the original HEAD. See"
print -P >&2 "\n %F{blue}git diff $original_head HEAD%f\n"
fi
Calling this script automatically
You can ask Git itself to run this script at the end of the rebase by adding an exec line to the end of the todo list. After all the pick, squash, fixup, etc. lines, add one for the post-rebase script:
pick 8bbfd15 update build instructions
pick caf5bfd frobnicate some additional blobs
exec check_git_diff caf5bfd3d4b6099aeed13604936976e610a08e18
# Rebase 9f594d2..caf5bfd onto 8bbfd15 (2 commands)
#
# ...
As you can see, you can include arguments to the post-rebase script if you need to.
If you run git rebase --abort or git rebase --quit, Git will stop running these commands, which is why I said above that check_git_diff will only be run at the conclusion of a successful rebase.
Automatically calling this script automatically
Since it’s your script that’s kicking off git rebase --interactive in the first place, you have the opportunity to automate the previous step. In the wrapper script, set the GIT_SEQUENCE_EDITOR environment variable prior to calling git rebase --interactive. For example,
original_head=$(git rev-parse HEAD)
export GIT_SEQUENCE_EDITOR="f() {
sed -i -e '/^$/a exec check_git_diff $original_head\n' \$1 && exec vim \$1
}; f"
If the GIT_SEQUENCE_EDITOR environment variable is set, Git will use its value as the name of the editor to use to edit the todo list. Typically you’d set this to something like vim or emacs, but here I’m actually defining a shell function and then using that function as the editor. (Git uses Bash to run the contents of GIT_SEQUENCE_EDITOR.) The shell function does two things: it calls sed to modify the todo list to include the exec line, and then it runs Vim so that you can edit the todo list like normal. Of course, you can replace vim with the name of your preferred editor.
The net effect is that you can run some certain logic before the interactive rebase starts, and then run some certain other logic after it successfully ends.
Thank you to @torek for bringing the GIT_SEQUENCE_EDITOR variable to my attention. That makes the solution a lot simpler!