The letter A styled as Alchemists logo. lchemists
Published July 15, 2025 Updated August 10, 2025
Cover
Git Rebase Exec

When using Git Rebase, you might find yourself wishing you could automate manual tasks by executing code during the rebase. Well, good news, you can! Executing code is one of the most powerful features of rebasing but also a slightly more complex feature to use than the other commands.

To start, let’s initialize a Git repository with a basic Ruby calculator implementation:

mkdir demo
cd demo
git init

printf "%s\n\n" "# frozen_string_literal: true" > calc.rb
git add --all .
git commit --message "Added calculator script file" \
           --message "Provides initial script file only (implementation to be added later)."

printf "%s\n" 'ARGV.then { |a, b| puts "#{a} plus #{b} equals #{a.to_i + b.to_i}." }' >> calc.rb
git add --all .
git commit --message "Added calculator implementation" \
           --message "Necessary for calculating the addition of two numbers."

If we look at our commit history, we see we have two commits:

git log --oneline
# af7ab2584e62 (HEAD -> main) Added calculator implementation
# d01d87b8c12c Added calculator script file

Now we can kick off a Git Rebase to add execution commands:

git rebase --interactive --root

⚠️ I’m using --root to rebase from the very first commit on this repository. In practice, Git Rebase should only be performed on a feature branch but since this repository isn’t shared with anyone, we can freely rebase without harming anyone.

The above will the yield the following in your editor:

p d01d87b8c12c # Added calculator script file
p af7ab2584e62 # Added calculator implementation

💡 I’m using Git Rebase Abbreviations if you’re wondering why each p isn’t pick.

Now update the above as follows:

p d01d87b8c12c # Added calculator script file
x printf "%s\n" "Demo."
p af7ab2584e62 # Added calculator implementation

Once you save the above changes and exit the Git Rebase Editor, you should see the following in your console:

Executing: printf "%s\n" "Demo."
Demo.
Successfully rebased and updated refs/heads/main.

Here we see Git running our x printf "%s\n" "Demo." command which printed Demo. as output. As long as your script’s exit code isn’t an error, then rebase will continue. To illustrate further, let’s rebase again by running git rebase --interactive --root with the following commands:

p d01d87b8c12c # Added calculator script file
x exit 1
p af7ab2584e62 # Added calculator implementation

When you save and exit the above, you should be presented with the following in your console:

Executing: exit 1
warning: execution failed: exit 1
You can fix the problem, and then run

  git rebase --continue

Notice Git halted the rebase so you can correct the issue accordingly. In this case, we trigged the error ourselves so we can use git rebase --continue to finish the rebase since there is nothing to do.

You’re not limited to only a single execution command, you can intersperse them between all commits if desired. For example, run git rebase --interactive --root again but with the following commands:

x printf "%s\n" "First script."
p d01d87b8c12c # Added calculator script file
x printf "%s\n" "Second script."
p af7ab2584e62 # Added calculator implementation

Now save and exit. You should see the following output this time:

Executing: printf "%s\n" "First script."
First script.
Executing: printf "%s\n" "Second script."
Second script.
Successfully rebased and updated refs/heads/main.

While the above are simple execution commands, you’ll often find yourself not wanting to repeat yourself by manually writing all of the execution commands each time you rebase. Luckily, Git supports running reusable scripts. For example, let’s say we want to use RuboCop to scan commits to debug a situation where we introduced a lint/syntax issue in our code. Here’s how to build that script:

mkdir bin
printf "%s\n" "#! /usr/bin/env bash" > bin/rubocop-scan
printf "\n%s\n" 'rubocop "$(git diff --name-only HEAD^)"' >> bin/rubocop-scan
chmod 755 bin/rubocop-scan

The above Bash script uses git diff --name-only HEAD^ to list files changed between the previous and current commit which, in this case, yields the following: rubocop calc.rb. Now let’s use git rebase --interactive --root to kick off the rebase with the following commands:

p d01d87b8c12c # Added calculator script file
p af7ab2584e62 # Added calculator implementation
x bin/rubocop-scan

You should see the following output:

Executing: bin/rubocop-scan
Inspecting 1 file
.

1 file inspected, no offenses detected
Successfully rebased and updated refs/heads/main.

Granted, this is silly because we didn’t have any violations and could have run RuboCop without using Git Rebase like this but the point is to demonstrate Git executing a reusable script.

Before wrapping up, it’s worth mentioning rebase accepts the --exec option when rebasing:

git rebase --interactive --root --exec bin/rubocop-scan

The above is great when applying the same script to all commits. …​and, if you don’t need visual confirmation, the following is faster:

GIT_EDITOR=true git rebase --interactive --root --exec bin/rubocop-scan

💡 Use of GIT_EDITOR=true tricks Git into thinking the editor saved successfully.

Now you know how to execute arbitrary code between your commits when rebasing. This only scratches the surface of what’s possible. Skies the limit.

Enjoy and may your rebasing workflow benefit from additional automation!