Skip to main content

Non-Interactive Git Rebase Guide

Status: Stable Guide for performing git rebases in non-interactive shells (automation, CI, AI agents).

The Problem​

Interactive git rebase -i opens an editor and waits for user input, which doesn't work in automated contexts. This guide shows reliable alternatives.

Use case: Squash multiple commits into one

# Squash commits from a5f4983 to fc0d964 into a single commit
git reset --hard fc0d964 # Go to end of range
git reset --soft a5f4983 # Soft reset to start (keeps changes staged)
git commit -m "Squashed message" # Commit all changes as one

# Re-apply any commits that should come after
git cherry-pick 8b23aeb..58ecfaf

Why this works:

  • --soft preserves all file changes in staging area
  • No editor needed, fully deterministic
  • Simple and reliable
  • Clean for combining many commits

When to use:

  • Squashing evolution/experimentation commits
  • Combining related commits from a feature branch
  • Cleaning up commit history before merge

Method 2: GIT_SEQUENCE_EDITOR (For Rewording)​

Use case: Change commit message without changing code

# Reword a specific commit message
GIT_SEQUENCE_EDITOR="sed -i '' '1s/^pick/reword/'" git rebase -i <commit>^

Platform-specific sed syntax:

# macOS/BSD (empty string after -i)
sed -i '' 's/pick/reword/' file

# Linux/GNU (no argument after -i)
sed -i 's/pick/reword/' file

# Portable (use ed instead)
ed -s file <<< $',s/pick/reword/\nw'

Using a script file:

cat > /tmp/rebase-edit.sh << 'EOF'
#!/bin/bash
sed -i '' 's/^pick \(abc123\)/reword \1/' "$1"
EOF
chmod +x /tmp/rebase-edit.sh
GIT_SEQUENCE_EDITOR="/tmp/rebase-edit.sh" git rebase -i <commit>^

What the script receives:

  • $1 = path to git-rebase-todo file
  • Must modify file in-place
  • Must exit with code 0

When to use:

  • Changing commit message for an older commit
  • Selective reword/edit/squash operations
  • When reset+commit approach isn't suitable

Method 3: Amend (Simplest for Last Commit)​

Use case: Change the most recent commit

# Change last commit message
git commit --amend -m "New message"

# Keep original message, just change files
git commit --amend --no-edit

# Change message interactively (opens editor)
git commit --amend # Don't use in non-interactive contexts!

When to use:

  • Modifying the most recent commit only
  • Adding forgotten files to last commit
  • Fixing typos in last commit message

Method 4: Autosquash Workflow​

Use case: Mark commits for automatic squashing later

# During development: create fixup/squash commits
git commit --fixup=<target-commit-hash>
git commit --squash=<target-commit-hash>

# Later: automatically combine them
git rebase -i --autosquash <base>

Difference:

  • --fixup: Discards the fixup commit message
  • --squash: Combines commit messages (opens editor)

When to use:

  • Incremental fixes during feature development
  • Clean up before final merge
  • Requires planning ahead (mark commits during development)

Common Pitfalls​

❌ Don't do this:

# Hangs waiting for editor in non-interactive shell
git rebase -i HEAD~5

# Wrong variable (controls commit message editor, not sequence)
GIT_EDITOR=true git rebase -i HEAD~5

# Platform-specific sed (breaks on BSD)
sed -i 's/pick/reword/' file # Missing '' on macOS

✅ Do this instead:

# Use GIT_SEQUENCE_EDITOR for sequence file
GIT_SEQUENCE_EDITOR=true git rebase -i HEAD~5

# Or use reset+commit for squashing (simpler)
git reset --soft HEAD~5
git commit -m "Squashed"

# Platform-safe sed
sed -i '' 's/pick/reword/' file # macOS

Best Practices​

  1. For squashing multiple commits: Use git reset --soft (simplest, most reliable)
  2. For rewording one commit: Use git commit --amend (if last) or GIT_SEQUENCE_EDITOR (if older)
  3. For complex multi-step rebases: Break into smaller operations
  4. Always verify after rebase: Run git log --oneline -10 to check result
  5. Test locally first: Never experiment with rebasing in CI/production
  6. Create backups: git branch backup-name HEAD before risky rebases
  7. Prefer simple operations: The simpler the rebase, the less likely to fail

Verification Checklist​

After any rebase operation:

# Check commit history
git log --oneline -10

# Compare file changes to expected state
git diff <original-commit> HEAD --stat

# Verify build still works
pnpm build

# Check for unintended changes
git status

Real Example from This Session​

Goal: Squash commits a5f4983..fc0d964 (11 commits exploring retry system), then re-apply commits after

# Step 1: Hard reset to end of range
git reset --hard 5106976
# HEAD is now at 5106976 refactor: radically simplify retry system

# Step 2: Soft reset to start (stages all changes)
git reset --soft a5f4983
# All changes from 11 commits now staged

# Step 3: Commit as one
git commit -m "feat: implement simple retry system for design tests"
# New commit created with squashed changes

# Step 4: Re-apply commits after fc0d964
git cherry-pick 8b23aeb 28f44d0 4029e70 b6b514d b8343ed 055f6f1 58ecfaf
# Commits after the squashed range preserved

# Step 5: Verify
git log --oneline -12
# Shows clean history with squashed commit + re-applied commits

Result:

  • 11 commits squashed into 1
  • 7 commits re-applied on top
  • Clean, linear history
  • No merge conflicts (deterministic)

References​