What's wrong
GitRepository.LocalPath is a public init property, and nothing requires it to be the top of the working tree. IsClonedAsync uses rev-parse --is-inside-work-tree, which also answers true for a subdirectory, so a repository opened at a subdirectory looks valid.
Every verb then runs git -C <LocalPath> --literal-pathspecs … (GitIntegration/GitRepository.cs, Builders/GitCommandBuilder.cs). The paths are mismatched:
Status(), Diff() and Patch() report paths relative to the repository root, which is how RelativeFilePath is documented.
- Git reads pathspecs relative to the
-C directory.
git apply run from a subdirectory silently skips any patched path outside that directory, and still exits 0.
Failure scenario (reproduced with git 2.43)
The repo contains root.txt and sub/s.txt. Open it with new GitRepository { LocalPath = <repo>/sub }:
- Apply:
Apply(patch).ToIndex() with a patch touching root.txt runs git -C sub apply --cached <tmp>. It exits 0, and git diff --cached is empty. The caller is told the hunk was staged.
- Unstage: stage
sub/s.txt, then call Unstage("sub/s.txt"), the path exactly as Status() reported it. That runs git -C sub --literal-pathspecs reset -q -- sub/s.txt, which exits 0, and the file is still staged.
- Log/Diff/Patch/RevList:
.ForPath("sub/s.txt") silently returns empty results.
- Add:
Add().ForPath("sub/s.txt") at least fails loudly (pathspec did not match, exit 128).
Suggested fix
Either:
- (a) Resolve paths from the root whatever
-C is: use :(top,literal)<path> pathspec magic in place of the global --literal-pathspecs, and run apply from rev-parse --show-toplevel; or
- (b) Make the requirement explicit: document that
LocalPath must be the working-tree root, and enforce it. For example, verbs that take paths would probe rev-parse --show-prefix and throw when it is non-empty.
Acceptance criteria
- An integration test opens a repository at a subdirectory. It shows that
Unstage, Apply().ToIndex() and the ForPath filters either act on root-relative paths correctly or fail loudly.
- None of these operations reports success while making no change.
What's wrong
GitRepository.LocalPathis a publicinitproperty, and nothing requires it to be the top of the working tree.IsClonedAsyncusesrev-parse --is-inside-work-tree, which also answers true for a subdirectory, so a repository opened at a subdirectory looks valid.Every verb then runs
git -C <LocalPath> --literal-pathspecs …(GitIntegration/GitRepository.cs,Builders/GitCommandBuilder.cs). The paths are mismatched:Status(),Diff()andPatch()report paths relative to the repository root, which is howRelativeFilePathis documented.-Cdirectory.git applyrun from a subdirectory silently skips any patched path outside that directory, and still exits 0.Failure scenario (reproduced with git 2.43)
The repo contains
root.txtandsub/s.txt. Open it withnew GitRepository { LocalPath = <repo>/sub }:Apply(patch).ToIndex()with a patch touchingroot.txtrunsgit -C sub apply --cached <tmp>. It exits 0, andgit diff --cachedis empty. The caller is told the hunk was staged.sub/s.txt, then callUnstage("sub/s.txt"), the path exactly asStatus()reported it. That runsgit -C sub --literal-pathspecs reset -q -- sub/s.txt, which exits 0, and the file is still staged..ForPath("sub/s.txt")silently returns empty results.Add().ForPath("sub/s.txt")at least fails loudly (pathspec did not match, exit 128).Suggested fix
Either:
-Cis: use:(top,literal)<path>pathspec magic in place of the global--literal-pathspecs, and runapplyfromrev-parse --show-toplevel; orLocalPathmust be the working-tree root, and enforce it. For example, verbs that take paths would proberev-parse --show-prefixand throw when it is non-empty.Acceptance criteria
Unstage,Apply().ToIndex()and theForPathfilters either act on root-relative paths correctly or fail loudly.