Skip to content

Commit 214a7f1

Browse files
author
Vincent Potucek
committed
[fix] NPE due to workingTreeIterator being null for git ignored files. #911
- #911 Signed-off-by: Vincent Potucek <[email protected]>
1 parent bf74d6f commit 214a7f1

File tree

2 files changed

+73
-12
lines changed

2 files changed

+73
-12
lines changed

lib-extra/src/main/java/com/diffplug/spotless/extra/GitRatchet.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public boolean isClean(Project project, ObjectId treeSha, String relativePathUni
9797
WorkingTreeIterator workingTreeIterator = treeWalk.getTree(WORKDIR, WorkingTreeIterator.class);
9898

9999
boolean hasTree = treeIterator != null;
100-
boolean hasDirCache = dirCacheIterator != null;
100+
boolean hasDirCache = dirCacheIterator != null && workingTreeIterator != null;
101101

102102
if (!hasTree) {
103103
// it's not in the tree, so it was added

lib-extra/src/test/java/com/diffplug/spotless/extra/GitRachetMergeBaseTest.java

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,54 @@ void test() throws IllegalStateException, GitAPIException, IOException {
5757
}
5858
}
5959

60+
@Test
61+
void testGitIgnoredDirectory_ShouldNotThrowNPE() throws IllegalStateException, GitAPIException, IOException {
62+
try (Git git = initRepo()) {
63+
// Create a directory with files and commit them
64+
setFile("useless/Wow.java").toContent("class Wow {}");
65+
setFile("useless/Another.java").toContent("class Another {}");
66+
addAndCommit(git, "Add useless package");
67+
68+
// Now ignore the entire directory and commit
69+
setFile(".gitignore").toContent("useless/");
70+
addAndCommit(git, "Ignore useless directory");
71+
72+
// The files in the useless directory are now committed but gitignored
73+
// This should not throw NPE
74+
ratchetFrom("main").onlyDirty("useless/Wow.java", "useless/Another.java");
75+
}
76+
}
77+
78+
@Test
79+
void testNewUntrackedFile_ShouldBeDirty() throws IllegalStateException, GitAPIException, IOException {
80+
try (Git git = initRepo()) {
81+
// Create and commit initial file
82+
setFile("committed.java").toContent("class Committed {}");
83+
addAndCommit(git, "Initial commit");
84+
85+
// Create a new file that's not tracked at all (not in gitignore either)
86+
setFile("new_untracked.java").toContent("class NewUntracked {}");
87+
88+
// The new untracked file should be considered dirty
89+
ratchetFrom("main").onlyDirty("new_untracked.java");
90+
}
91+
}
92+
93+
@Test
94+
void testModifiedTrackedFile_ShouldBeDirty() throws IllegalStateException, GitAPIException, IOException {
95+
try (Git git = initRepo()) {
96+
// Create and commit initial file
97+
setFile("Main.java").toContent("class Main { void old() {} }");
98+
addAndCommit(git, "Initial commit");
99+
100+
// Modify the file
101+
setFile("Main.java").toContent("class Main { void newMethod() {} }");
102+
103+
// The modified file should be considered dirty
104+
ratchetFrom("main").onlyDirty("Main.java");
105+
}
106+
}
107+
60108
static class GitRatchetSimple extends GitRatchet<File> {
61109
@Override
62110
protected File getDir(File project) {
@@ -81,14 +129,20 @@ class Asserter {
81129
Asserter(String... ratchetFrom) {
82130
this.ratchetFroms = ratchetFrom;
83131
this.shas = Arrays.stream(ratchetFrom)
84-
.map(from -> ratchet.rootTreeShaOf(rootFolder(), from))
85-
.toArray(ObjectId[]::new);
132+
.map(from -> ratchet.rootTreeShaOf(rootFolder(), from))
133+
.toArray(ObjectId[]::new);
86134
}
87135

88136
private void assertClean(int i, String filename, boolean expected) throws IOException {
89-
boolean actual = ratchet.isClean(rootFolder(), shas[i], newFile(filename));
137+
File file = new File(rootFolder(), filename);
138+
if (!file.exists()) {
139+
throw new AssertionError("File " + filename + " does not exist");
140+
}
141+
142+
boolean actual = ratchet.isClean(rootFolder(), shas[i], file);
90143
if (actual != expected) {
91-
throw new AssertionError("Expected " + filename + " to be " + (expected ? "clean" : "dirty") + " relative to " + ratchetFroms[i]);
144+
throw new AssertionError("Expected " + filename + " to be " + (expected ? "clean" : "dirty") +
145+
" relative to " + ratchetFroms[i] + " but was " + (actual ? "clean" : "dirty"));
92146
}
93147
}
94148

@@ -98,24 +152,31 @@ public void allClean() throws IOException {
98152

99153
public void allDirty() throws IOException {
100154
String[] filenames = Arrays.stream(rootFolder().listFiles())
101-
.filter(File::isFile)
102-
.map(File::getName)
103-
.toArray(String[]::new);
155+
.filter(File::isFile)
156+
.map(File::getName)
157+
.toArray(String[]::new);
104158
onlyDirty(filenames);
105159
}
106160

107161
public void onlyDirty(String... filenames) throws IOException {
108162
List<String> dirtyFiles = Arrays.asList(filenames);
109-
for (File file : rootFolder().listFiles()) {
110-
if (!file.isFile()) {
163+
for (File file : getAllFilesRecursive(rootFolder())) {
164+
if (file.isDirectory()) {
111165
continue;
112166
}
113-
boolean expectedClean = !dirtyFiles.contains(file.getName());
167+
String relativePath = rootFolder().toPath().relativize(file.toPath()).toString();
168+
boolean expectedClean = !dirtyFiles.contains(relativePath);
114169
for (int i = 0; i < shas.length; i++) {
115-
assertClean(i, file.getName(), expectedClean);
170+
assertClean(i, relativePath, expectedClean);
116171
}
117172
}
118173
}
174+
175+
private List<File> getAllFilesRecursive(File dir) {
176+
return Arrays.stream(dir.listFiles())
177+
.flatMap(file -> file.isDirectory() ? getAllFilesRecursive(file).stream() : Arrays.stream(new File[]{file}))
178+
.toList();
179+
}
119180
}
120181

121182
private Git initRepo() throws IllegalStateException, GitAPIException, IOException {

0 commit comments

Comments
 (0)