fix(git): refuse to commit when nothing is staged - #4761
Open
ConnorMoss02 wants to merge 3 commits into
Open
Conversation
repo.index.commit() writes a tree from the index unconditionally, so git_commit returned "Changes committed successfully with hash ..." even when the index matched HEAD. A caller that edited files and skipped git_add got a hash back, reported the work as committed, and left the working tree dirty with an empty commit on top. The message could not be false, so nothing downstream could tell a real commit from an empty one. Mirrors git commit, which refuses this without --allow-empty: raises when the index matches HEAD, while still allowing the first commit on an unborn branch and an empty merge commit when MERGE_HEAD is present.
ConnorMoss02
force-pushed
the
fix/git-commit-empty
branch
from
September 5, 2026 23:55
cc46adb to
537979e
Compare
The test provoked a merge conflict and asserted only that some GitCommandError was raised, so a merge that failed for an unrelated reason satisfied it and left no MERGE_HEAD. It passed locally and failed on CI, where the fixture's missing user identity makes `git merge` refuse to run at all. Uses --no-commit --no-ff instead, which sets MERGE_HEAD without depending on how a git version reports conflicts, sets an explicit identity because `git merge` shells out to git, and asserts MERGE_HEAD exists so a failure says what actually went wrong.
They differed only in starting state, and the repo's python tests use no parametrize, so one test walking clean tree -> untracked -> unstaged edit covers the same ground in a third of the lines.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4762
git_commitreturnsChanges committed successfully with hash <sha>even when nothing is staged.repo.index.commit()writes a tree from the index unconditionally — GitPython has no--allow-emptygate — and the return string cannot be false:An agent edits files, calls
git_commitwithoutgit_add(or after agit_addthat matched nothing), and gets a hash. It reports the work as committed. The working tree is still dirty,HEADstill holds the old content, and the repo now carries an empty commit. Nothing in the response distinguishes that from a real commit.git commititself refuses this: "no changes added to commit".Fix
Raise when the index matches HEAD, matching git's default. Two cases git does allow are preserved:
MERGE_HEADexists — a conflict resolved back to HEAD's content leaves the index matching HEAD, and that commit is legitimateNo new parameter: git requires
--allow-emptyexplicitly, so refusing by default matches it.Tests
Six, covering both directions. Three assert the refusal (unstaged edit, clean tree, untracked-only) and fail with the source change reverted. Three assert it does not over-refuse (staged deletion, unborn branch, empty merge commit) and pass either way.
53 passed in
src/git.