Reproduction steps
git refuses a pathspec whose non-final component is a symlink; git_index_add_bypath accepts it.
git init r && cd r
mkdir realdir && echo hi > realdir/file.txt
ln -s realdir linkdir
git add linkdir/file.txt
# fatal: pathspec 'linkdir/file.txt' is beyond a symbolic link
git_repository_open(&repo, "r");
git_repository_index(&index, repo);
int err = git_index_add_bypath(index, "linkdir/file.txt");
libgit2 1.9.7
git_index_add_bypath("linkdir/file.txt") -> 0 (accepted)
index entry present: linkdir/file.txt
Committing that index produces a tree git cannot reconcile:
$ git ls-tree -r HEAD
100644 blob 45b983be... linkdir/file.txt
$ git fsck # clean
$ git status --porcelain
D linkdir/file.txt
?? linkdir
The path is in the commit, fsck is happy, and git reports it deleted, because git will not walk through the symlink to find it. A checkout of that commit has to create linkdir as a directory, so the symlink the repository also tracks and the committed path cannot both exist.
Expected behavior
git_index_add_bypath rejects a path whose non-final component is a symlink, as git add does.
Actual behavior
It is accepted and an index entry is created at the literal path.
Version of libgit2 (release number or SHA1)
1.9.7
Operating system(s) tested
macOS 26.6, arm64. Reproduced with a C program linked against libgit2 directly; originally found through pygit2 in libgit2/pygit2#1375, where @calestyo asked for it to be raised here.
One related asymmetry from that thread, in case it bears on where the check belongs: git_index_add_all with the same path adds nothing and returns success, so the two entry points already disagree with each other as well as with git.
I have not checked whether this is deliberate — docs/differences-from-git.md does not mention it, but "libgit2 is not a pathspec parser" is a reasonable position and would make this a documentation note instead.
Filed by Claude Opus 5 (Claude Code) running under my account; the reproduction above was compiled and run rather than reasoned about.
Reproduction steps
gitrefuses a pathspec whose non-final component is a symlink;git_index_add_bypathaccepts it.Committing that index produces a tree
gitcannot reconcile:The path is in the commit,
fsckis happy, andgitreports it deleted, becausegitwill not walk through the symlink to find it. A checkout of that commit has to createlinkdiras a directory, so the symlink the repository also tracks and the committed path cannot both exist.Expected behavior
git_index_add_bypathrejects a path whose non-final component is a symlink, asgit adddoes.Actual behavior
It is accepted and an index entry is created at the literal path.
Version of libgit2 (release number or SHA1)
1.9.7
Operating system(s) tested
macOS 26.6, arm64. Reproduced with a C program linked against libgit2 directly; originally found through pygit2 in libgit2/pygit2#1375, where @calestyo asked for it to be raised here.
One related asymmetry from that thread, in case it bears on where the check belongs:
git_index_add_allwith the same path adds nothing and returns success, so the two entry points already disagree with each other as well as withgit.I have not checked whether this is deliberate —
docs/differences-from-git.mddoes not mention it, but "libgit2 is not a pathspec parser" is a reasonable position and would make this a documentation note instead.Filed by Claude Opus 5 (Claude Code) running under my account; the reproduction above was compiled and run rather than reasoned about.