Skip to content

t1049: Fix auto-rebase: abort stale rebase state before retrying#1480

Merged
marcusquinn merged 1 commit intomainfrom
bugfix/t1049
Feb 14, 2026
Merged

t1049: Fix auto-rebase: abort stale rebase state before retrying#1480
marcusquinn merged 1 commit intomainfrom
bugfix/t1049

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 14, 2026

Summary

  • Abort stale rebase-merge/rebase-apply state before starting a new rebase in rebase_sibling_pr()
  • Prevents "already a rebase-merge directory" fatal errors that permanently block tasks

Root Cause

Follow-up to t1048. When a prior rebase crashes mid-way (AI resolution timeout, process kill, etc.), it leaves rebase state directories behind. Every subsequent rebase attempt on that worktree fails immediately with fatal: It seems that there is already a rebase-merge directory, and the task stays permanently blocked.

Fix

Before calling git rebase origin/main, check if stale rebase state exists and abort it. The check runs once, before the worktree/non-worktree branch, so it covers both paths. Uses git rev-parse --git-dir which resolves correctly for worktrees.

Testing

  • bash -n: pass
  • shellcheck -S warning -x: no new warnings

Summary by CodeRabbit

  • Bug Fixes
    • Improved deployment reliability by adding automatic cleanup for stale rebase states that may persist after failed deployment attempts, preventing deployment conflicts and enabling reliable redeployment.

When a prior rebase crashes mid-way (e.g., AI resolution timeout), it
leaves rebase-merge/rebase-apply directories behind. Subsequent rebase
attempts fail with 'already a rebase-merge directory' and the task
stays permanently blocked.

Fix: check for and abort stale rebase state before starting a new
rebase. Uses git rev-parse --git-dir which works correctly for both
worktrees and main repos.
@gemini-code-assist
Copy link

Summary of Changes

Hello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where a prior rebase operation crashing mid-way would leave behind stale rebase state directories. This state would then cause all subsequent rebase attempts to fail, permanently blocking associated tasks. The fix introduces a check to detect and abort any existing stale rebase state before proceeding with a new rebase, ensuring that tasks can recover and proceed as expected.

Highlights

  • Stale Rebase State Handling: Implemented a mechanism to abort stale rebase-merge or rebase-apply states before initiating a new rebase operation.
  • Error Prevention: Prevents 'already a rebase-merge directory' fatal errors that previously caused tasks to become permanently blocked.
Changelog
  • .agents/scripts/supervisor/deploy.sh
    • Added logic to detect and abort stale Git rebase states (rebase-merge or rebase-apply) before attempting a new rebase.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 14, 2026

Walkthrough

A defensive cleanup step is added to the rebase_sibling_pr function that detects and aborts any stale git rebase state (rebase-merge or rebase-apply directories) before attempting a new rebase operation, preventing failures from prior crashes.

Changes

Cohort / File(s) Summary
Git Rebase State Cleanup
.agents/scripts/supervisor/deploy.sh
Added pre-rebase cleanup that detects stale in-progress rebase state and aborts with a warning log before attempting the rebase operation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

Possibly related PRs

Poem

⚙️ A stale rebase lies in wait,
But our script won't hesitate,
Abort and clean with DevOps grace,
Zero debt, a pristine state!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: adding abort of stale rebase state before retry in the auto-rebase flow, matching the core fix in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/t1049

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 5 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Feb 14 17:08:39 UTC 2026: Code review monitoring started
Sat Feb 14 17:08:40 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 5

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 5
  • VULNERABILITIES: 0

Generated on: Sat Feb 14 17:08:43 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust fix to prevent fatal errors from stale git rebase states. By checking for and aborting any existing rebase operations before starting a new one, it effectively addresses the issue of permanently blocked tasks. The implementation is clean and considers worktree environments correctly. I have one minor suggestion to improve logging and adherence to the repository's style guide regarding error redirection.

Comment on lines +2181 to +2182
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>/dev/null)"
if [[ -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ]]; then

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of 2>/dev/null on line 2181 to suppress potential errors from git rev-parse violates the repository style guide. The guide specifies that errors should be redirected to log files rather than being suppressed. This makes debugging more difficult if an unexpected issue occurs (e.g., if $git_dir is not a valid git repository).

A more robust approach is to redirect stderr to the supervisor log and explicitly check if a git directory was found before proceeding.

Suggested change
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>/dev/null)"
if [[ -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ]]; then
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>>"$SUPERVISOR_LOG")" || true
if [[ -n "$pre_git_state_dir" && ( -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ) ]]; then
References
  1. The style guide at line 50 states that 2>/dev/null is only acceptable when redirecting to log files, not for blanket suppression of errors. The suggested change redirects stderr to the supervisor log to comply with this rule. (link)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.agents/scripts/supervisor/deploy.sh:
- Around line 2178-2185: The stale-rebase detection uses
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir)" which can return a
relative path (".git") and thus the subsequent -d checks run against the
supervisor CWD; make the git-dir absolute (either by calling git -C "$git_dir"
rev-parse --absolute-git-dir or by normalizing pre_git_state_dir after
rev-parse: if it does not start with '/', prefix it with "$git_dir/") so the
rebase-merge/rebase-apply existence checks operate on the correct repository
directory; apply the same normalization to the other occurrences that use
rev-parse --git-dir (the similar checks after the rebase is initiated).

