Skip to content

feat(git-shim): warn on reset --soft/--mixed onto a diverged target - #462

Merged
getappz merged 2 commits into
masterfrom
task/98
Aug 12, 2026
Merged

feat(git-shim): warn on reset --soft/--mixed onto a diverged target#462
getappz merged 2 commits into
masterfrom
task/98

Conversation

@getappz

@getappz getappz commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • git reset --soft/--mixed onto a target that's diverged from HEAD (not just fast-forwarded) silently stages the full content diff between them — including any unrelated drift the current branch never had, not just the intended commit(s).
  • Live incident (agentflare#98): rescuing a stranded fix on a stale branch, git reset --soft origin/master staged ~20 files including a phantom crate deletion that was actually a refactor on master — caught manually before committing, but nothing warned.
  • Adds a warning (not a block — this is a legitimate common operation) in the git shim when reset --soft/--mixed targets a diverged ref, using merge-base --is-ancestor in both directions to detect true divergence vs. a clean fast-forward, plus rev-list --left-right --count for the commit count shown in the message.
  • --hard is unaffected — that's is_destructive's existing snapshot-based protection (a working-tree-loss concern), orthogonal to this (a staged-diff-surprise concern).

Test plan

  • cargo test -p flare-git-core reset_soft_divergence — 4/4 pass (soft/mixed-only gating, no-target no-op, fires on real divergence, silent on both fast-forward directions)
  • cargo clippy clean on both touched files
  • cargo fmt --check clean
  • cargo test -p flare-git-shim — 3 pre-existing failures unrelated to this diff, confirmed identical on unmodified master (agent/human detection quirk in this environment, not caused by this change)

Agentflare-Item: 98

Summary by CodeRabbit

  • New Features

    • Added warnings for potentially risky git reset --soft and git reset --mixed operations when histories have diverged.
    • Warnings identify possible staged content differences before the Git command runs.
  • Bug Fixes

    • Improved handling of missing targets and unresolved Git history without blocking the reset operation.
  • Tests

    • Added coverage for applicable reset modes, divergent histories, and clean ancestor/descendant resets.

reset --soft/--mixed silently stages the full content diff between
HEAD and target when they've diverged (not just fast-forwarded) --
including any unrelated drift the current branch never had. Adds a
warning (not a block) when neither ref is an ancestor of the other,
using merge-base --is-ancestor in both directions plus rev-list
--left-right --count for the divergence size. --hard is unaffected --
that's is_destructive's existing snapshot-based protection, a
different concern (working-tree loss vs. staged-diff surprise).

Agentflare-Branch: task/98
Agentflare-Item: 98
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds divergence detection for reset --soft and reset --mixed. The Git shim checks the target and HEAD histories before execution and prints a warning when both histories contain unique commits.

Changes

Reset divergence warning

Layer / File(s) Summary
Divergence detection and validation
crates/flare-git-core/src/classify.rs
reset_soft_divergence_warning checks explicit targets for soft and mixed resets. It warns only when both histories contain unique commits. Tests cover excluded commands, missing targets, divergent histories, and ancestor relationships.
Shim execution integration
crates/flare-git-shim/src/main.rs
The shim calls the classifier before executing Git and prints the warning when one is returned.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant flare-git-shim
  participant flare-git-core
  participant Git
  User->>flare-git-shim: Run reset command
  flare-git-shim->>flare-git-core: Check reset divergence
  flare-git-core->>Git: Resolve HEAD and target histories
  Git-->>flare-git-core: Return ancestry data
  flare-git-core-->>flare-git-shim: Return warning or no warning
  flare-git-shim->>User: Print warning when applicable
  flare-git-shim->>Git: Execute reset command
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the warning added for divergent soft and mixed resets.
Description check ✅ Passed The description includes a detailed summary and test plan, with risk and compatibility information addressed in the summary.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/98

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/flare-git-core/src/classify.rs`:
- Around line 313-320: Update the reset classification logic around the
subcommand mode check to parse only arguments before the `--` boundary, treat an
absent mode as the default `--mixed`, and reject pathspec and `--patch` forms.
Ensure target selection and divergence handling use the pre-boundary arguments,
and extend the related tests covering implicit mixed resets and arguments after
`--`.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38041212-1ea1-41e9-acaa-168a125192b4

📥 Commits

Reviewing files that changed from the base of the PR and between c905c38 and 6f12be3.

📒 Files selected for processing (2)
  • crates/flare-git-core/src/classify.rs
  • crates/flare-git-shim/src/main.rs

Comment on lines +313 to +320
if subcommand != "reset" || !args.iter().any(|a| a == "--soft" || a == "--mixed") {
return None;
}
let target = args
.iter()
.take_while(|a| a.as_str() != "--")
.find(|a| !a.starts_with('-'))?;
let commits = reset_soft_divergence(repo_root, target)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate file outline ---'
ast-grep outline crates/flare-git-core/src/classify.rs 2>/dev/null | head -200 || true
printf '%s\n' '--- relevant implementation ---'
sed -n '260,345p' crates/flare-git-core/src/classify.rs
printf '%s\n' '--- referenced tests ---'
sed -n '1370,1485p' crates/flare-git-core/src/classify.rs
printf '%s\n' '--- related symbols and call sites ---'
rg -n -C 3 'reset_soft_divergence|--soft|--mixed|subcommand.*reset|reset.*subcommand' crates/flare-git-core

Repository: getappz/agentflare

Length of output: 25163


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- repository files related to classify tests ---'
git ls-files | rg '(^|/)(classify|git-core|Cargo\.toml|Cargo\.lock)' | head -200
printf '%s\n' '--- exact test names and reset test bodies ---'
rg -n -C 8 'reset|divergen|soft|mixed' crates/flare-git-core/src/classify.rs crates/flare-git-core/tests 2>/dev/null | head -500

Repository: getappz/agentflare

Length of output: 20883


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- Git reset synopsis ---'
git reset -h 2>&1 | sed -n '1,120p'
printf '%s\n' '--- warning callers ---'
rg -n -C 8 'reset_soft_divergence_warning' crates src
printf '%s\n' '--- classify orchestration around warning handling ---'
sed -n '680,805p' crates/flare-git-core/src/classify.rs
printf '%s\n' '--- current argument behavior for representative inputs ---'
python3 - <<'PY'
def current(args):
    if not any(a in ('--soft', '--mixed') for a in args):
        return None
    before = []
    for a in args:
        if a == '--':
            break
        before.append(a)
    return next((a for a in before if not a.startswith('-')), None)

cases = [
    ['origin/master'],
    ['origin/master', '--', 'path'],
    ['origin/master', '--', '--soft'],
    ['--soft', 'origin/master', '--', 'path'],
    ['--', '--soft'],
    ['--patch', 'origin/master'],
]
for args in cases:
    print(args, '=>', current(args))
PY

Repository: getappz/agentflare

Length of output: 17311


🌐 Web query:

git reset documentation default mixed mode pathspec -- boundary patch form

💡 Result:

The git reset command documentation defines multiple invocation forms, each handling different scopes such as entire branches, specific paths, or interactive changes [1][2]. Default Behavior and Mixed Mode When git reset is used without path specifications—specifically in the form git reset [] []—it acts on the current branch head [1][3]. In this form, if the argument is omitted, it defaults to --mixed [1][4]. The --mixed mode updates the index to match the specified commit (defaulting to HEAD) while leaving the working directory unchanged [1][5]. Pathspec Usage When you provide path specifications (pathspec) to git reset, the command operates differently; it does not move the branch head [2][6]. Instead, it updates the staged version of the specified files or directories to match the version from the given commit or tree (which also defaults to HEAD) [7][2]. This essentially unstages changes for the specified paths and is considered the opposite of git add [7][6]. Patch Mode The git reset --patch (or -p) option allows for interactive selection of changes [7][2]. In this mode, Git presents differences between the index and the specified commit or tree (defaulting to HEAD) [6][3]. You can interactively choose hunks to "reset," which reverses the chosen changes in the index [2][3]. Like the pathspec form, this is the opposite of git add --patch and does not affect the working tree or the current branch head [7][2]. Summary of Forms 1. Branch-level reset: git reset [--soft | --mixed | --hard |...] [] (defaults to --mixed HEAD) [1][2]. 2. Path-level reset: git reset [] [--] ... (updates index for specific paths) [2][6]. 3. Interactive reset: git reset --patch [] [--] [...] (selectively unstage changes) [2][3].

Citations:


Handle default mixed resets before the -- boundary.

git reset <target> defaults to --mixed and moves HEAD, but lines 313-320 require an explicit mode. The mode check also scans arguments after --, so git reset <target> -- --soft can produce a warning even though this is a pathspec form. Parse only options before --, treat no explicit mode as --mixed, and exclude pathspec and --patch forms. Update tests at lines 1420-1451 for the implicit mixed case and the -- boundary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/flare-git-core/src/classify.rs` around lines 313 - 320, Update the
reset classification logic around the subcommand mode check to parse only
arguments before the `--` boundary, treat an absent mode as the default
`--mixed`, and reject pathspec and `--patch` forms. Ensure target selection and
divergence handling use the pre-boundary arguments, and extend the related tests
covering implicit mixed resets and arguments after `--`.

@getappz
getappz merged commit 5dbc42b into master Aug 12, 2026
17 checks passed
@getappz
getappz deleted the task/98 branch August 12, 2026 09:02
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