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.
Summary
gr commit --amend -m "<msg>"on a repository with no staged changes does not amend anything, printsNo changes to commit., and exits 0. The commit message is unchanged andHEADdoes not move.A message-only amend is the ordinary way to fix a commit message and is valid in git with an empty index.
grmakes it unreachable, and reports success while doing so.Reproduction
Built from
devat6192e8c. Measured in an isolated single-repo gripspace.Note for anyone who cannot reproduce: the released
gronPATHmay be older thandev— mine was eight days behind when this was filed, so a released binary can predate the code described here. Build fromdevbefore concluding the defect is absent.HEADunmoved, subject unchanged.Controls
Both run in the same sandbox, against the same repo, immediately before and after the subject:
gr commit --amend -mwith a staged changegit commit --amend -mwith a clean indexgr commit --amend -mwith a clean indexControl 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 whenamendis set:A message-only amend has no staged changes by design, so the repo is classified as skipped,
create_commitis never called, and--amendis 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 printsNo changes to commit.and the function returnsOk(())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
--jsonmode the payload carrieswhile 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
!amend && !has_staged_changes(...), so an amend proceeds with a clean index andgitdecides.Ok(()): if the user requested a commit and none was produced, that is a non-zero exit.successfield and the process exit code derive from the same value.Note on the message
No changes to commit.is accurate for a plaingr commitand misleading forgr 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.