Skip to content

GitPython can stage changes for commit that git won't commit #1185

Open
@LinuxMercedes

Description

@LinuxMercedes

Steps to reproduce:

  1. Check out the active branch.
  2. Make an IndexFile object
  3. Stage changes by calling .add() on that object
  4. Write that object's state using .write()
  5. Make a commit using the git binary on the system (instead of using .commit() on the IndexFile object)

Here is a minimal working example:

from git.repo import Repo

r = Repo.init('repo')

# Create an initial commit and active branch
with open('repo/test.txt', 'w+') as fh:
    fh.write("initial text\n")

r.index.add('test.txt')
r.index.commit('initial commit')

# Make some changes to commit
with open('repo/test.txt', 'a+') as fh:
    fh.write("next commit text\n")

# Bug doesn't occur if we don't do this
r.active_branch.checkout()  ## creates TREE extension data in the index

# Note the reference to r.index
our_index = r.index
our_index.add('test.txt')  ## implicit .write() call removes TREE extension from the index
our_index.write()  ## writes cached, invalid TREE extension data to the index

print("Status: should show changes to commit")
print(r.git.status())

r.git.commit(m='direct git call')  ## confused by improperly invalidated TREE extension data

print("Status: should show nothing to commit but doesn't")
print(r.git.status())

### At this point, there are changes staged for commit that will *not* be committed by `git commit` ###

# Direct use of r.index works, though:
# Both the .add and .write calls are necessary here
r.index.add('test.txt')  ## implicit .write() call removes invalid TREE extension data from the index
r.index.write()  ## re-creating the IndexFile object forgets the cached extension data, so .write() does not write it

r.git.commit(m='direct git call 2')  ## no confusing TREE extension data; everything's fine

print("Status: actually shows nothing to commit")
print(r.git.status())

I've reproduced this on both Python 3.9 and 3.8, git 2.30, gitpython 3.0.8 and 3.1.13 across several machines, both Windows and Linux.

It seems the only difference between the calls to r.git.commit is that r.index.add and r.index.write are calls made to two different IndexFile objects. Using the same object for both calls results in the changes not being committed.

Added twists:

  • if I do our_index.write_tree() and use git commit-tree, the changes staged get committed.
  • if I use our_index.commit(), the changes staged get committed.
  • if I remove the call to r.active_branch.checkout(), the changes staged get committed.

This is truly bewildering to me. I'm happy to try to debug more, but any sort of speculation on where to start would be helpful!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions