Add gap fixes for temporary index plumbing, 3-way merge, and intent-t… - #1486
Add gap fixes for temporary index plumbing, 3-way merge, and intent-t…#1486czha168 wants to merge 1 commit into
Conversation
…o-add - Gap 1: Add Repository.set_index() to swap repo's index with a temporary Index object via git_repository_set_index(). This enables atomic operations on temp indices without modifying the working tree. - Gap 2: Add Repository.merge_diff() for 3-way merge support. Accepts theirs and ancestor trees and performs git_merge_trees() internally, returning an Index with merged content and optional conflicts. - Gap 3: Add Index.add_intent() for intent-to-add entries (null OID) like git add -N. Creates a valid entry then zeroes the OID bytes via FFI. - Add test_gap_fixes.py with 8 tests covering all three fixes. - Add docs/gap-fix-plan.md documenting the investigation and fixes. Assisted-by: Claude Sonnet 4.6
|
Below a review by Kimi K2.7 Coding Highspeed model. Overall assessment The PR currently needs substantial revision before it can be merged. Two of the three proposed additions (set_index, merge_diff) have serious correctness or redundancy problems, and the new docs/gap-fix-plan.md file should not be part of the upstream repository. Repository.set_index() — has an ownership/lifetime bug pygit2/repository.py:1614 The implementation simply passes index._index to git_repository_set_index(). In libgit2, git_repository_set_index() takes ownership of the index (it frees the repo’s old index and stores the new pointer without incrementing a reference count). The Python Index object also believes it owns that same pointer and will call git_index_free() from Index.del. This creates a double-free / use-after-free once the temporary Index is garbage collected. Example of the hazard in test/test_gap_fixes.py:78: original_index = testrepo.index
testrepo.set_index(temp_index)
...
testrepo.set_index(original_index)After the first set_index(temp_index), libgit2 frees the repository’s original index. The Python object original_index still holds a pointer to that freed index; passing it back to set_index() is UB. The tests happen to pass because the memory is still mapped, but this is not safe. To fix this, set_index() must either: • be implemented in the C extension where the Index object can be detached/invalidated after ownership transfer, or Also note: the plan document describes C extension changes in src/repository.c/src/repository.h, but the actual PR only adds a CFFI declaration in Repository.merge_diff() — redundant and lower-quality pygit2/repository.py:1641 This duplicates the existing Repository.merge_trees() method (pygit2/repository.py:956), which already performs a 3-way tree merge and returns an Index. merge_diff() is less flexible: ours is hard-coded to self.head.peel().tree, it only accepts Tree objects (not Oid/str), and it does not expose the MergeFlag options. Additional implementation issues: • The parameter is named flags but typed MergeFileFlag; the existing convention is flags: MergeFlag and file_flags: MergeFileFlag. Recommendation: remove merge_diff() entirely and use the existing merge_trees(ancestor, ours, theirs, favor=..., flags=..., file_flags=...). Index.add_intent() — works but needs polish pygit2/index.py:230 The two-step approach (add() the file, then zero the OID via the returned git_index_entry *) is pragmatic because git_index_add() rejects a null OID. It correctly produces intent-to-add entries. Requested changes: • Accept str | PathLike[str], not just str, to match Index.add(). docs/gap-fix-plan.md — should not be merged This is an internal planning document that references “Athena’s PR generation workflow”, athena/pr_generator/branch.py, etc. It does not belong in the upstream pygit2 repository. Remove it before merging. Tests — needs restructuring and fixes test/test_gap_fixes.py • The new tests should be split into the existing test/test_repository.py, test/test_index.py, and test/test_merge.py rather than adding a separate file named after the PR’s internal gaps. Suggested path forward
|
…o-add
Gap 1: Add Repository.set_index() to swap repo's index with a temporary Index object via git_repository_set_index(). This enables atomic operations on temp indices without modifying the working tree.
Gap 2: Add Repository.merge_diff() for 3-way merge support. Accepts theirs and ancestor trees and performs git_merge_trees() internally, returning an Index with merged content and optional conflicts.
Gap 3: Add Index.add_intent() for intent-to-add entries (null OID) like git add -N. Creates a valid entry then zeroes the OID bytes via FFI.
Add test_gap_fixes.py with 8 tests covering all three fixes.
Add docs/gap-fix-plan.md documenting the investigation and fixes.
Assisted-by: Claude Sonnet 4.6