Skip to content

gr commit --amend with a clean index silently does nothing and exits 0 #908

Description

@laynepenney

Summary

gr commit --amend -m "<msg>" on a repository with no staged changes does not amend anything, prints No changes to commit., and exits 0. The commit message is unchanged and HEAD does not move.

A message-only amend is the ordinary way to fix a commit message and is valid in git with an empty index. gr makes it unreachable, and reports success while doing so.

Reproduction

Built from dev at 6192e8c. Measured in an isolated single-repo gripspace.

Note for anyone who cannot reproduce: the released gr on PATH may be older than dev — mine was eight days behind when this was filed, so a released binary can predate the code described here. Build from dev before concluding the defect is absent.

$ gr commit --amend -m "MESSAGE ONLY AMEND"

Committing changes...

No changes to commit.
$ echo $?
0

HEAD unmoved, subject unchanged.

Controls

Both run in the same sandbox, against the same repo, immediately before and after the subject:

# Case Exit HEAD moved Meaning
B gr commit --amend -m with a staged change 0 yes gr's amend path works; the sandbox is valid
C git commit --amend -m with a clean index 0 yes the operation is valid; gr is refusing something git supports
gr commit --amend -m with a clean index 0 no the defect

Control B is what makes this a defect report rather than a broken-sandbox report: the identical command succeeds when the index is dirty.

Mechanism

src/cli/commands/commit.rs. The staged-changes precondition runs unconditionally, including when amend is set:

// Check if there are staged changes
if !has_staged_changes(&git_repo)? {
    skip_count += 1;
    json_skipped.push(repo.name.clone());
    continue;
}

match create_commit(&git_repo, message, amend) { ... }

A message-only amend has no staged changes by design, so the repo is classified as skipped, create_commit is never called, and --amend is silently discarded. The precondition is correct for a commit and wrong for an amend.

The exit code then follows from the reporting block: with every repo skipped, success_count == 0, the command prints No changes to commit. and the function returns Ok(()) unconditionally.

Two defects, not one

1. The gate does not account for --amend. An amend with an empty index is a legitimate operation.

2. The exit code does not reflect the outcome — this is the #886 class. The user asked for a commit, no commit was produced, and the process reports success. Note also that the two output channels disagree: in --json mode the payload carries

"success": false,
"committed": [],
"skipped": [ ... ]

while the process still exits 0. A script branching on $? reads success; the same run's own JSON says otherwise. Whichever way that is resolved, the two should not be able to disagree.

Suggested fix

  • Gate on !amend && !has_staged_changes(...), so an amend proceeds with a clean index and git decides.
  • Make the exit code a disjunction over per-repo outcomes rather than an unconditional Ok(()): if the user requested a commit and none was produced, that is a non-zero exit.
  • Have the JSON success field and the process exit code derive from the same value.

Note on the message

No changes to commit. is accurate for a plain gr commit and misleading for gr commit --amend, where the user is not trying to commit changes — they are trying to rewrite a message. If the gate stays for some case, the amend wording should say what was actually refused.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions