Summary
When SwiftPorts' SwiftGit is built against GitKit (libgit2 1.9.4), its
git stash apply / pop / branch operations no longer restore the stashed
change to the working tree. The exact same SwiftGit code passes against the
odrobnik/libgit2 fork SwiftPorts used previously. This blocked SwiftPorts'
adoption of GitKit and was decoupled in Cocoanetics/SwiftPorts#59 (reverted to
the fork to keep stash working).
Repro (SwiftGit GitClientStashTests)
git init, commit file.txt = v1.
- Write
file.txt = v2 (working-tree change).
git_stash_save → working tree correctly reverts to v1. ✅
git_stash_apply(index: 0) → working tree should become v2, but stays v1. ❌
save / list / drop / show / clear all pass; only the operations that
go through git_stash_apply (apply / pop / branch) fail. So git_stash_apply
computes the merge but never writes the working directory.
What I ruled out
- Explicitly setting
apply_opts.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE before git_stash_apply is a no-op — the tests still
fail with it set.
- In
src/libgit2/stash.c, normalize_apply_options() only ORs in
GIT_CHECKOUT_NO_REFRESH; it does not default the strategy to SAFE
(unlike what some older versions did). The apply path then
git_checkout_index(modified_index, …) with that strategy.
Hypothesis (needs confirmation)
After the merge yields modified_index, GIT_CHECKOUT_SAFE skips writing the
file because the working-tree copy looks "modified" relative to the post-merge
index baseline — so nothing is written. The fix is likely an explicit checkout
baseline (HEAD tree) for the stash-apply checkout, or this is a genuine
libgit2 1.9.x change worth an upstream report. GIT_CHECKOUT_FORCE makes the
test pass but can clobber unrelated uncommitted changes, so it's not a safe fix.
Impact / next step
Re-adopting GitKit in SwiftPorts needs either a SwiftGit stashApply fix that
works under libgit2 1.9.4, or confirmation + an upstream libgit2 fix. Until
then SwiftPorts stays on the fork.
Summary
When SwiftPorts'
SwiftGitis built against GitKit (libgit2 1.9.4), itsgit stash apply/pop/branchoperations no longer restore the stashedchange to the working tree. The exact same SwiftGit code passes against the
odrobnik/libgit2fork SwiftPorts used previously. This blocked SwiftPorts'adoption of GitKit and was decoupled in Cocoanetics/SwiftPorts#59 (reverted to
the fork to keep stash working).
Repro (SwiftGit
GitClientStashTests)git init, commitfile.txt=v1.file.txt=v2(working-tree change).git_stash_save→ working tree correctly reverts tov1. ✅git_stash_apply(index: 0)→ working tree should becomev2, but staysv1. ❌save/list/drop/show/clearall pass; only the operations thatgo through
git_stash_apply(apply / pop / branch) fail. Sogit_stash_applycomputes the merge but never writes the working directory.
What I ruled out
apply_opts.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFEbeforegit_stash_applyis a no-op — the tests stillfail with it set.
src/libgit2/stash.c,normalize_apply_options()only ORs inGIT_CHECKOUT_NO_REFRESH; it does not default the strategy toSAFE(unlike what some older versions did). The apply path then
git_checkout_index(modified_index, …)with that strategy.Hypothesis (needs confirmation)
After the merge yields
modified_index,GIT_CHECKOUT_SAFEskips writing thefile because the working-tree copy looks "modified" relative to the post-merge
index baseline — so nothing is written. The fix is likely an explicit checkout
baseline (HEAD tree) for the stash-apply checkout, or this is a genuine
libgit2 1.9.x change worth an upstream report.
GIT_CHECKOUT_FORCEmakes thetest pass but can clobber unrelated uncommitted changes, so it's not a safe fix.
Impact / next step
Re-adopting GitKit in SwiftPorts needs either a SwiftGit
stashApplyfix thatworks under libgit2 1.9.4, or confirmation + an upstream libgit2 fix. Until
then SwiftPorts stays on the fork.