How gh-stack Handles Rebase Conflicts Using Git rerere
gh-stack automates rebase conflict resolution by enabling Git's rerere (reuse recorded resolution) feature, which records previous fixes and replays them during cascading rebases, falling back to manual intervention only when encountering previously unseen conflicts.
gh-stack is a GitHub CLI extension that manages stacked pull requests. When you rebase complex stacks of dependent branches, merge conflicts are inevitable. According to the github/gh-stack source code, the tool integrates deeply with Git's built-in rerere mechanism to remember how you resolved conflicts once and automatically apply those resolutions to future rebases.
Enabling rerere Before Rebase Operations
Before any command that might trigger a rebase, gh-stack ensures the repository is configured to record conflict resolutions. This happens through the ensureRerere helper function.
Checking Repository Configuration
The tool first queries the repository state using IsRerereEnabled, which inspects Git's configuration for the rerere.enabled setting. In internal/git/gitops.go (lines 282-306), this check determines whether the automation layer can rely on recorded resolutions or needs to prompt the user.
Prompting the User
If rerere is disabled, ensureRerere—implemented in cmd/utils.go—interactively asks: "Enable git rerere to remember conflict resolutions?" The function accepts a boolean true default, allowing users to simply press Enter to enable the feature.
// cmd/utils.go – ensureRerere workflow
if !git.IsRerereEnabled() {
ok, err := cfg.Confirm("Enable git rerere to remember conflict resolutions?", true)
if err != nil { return err }
if ok {
if err := git.EnableRerere(); err != nil { return err }
} else {
git.SaveRerereDeclined()
}
}
Persisting User Preferences
When a user declines to enable rerere, SaveRerereDeclined stores this preference to prevent repetitive prompting across command invocations. Conversely, accepting the prompt triggers EnableRerere, which sets both rerere.enabled and rerere.autoupdate to true in the Git configuration. The autoupdate flag is critical—it automatically stages files that rerere resolves, removing the need for manual git add operations during automated rebases.
Automatic Conflict Resolution During Rebases
When a cascading rebase encounters a conflict that has been resolved before, gh-stack leverages the recorded resolution without human intervention.
The tryAutoResolveRebase Loop
In internal/git/git.go (lines 114-138), the tryAutoResolveRebase function implements a retry loop that attempts to continue the rebase automatically. Because rerere.autoupdate is enabled, Git stages any resolved files automatically, allowing git rebase --continue to proceed immediately.
// internal/git/git.go – automatic resolution loop
func tryAutoResolveRebase() error {
for i := 0; i < 1_000; i++ {
if err := git.RebaseContinue(); err != nil { return err }
// If no more conflicts exist, the rebase completes automatically
if !git.IsRebaseInProgress() { return nil }
}
return fmt.Errorf("rebase not auto-resolved after many attempts")
}
The loop caps at 1,000 attempts to prevent infinite retries if a conflict remains unresolvable. Each iteration relies on Git's rerere database to match the current conflict markers against previously recorded resolutions.
How rerere.autoupdate Stages Resolved Files
The rerere.autoupdate configuration set during initialization ensures that when rerere successfully resolves a conflicted file, Git immediately stages that file to the index. This eliminates the manual git add step that would otherwise interrupt an automated rebase flow, allowing tryAutoResolveRebase to call RebaseContinue repeatedly until the operation completes or hits a new, unrecorded conflict.
Integration Points in gh-stack Commands
The ensureRerere function is invoked defensively before any operation that might trigger complex rebasing:
cmd/init.go(line 70): Enables rerere when initializing a new stack.cmd/rebase.go(line 128): Prepares conflict resolution before rebasing the entire stack onto a new base.cmd/sync.go(line 104): Ensures rerere is active before synchronizing branch dependencies.
This consistent pre-flight check guarantees that users won't lose conflict resolution history mid-operation.
Manual Resolution Fallback
When rerere encounters a conflict it has not seen before—or when the context has changed significantly enough that the recorded resolution no longer applies—gh-stack stops the automation and presents the conflict to the user. After manual resolution and staging, the standard rebase continue flow resumes. Any new resolution you create is automatically recorded by rerere for future use, expanding the database of known fixes.
You can also enable rerere manually outside of gh-stack to achieve the same effect:
# Manual configuration equivalent to gh-stack's EnableRerere
git config --global rerere.enabled true
git config --global rerere.autoupdate true
Summary
- gh-stack integrates Git's rerere feature to automate conflict resolution during stacked branch rebases.
- The
ensureRererefunction incmd/utils.goprompts users to enable rerere before any rebase operation, settingrerere.enabledandrerere.autoupdate. tryAutoResolveRebaseininternal/git/git.goimplements a capped retry loop (1,000 attempts) that automatically continues rebases when rerere resolves conflicts.rerere.autoupdatestages resolved files automatically, removing manual intervention from the automated flow.- If rerere cannot resolve a conflict, gh-stack falls back to manual resolution, with new fixes recorded for future automation.
- The tool invokes these checks consistently across
init,rebase, andsynccommands to ensure the feature is active before conflicts arise.
Frequently Asked Questions
What is Git rerere and why does gh-stack use it?
Git rerere (reuse recorded resolution) is a Git feature that automatically remembers how you resolved a conflict and reapplies that resolution when the same conflict appears again. gh-stack uses it because stacked pull requests require frequent rebasing of dependent branches, and rerere eliminates the tedious repetition of resolving the same merge conflicts across multiple branches.
How do I disable the rerere prompt in gh-stack?
If you choose not to enable rerere when prompted, gh-stack calls SaveRerereDeclined to persist your preference, preventing future prompts. To reverse this decision and enable rerere later, you can run git config rerere.enabled true manually, or delete the stored preference in gh-stack's configuration storage to trigger the prompt again on the next rebase operation.
What happens if rerere resolves a conflict incorrectly?
If rerere applies a recorded resolution that is no longer correct due to code changes, the resulting conflict markers or incorrect merge will cause the rebase to fail tests or compilation. In this case, you should resolve the conflict manually, update the rerere cache by resolving it correctly, and commit the proper resolution. The next rebase will record the corrected resolution.
Why does tryAutoResolveRebase limit retries to 1,000 attempts?
The 1,000-attempt limit in tryAutoResolveRebase serves as a safety guard against infinite loops. In pathological cases where rerere repeatedly resolves a conflict in a way that immediately re-creates the same conflict (or when Git's state machine enters an unexpected cycle), the cap ensures the process terminates with an error rather than hanging indefinitely. In practice, most auto-resolved rebases complete in fewer than ten iterations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →