In a Git interactive rebase, how do you run the test suite on every commit?
answer
- a todo verb that is not a commit
- runs a shell command during the replay
- one flag inserts it after every pick
- exit code decides stop or continue
- it is a build per commit, so it is slow
basics
~20 sUse git rebase -i --exec "make test" <base>. Git inserts an exec line after every commit in the todo list and runs that command as each commit is created; a non-zero exit stops the rebase at that commit so you can fix it and continue.
solid answer
~40 s`git rebase --exec <cmd> <base>` (which implies `-i`) appends an `exec <cmd>` line after **every** `pick` line in the todo list, so the command runs once per replayed commit, in the working tree at that commit's state. If the command exits non-zero, Git halts the rebase right there with the failing commit checked out; you fix it, `git add`, `git commit --amend`, and `git rebase --continue`. You can also type `exec` lines by hand to run a command after only some commits, which is cheaper on a long branch. The point is to guarantee every commit on the branch builds and tests on its own, so nobody lands a branch whose intermediate commits are broken. Expect it to be slow — it is a full build per commit.
code
console · 9 lines$ git rebase -i --exec "make test" origin/main
Executing: make test
...
Executing: make test
make: *** [test] Error 1
warning: execution failed: make test
You can fix the problem, and then run
git rebase --continuego deeper
Know that a rebase todo list can contain exec lines that run a shell command during the replay, and that the rebase pauses if the command fails.
Explain that --exec inserts the line after every commit, that only the exit code matters, and how to amend and --continue after a failure.
Show the production judgment: verify that every commit on a cleaned-up branch actually builds, choose a cheap enough command, and know the cost of a build per commit.
Decide whether per-commit greenness is a standard the team enforces, where that check runs, and what it buys against the time it costs on every branch.
## What exec is `exec` (`x`) is a todo verb that is not a commit at all. Its argument is a shell command, run from the top level of the working tree at whatever state the replay has reached. A todo list can mix them freely: pick 9f2a1c3 add parser exec make test pick 4b7e0aa add validator exec make test The `--exec <cmd>` command-line option is a shortcut that inserts one such line after **every** commit for you, and it implies `--interactive`, so you get the todo list to review as well. ## The exit code is the contract Git only looks at the exit status. Zero means continue to the next todo entry. Non-zero means **stop the rebase** with a message saying the execution failed, leaving you at that point in the replay with the commit already created and the remaining todo entries pending. From there you can: - fix the source, `git add`, `git commit --amend` to repair the commit, then `git rebase --continue`; - or make no change and `git rebase --continue` anyway, accepting the failure; - or `git rebase --abort` to put the branch back as it was. Because it is a shell command, the usual shell rules apply: quote it as one argument, and `x cd sub && make` behaves like any shell line. A command that leaves the working tree dirty will make the next step complain, so tests that write artefacts should clean up or write outside the repo. ## Why you would do this The motivation is the difference between "the branch tip is green" and "every commit is green". After an interactive rebase that reordered, split or folded commits, the tip may be fine while an intermediate commit does not compile — nothing verified them individually. Broken intermediate commits hurt in two concrete ways: anyone who checks out an old commit to reproduce something gets a build failure, and any automated search over history has to work around commits that cannot be tested. Running `--exec` with the build (or a fast subset of tests) after a cleanup rebase is the mechanical proof that the cleaned-up history is honest. ## Cost and how to control it This is expensive: a twenty-commit branch means twenty builds. Practical mitigations: - Use a **cheap** command — `--exec "make build"` or a single fast test target rather than the whole suite. - Hand-write `exec` lines after only the commits that matter, instead of using the flag that inserts them everywhere. - Run it before publishing the branch, not on every rebase. - Combine with `--autosquash` so the plan is arranged in one pass and you only pay the build cost once. ## Neighbouring mechanics worth knowing - `break` (`b`) is the manual sibling: it stops the replay at that point with no command run, so you can poke around and then `git rebase --continue`. - `git rebase --edit-todo` lets you add or remove `exec` lines mid-rebase — useful when the first failure tells you the rest of the run will be wasted. - A failing `exec` does **not** abort the rebase; it pauses it. The branch ref is still not updated until the whole list completes, so the pre-rebase state is intact if you abort. ## Failure modes to mention - **Treating a stop as a crash.** The rebase is paused and resumable; `--continue` and `--abort` both work. - **Assuming a green tip implies green history.** That is exactly the assumption `--exec` exists to break. - **Forgetting the working tree must be clean to continue.** Test output left behind will block or confuse the next step. - **Running it on someone else's published branch.** The rebase still rewrites every commit, with the usual consequences for a branch others have pulled. - **Expecting Git to parse the command's output.** It does not — only the exit code is read.
- What exactly happens when the exec command exits non-zero?Git stops the rebase at that point, prints that the execution failed, and leaves you with that commit created and the remaining todo entries pending. You can fix and `git commit --amend`, then `git rebase --continue`; continue anyway; or `git rebase --abort`. The branch ref is not updated until the list finishes, so aborting restores the original branch.
- How do you run a command after only one specific commit instead of all of them?Skip the `--exec` option and write the `exec` line yourself in the todo list, directly after the commit you care about. That is far cheaper on a long branch than a build per commit, and you can also add lines mid-rebase with `git rebase --edit-todo` once you know where the interesting commit is.
- What is the difference between the exec and break todo verbs?`exec` runs a shell command and only stops the rebase if that command fails. `break` always stops at that point in the list with nothing run, so you can inspect the tree manually. Both resume with `git rebase --continue`; one is automated verification, the other is a deliberate manual pause.
saying these in an interview costs you the question
- Assuming a green branch tip means every commit builds
- Thinking a failed exec aborts and discards the rebase
- Believing Git inspects the command's output rather than its exit code
- Claiming exec creates a commit of its own
- Running a full suite per commit on a long branch without expecting the cost