Skip to content

Commit df58937

Browse files
Copilotpascalberger
andcommitted
Fix Git LFS sparse checkout issue - use git ls-files -t -z and filter skip-worktree files
Co-authored-by: pascalberger <2190718+pascalberger@users.noreply.github.com>
1 parent 63576c7 commit df58937

File tree

2 files changed

+120
-1
lines changed

2 files changed

+120
-1
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
namespace Cake.Issues.GitRepository.Tests;
2+
3+
public sealed class GitRepositoryIssuesProviderTests
4+
{
5+
public sealed class TheSparseCheckoutFiltering
6+
{
7+
[Fact]
8+
public void Should_Filter_Out_Skip_Worktree_Files()
9+
{
10+
// Given - Simulate git ls-files -t -z output with sparse checkout
11+
var gitOutput = new[]
12+
{
13+
"H file1.txt",
14+
"S file2.txt", // Skip-worktree file (should be filtered out)
15+
"H subdir/file3.txt",
16+
"S subdir/file4.txt", // Skip-worktree file (should be filtered out)
17+
""
18+
};
19+
20+
// When - Apply the same filtering logic as GetAllFilesFromRepository
21+
var result = gitOutput
22+
.Where(x => !string.IsNullOrEmpty(x))
23+
.Where(x => !x.StartsWith("S ")) // Exclude skip-worktree files (sparse checkout)
24+
.Select(x => x.Length > 2 ? x.Substring(2) : x) // Remove status prefix (e.g., "H ")
25+
.ToList();
26+
27+
// Then - Only non-skip-worktree files should remain
28+
result.ShouldNotBeNull();
29+
result.Count.ShouldBe(2);
30+
result.ShouldContain("file1.txt");
31+
result.ShouldContain("subdir/file3.txt");
32+
result.ShouldNotContain("file2.txt");
33+
result.ShouldNotContain("subdir/file4.txt");
34+
}
35+
36+
[Fact]
37+
public void Should_Handle_Empty_Output()
38+
{
39+
// Given - Empty git output
40+
var gitOutput = new[] { "" };
41+
42+
// When - Apply the same filtering logic as GetAllFilesFromRepository
43+
var result = gitOutput
44+
.Where(x => !string.IsNullOrEmpty(x))
45+
.Where(x => !x.StartsWith("S "))
46+
.Select(x => x.Length > 2 ? x.Substring(2) : x)
47+
.ToList();
48+
49+
// Then - Result should be empty
50+
result.ShouldNotBeNull();
51+
result.Count.ShouldBe(0);
52+
}
53+
54+
[Fact]
55+
public void Should_Handle_Various_Git_Status_Codes()
56+
{
57+
// Given - Various git status codes
58+
var gitOutput = new[]
59+
{
60+
"H cached_file.txt", // Cached (should be included)
61+
"S skip_worktree.txt", // Skip-worktree (should be excluded)
62+
"M modified_file.txt", // Modified (should be included)
63+
"R renamed_file.txt", // Renamed (should be included)
64+
"C copied_file.txt", // Copied (should be included)
65+
"K to_be_killed.txt", // To be killed (should be included)
66+
""
67+
};
68+
69+
// When - Apply the filtering logic
70+
var result = gitOutput
71+
.Where(x => !string.IsNullOrEmpty(x))
72+
.Where(x => !x.StartsWith("S "))
73+
.Select(x => x.Length > 2 ? x.Substring(2) : x)
74+
.ToList();
75+
76+
// Then - Only skip-worktree files should be filtered out
77+
result.ShouldNotBeNull();
78+
result.Count.ShouldBe(5);
79+
result.ShouldContain("cached_file.txt");
80+
result.ShouldContain("modified_file.txt");
81+
result.ShouldContain("renamed_file.txt");
82+
result.ShouldContain("copied_file.txt");
83+
result.ShouldContain("to_be_killed.txt");
84+
result.ShouldNotContain("skip_worktree.txt");
85+
}
86+
87+
[Fact]
88+
public void Should_Handle_Edge_Cases_In_Status_Parsing()
89+
{
90+
// Given - Edge cases that might occur
91+
var gitOutput = new[]
92+
{
93+
"H normal_file.txt",
94+
"S", // Just "S" without filename (malformed)
95+
"Sfile_without_space.txt", // "S" without space (should not be filtered)
96+
"H ", // Just status with space but no filename
97+
"X unknown_status.txt", // Unknown status (should be included)
98+
""
99+
};
100+
101+
// When - Apply the filtering logic
102+
var result = gitOutput
103+
.Where(x => !string.IsNullOrEmpty(x))
104+
.Where(x => !x.StartsWith("S "))
105+
.Select(x => x.Length > 2 ? x.Substring(2) : x)
106+
.ToList();
107+
108+
// Then
109+
result.ShouldNotBeNull();
110+
result.Count.ShouldBe(4);
111+
result.ShouldContain("normal_file.txt");
112+
result.ShouldContain("Sfile_without_space.txt"); // This wasn't filtered because no space after S
113+
result.ShouldContain(""); // Empty filename from "H "
114+
result.ShouldContain("unknown_status.txt");
115+
}
116+
}
117+
}

src/Cake.Issues.GitRepository/GitRepositoryIssuesProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ private List<string> GetAllFilesFromRepository()
182182
};
183183

184184
settings.Arguments.Clear();
185-
settings.Arguments.Add("ls-files -z");
185+
settings.Arguments.Add("ls-files -t -z");
186186
var output =
187187
this.runner.RunCommand(settings)
188188
?? throw new Exception("Error reading files from repository");
@@ -192,6 +192,8 @@ private List<string> GetAllFilesFromRepository()
192192
output)
193193
.Split('\0')
194194
.Where(x => !string.IsNullOrEmpty(x))
195+
.Where(x => !x.StartsWith("S ")) // Exclude skip-worktree files (sparse checkout)
196+
.Select(x => x.Length > 2 ? x.Substring(2) : x) // Remove status prefix (e.g., "H ")
195197
.ToList();
196198
this.Log.Verbose("Found {0} file(s)", result.Count);
197199

0 commit comments

Comments
 (0)