Open
Description
Steps to reproduce:
- Check out the active branch.
- Make an
IndexFile
object - Stage changes by calling
.add()
on that object - Write that object's state using
.write()
- Make a commit using the
git
binary on the system (instead of using.commit()
on theIndexFile
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 usegit 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!