Comment on lines +2178 to +2185
# t1049: Abort any stale rebase state from a prior crash before starting
# a new rebase. Without this, git refuses with "already a rebase-merge directory".
local pre_git_state_dir
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>/dev/null)"
if [[ -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ]]; then
log_warn "rebase_sibling_pr: aborting stale rebase state for $task_id"
git -C "$git_dir" rebase --abort 2>>"$SUPERVISOR_LOG" || true
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check if the file exists and examine its structure
if [ -f ".agents/scripts/supervisor/deploy.sh" ]; then
    echo "=== File found ==="
    wc -l ".agents/scripts/supervisor/deploy.sh"
else
    echo "File not found at expected path"
    find . -name "deploy.sh" -type f 2>/dev/null | head -5
fi

Repository: marcusquinn/aidevops

Length of output: 124


🏁 Script executed:

#!/bin/bash
# Examine the specific lines mentioned in the review
if [ -f ".agents/scripts/supervisor/deploy.sh" ]; then
    echo "=== Lines 2178-2185 ==="
    sed -n '2178,2185p' ".agents/scripts/supervisor/deploy.sh" | cat -n
    echo ""
    echo "=== Context: Lines 2170-2210 ==="
    sed -n '2170,2210p' ".agents/scripts/supervisor/deploy.sh" | cat -n
fi

Repository: marcusquinn/aidevops

Length of output: 3058


🏁 Script executed:

#!/bin/bash
# Verify git rev-parse behavior with a temporary test repo
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

# Create a test repo
git init "$tmpdir/testrepo" >/dev/null 2>&1

echo "=== Non-worktree case ==="
result=$(git -C "$tmpdir/testrepo" rev-parse --git-dir 2>/dev/null)
echo "Result: '$result'"
if [[ "$result" == /* ]]; then
    echo "Status: Absolute path (unexpected for regular repo)"
else
    echo "Status: Relative path (as claimed in review)"
fi

# Create an initial commit and add worktree
cd "$tmpdir/testrepo" && git commit --allow-empty -m "init" >/dev/null 2>&1
git worktree add "$tmpdir/wt" -b testbranch >/dev/null 2>&1

echo ""
echo "=== Worktree case ==="
result=$(git -C "$tmpdir/wt" rev-parse --git-dir 2>/dev/null)
echo "Result: '$result'"
if [[ "$result" == /* ]]; then
    echo "Status: Absolute path (as claimed in review)"
else
    echo "Status: Relative path (contradicts review claim)"
fi

# Test the actual path resolution behavior
echo ""
echo "=== Path resolution behavior test ==="
cd "$tmpdir" || exit 1
non_worktree_gitdir=$(git -C "$tmpdir/testrepo" rev-parse --git-dir 2>/dev/null)
echo "From testrepo: '$non_worktree_gitdir'"

# Test if -d check works with the relative path
if [[ -d "$non_worktree_gitdir" ]]; then
    echo "Relative path check PASSES from current dir"
else
    echo "Relative path check FAILS from current dir"
fi

# Test if -d check works with constructed absolute path
if [[ -d "$tmpdir/testrepo/$non_worktree_gitdir" ]]; then
    echo "Constructed absolute path check PASSES"
else
    echo "Constructed absolute path check FAILS"
fi

Repository: marcusquinn/aidevops

Length of output: 375


🏁 Script executed:

#!/bin/bash
# Find where $git_dir is initialized in the rebase_sibling_pr function
sed -n '2150,2180p' ".agents/scripts/supervisor/deploy.sh" | cat -n

Repository: marcusquinn/aidevops

Length of output: 1214


🏁 Script executed:

#!/bin/bash
# Search for git_dir assignments in the function context
rg 'git_dir=' ".agents/scripts/supervisor/deploy.sh" -B 2 -A 2 | head -50

Repository: marcusquinn/aidevops

Length of output: 370


🏁 Script executed:

#!/bin/bash
# Look for the function definition and early variable assignments
sed -n '2100,2180p' ".agents/scripts/supervisor/deploy.sh" | grep -E 'rebase_sibling_pr|git_dir|use_worktree' | cat -n

Repository: marcusquinn/aidevops

Length of output: 748


🏁 Script executed:

#!/bin/bash
# Check for other instances of the same pattern at lines 2197-2198 and 2234-2235
echo "=== Lines 2195-2200 ==="
sed -n '2195,2200p' ".agents/scripts/supervisor/deploy.sh" | cat -n

echo ""
echo "=== Lines 2230-2240 ==="
sed -n '2230,2240p' ".agents/scripts/supervisor/deploy.sh" | cat -n

Repository: marcusquinn/aidevops

Length of output: 1356


🏁 Script executed:

#!/bin/bash
# Verify that git_dir can indeed be either trepo or tworktree at line 2178
# and test what rev-parse returns for an actual worktree
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

git init "$tmpdir/main_repo" >/dev/null 2>&1
cd "$tmpdir/main_repo"
git commit --allow-empty -m "init" >/dev/null 2>&1
git worktree add "$tmpdir/worktree_path" -b test-branch >/dev/null 2>&1

echo "=== Test 1: Main repo with -C ==="
main_git_dir=$(git -C "$tmpdir/main_repo" rev-parse --git-dir 2>/dev/null)
echo "Result: '$main_git_dir'"
echo "Is absolute: $(if [[ \"$main_git_dir\" == /* ]]; then echo 'YES'; else echo 'NO'; fi)"

echo ""
echo "=== Test 2: Worktree with -C ==="
wt_git_dir=$(git -C "$tmpdir/worktree_path" rev-parse --git-dir 2>/dev/null)
echo "Result: '$wt_git_dir'"
echo "Is absolute: $(if [[ \"$wt_git_dir\" == /* ]]; then echo 'YES'; else echo 'NO'; fi)"

echo ""
echo "=== Test 3: Directory existence check from different CWD ==="
cd "$tmpdir" || exit 1

# Simulate the bug: check with relative path from wrong CWD
if [[ -d "$main_git_dir" ]]; then
    echo "Check '-d \"$main_git_dir\"' from $tmpdir: FOUND (unexpected!)"
else
    echo "Check '-d \"$main_git_dir\"' from $tmpdir: NOT FOUND (confirms bug)"
fi

# Fixed version: prepend the repo path
fixed_path="$tmpdir/main_repo/$main_git_dir"
if [[ -d "$fixed_path" ]]; then
    echo "Check '-d \"$fixed_path\"': FOUND (fix works)"
else
    echo "Check '-d \"$fixed_path\"': NOT FOUND"
fi

Repository: marcusquinn/aidevops

Length of output: 387


Relative path from rev-parse --git-dir breaks stale rebase detection for non-worktree repos.

When git_dir points to the main repository (non-worktree case at this stage), git -C "$git_dir" rev-parse --git-dir returns .git—a relative path. The subsequent -d checks then resolve against the supervisor script's CWD rather than $git_dir, causing the stale rebase state detection to silently fail.

This directly breaks the crash-recovery feature for the primary (non-worktree) use case. The fix requires normalizing relative paths:

🐛 Construct absolute path for git directory
 	local pre_git_state_dir
 	pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>/dev/null)"
+	# rev-parse --git-dir returns a relative path (e.g., ".git") for non-worktree repos;
+	# make it absolute so the -d checks resolve correctly.
+	if [[ "$pre_git_state_dir" != /* ]]; then
+		pre_git_state_dir="$git_dir/$pre_git_state_dir"
+	fi
 	if [[ -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ]]; then
 		log_warn "rebase_sibling_pr: aborting stale rebase state for $task_id"
 		git -C "$git_dir" rebase --abort 2>>"$SUPERVISOR_LOG" || true
 	fi

Note: The same pattern exists at lines 2197–2198 and 2234–2235 (both in conditional branches after the rebase is initiated), though those are pre-existing.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# t1049: Abort any stale rebase state from a prior crash before starting
# a new rebase. Without this, git refuses with "already a rebase-merge directory".
local pre_git_state_dir
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>/dev/null)"
if [[ -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ]]; then
log_warn "rebase_sibling_pr: aborting stale rebase state for $task_id"
git -C "$git_dir" rebase --abort 2>>"$SUPERVISOR_LOG" || true
fi
# t1049: Abort any stale rebase state from a prior crash before starting
# a new rebase. Without this, git refuses with "already a rebase-merge directory".
local pre_git_state_dir
pre_git_state_dir="$(git -C "$git_dir" rev-parse --git-dir 2>/dev/null)"
# rev-parse --git-dir returns a relative path (e.g., ".git") for non-worktree repos;
# make it absolute so the -d checks resolve correctly.
if [[ "$pre_git_state_dir" != /* ]]; then
pre_git_state_dir="$git_dir/$pre_git_state_dir"
fi
if [[ -d "$pre_git_state_dir/rebase-merge" || -d "$pre_git_state_dir/rebase-apply" ]]; then
log_warn "rebase_sibling_pr: aborting stale rebase state for $task_id"
git -C "$git_dir" rebase --abort 2>>"$SUPERVISOR_LOG" || true
fi
🤖 Prompt for AI Agents
In @.agents/scripts/supervisor/deploy.sh around lines 2178 - 2185, The
stale-rebase detection uses pre_git_state_dir="$(git -C "$git_dir" rev-parse
--git-dir)" which can return a relative path (".git") and thus the subsequent -d
checks run against the supervisor CWD; make the git-dir absolute (either by
calling git -C "$git_dir" rev-parse --absolute-git-dir or by normalizing
pre_git_state_dir after rev-parse: if it does not start with '/', prefix it with
"$git_dir/") so the rebase-merge/rebase-apply existence checks operate on the
correct repository directory; apply the same normalization to the other
occurrences that use rev-parse --git-dir (the similar checks after the rebase is
initiated).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant