This is an automated email from the ASF dual-hosted git repository.
claude pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/creadur-rat.git
The following commit(s) were added to refs/heads/master by this push:
new 34eee888 RAT-473: take into account global gitignore (#433)
34eee888 is described below
commit 34eee8886adb6b5db494abe56e25dca4a5206aac
Author: Arnout Engelen <[email protected]>
AuthorDate: Sun Jun 1 10:39:44 2025 +0200
RAT-473: take into account global gitignore (#433)
Take into account exclusions from the global gitignore file if it is
present in the default location or specified by the XDG_CONFIG_HOME environment
variable.
see https://git-scm.com/docs/gitignore for details.
---
.../rat/config/exclusion/StandardCollection.java | 6 +-
.../AbstractFileProcessorBuilder.java | 6 +-
.../exclusion/fileProcessors/GitIgnoreBuilder.java | 70 ++++++++++++++++++++++
.../fileProcessors/AbstractIgnoreBuilderTest.java | 7 ++-
.../fileProcessors/GitIgnoreBuilderTest.java | 63 +++++++++++++++----
.../GitIgnoreBuilderTest/global-gitignore | 3 +
.../resources/GitIgnoreBuilderTest/src/.gitignore | 3 +
.../src/local-should-precede-global.md | 5 ++
.../src/local-should-precede-global.xml | 5 ++
src/changes/changes.xml | 3 +
10 files changed, 155 insertions(+), 16 deletions(-)
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
index 62c17972..e2387eb8 100644
---
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
@@ -89,9 +89,11 @@ public enum StandardCollection {
"**/.project", "**/.settings/**",
"**/.externalToolBuilders"),
null, null),
/**
- * The files and directories created by GIT source code control to support
GIT, also processes files listed in '.gitignore'.
+ * The files and directories created by GIT source code control to support
GIT, also processes files listed in '.gitignore'
+ * and (unless RAT_NO_GIT_GLOBAL_IGNORE is specified) the global gitignore.
*/
- GIT("The files and directories created by GIT source code control to
support GIT, also processes files listed in '.gitignore'.",
+ GIT("The files and directories created by GIT source code control to
support GIT, also processes files listed in '.gitignore' " +
+ "and (unless RAT_NO_GIT_GLOBAL_IGNORE is specified) the global
gitignore.",
Arrays.asList("**/.git/**", "**/.gitignore"),
null,
new GitIgnoreBuilder()
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/AbstractFileProcessorBuilder.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/AbstractFileProcessorBuilder.java
index 4fc9f421..4f526261 100644
---
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/AbstractFileProcessorBuilder.java
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/AbstractFileProcessorBuilder.java
@@ -203,10 +203,14 @@ public abstract class AbstractFileProcessorBuilder {
return result == null ? new File[0] : result;
}
+ protected LevelBuilder getLevelBuilder(final int level) {
+ return levelBuilders.computeIfAbsent(level, k -> new LevelBuilder());
+ }
+
/**
* Manages the merging of {@link MatcherSet}s for the specified level.
*/
- private static final class LevelBuilder {
+ protected static final class LevelBuilder {
/**
* The list of MatcherSets that this builder produced.
*/
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilder.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilder.java
index cf4e9fb9..d43a2ccd 100644
---
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilder.java
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilder.java
@@ -19,7 +19,12 @@
package org.apache.rat.config.exclusion.fileProcessors;
import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Consumer;
import org.apache.rat.config.exclusion.ExclusionUtils;
@@ -53,6 +58,41 @@ public class GitIgnoreBuilder extends
AbstractFileProcessorBuilder {
super(IGNORE_FILE, COMMENT_PREFIX, true);
}
+ private MatcherSet processGlobalIgnore(final Consumer<MatcherSet>
matcherSetConsumer, final DocumentName root, final DocumentName
globalGitIgnore) {
+ final MatcherSet.Builder matcherSetBuilder = new MatcherSet.Builder();
+ final List<String> iterable = new ArrayList<>();
+ ExclusionUtils.asIterator(globalGitIgnore.asFile(), commentFilter)
+ .map(entry -> modifyEntry(matcherSetConsumer, globalGitIgnore,
entry).orElse(null))
+ .filter(Objects::nonNull)
+ .map(entry -> ExclusionUtils.qualifyPattern(root, entry))
+ .forEachRemaining(iterable::add);
+
+ Set<String> included = new HashSet<>();
+ Set<String> excluded = new HashSet<>();
+ MatcherSet.Builder.segregateList(excluded, included, iterable);
+ DocumentName displayName = DocumentName.builder(root).setName("global
gitignore").build();
+ matcherSetBuilder.addExcluded(displayName, excluded);
+ matcherSetBuilder.addIncluded(displayName, included);
+ return matcherSetBuilder.build();
+ }
+
+ @Override
+ protected MatcherSet process(final Consumer<MatcherSet>
matcherSetConsumer, final DocumentName root, final DocumentName documentName) {
+ if (root.equals(documentName.getBaseDocumentName())) {
+ Optional<File> globalGitIgnore = globalGitIgnore();
+ List<MatcherSet> matcherSets = new ArrayList<MatcherSet>();
+ matcherSets.add(super.process(matcherSetConsumer, root,
documentName));
+ if (globalGitIgnore.isPresent()) {
+ LevelBuilder levelBuilder = getLevelBuilder(Integer.MAX_VALUE);
+ DocumentName ignore =
DocumentName.builder(globalGitIgnore.get()).build();
+ matcherSets.add(processGlobalIgnore(levelBuilder::add, root,
ignore));
+ }
+ return MatcherSet.merge(matcherSets);
+ } else {
+ return super.process(matcherSetConsumer, root, documentName);
+ }
+ }
+
/**
* Convert the string entry.
* If the string ends with a slash an {@link DocumentNameMatcher#and} is
constructed from a directory check and the file
@@ -102,4 +142,34 @@ public class GitIgnoreBuilder extends
AbstractFileProcessorBuilder {
}
return Optional.of(prefix ? NEGATION_PREFIX + pattern : pattern);
}
+
+ /**
+ * The global gitignore file to process, based on the
+ * RAT_NO_GIT_GLOBAL_IGNORE, XDG_CONFIG_HOME, and HOME environment
+ * variables.
+ */
+ protected Optional<File> globalGitIgnore() {
+ if (System.getenv("RAT_NO_GIT_GLOBAL_IGNORE") != null) {
+ return Optional.empty();
+ }
+
+ String xdgConfigHome = System.getenv("XDG_CONFIG_HOME");
+ String filename;
+ if (xdgConfigHome != null && !xdgConfigHome.isEmpty()) {
+ filename = xdgConfigHome + File.separator + "git" + File.separator
+ "ignore";
+ } else {
+ String home = System.getenv("HOME");
+ if (home == null) {
+ home = "";
+ }
+ filename = home + File.separator + ".config" + File.separator +
"git" + File.separator + "ignore";
+ }
+ File file = new File(filename);
+ if (file.exists()) {
+ return Optional.of(file);
+ } else {
+ return Optional.empty();
+ }
+ }
+
}
diff --git
a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/AbstractIgnoreBuilderTest.java
b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/AbstractIgnoreBuilderTest.java
index d813414c..1a4de5ec 100644
---
a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/AbstractIgnoreBuilderTest.java
+++
b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/AbstractIgnoreBuilderTest.java
@@ -95,13 +95,14 @@ public class AbstractIgnoreBuilderTest {
}
/**
- * Asserts the correctness of the excluder. An excluder returns false if
the document name is matched.
+ * Asserts the correctness of the matcher.
* @param matcherSets the list of matchers to create the
DocumentNameMatcher from.
* @param baseDir the base directory for the excluder test.
- * @param matching the matching strings.
- * @param notMatching the non-matching strings.
+ * @param matching the matching strings (i.e. that should be ignored)
+ * @param notMatching the non-matching strings (i.e. that should be
checked)
*/
protected void assertCorrect(List<MatcherSet> matcherSets, DocumentName
baseDir, Iterable<String> matching, Iterable<String> notMatching) {
+ // An excluder returns false if the document name is matched.
DocumentNameMatcher excluder =
MatcherSet.merge(matcherSets).createMatcher();
for (String name : matching) {
DocumentName docName =
baseDir.resolve(SelectorUtils.extractPattern(name,
baseDir.getDirectorySeparator()));
diff --git
a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilderTest.java
b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilderTest.java
index 60f73442..6a3f9b6d 100644
---
a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilderTest.java
+++
b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/fileProcessors/GitIgnoreBuilderTest.java
@@ -56,7 +56,12 @@ public class GitIgnoreBuilderTest extends
AbstractIgnoreBuilderTest {
writeFile(".gitignore", Arrays.asList(lines));
- assertCorrect(new GitIgnoreBuilder(), matches, notMatches);
+ assertCorrect(new GitIgnoreBuilder() {
+ @Override
+ protected Optional<File> globalGitIgnore() {
+ return Optional.empty();
+ }
+ }, matches, notMatches);
} finally {
System.getProperties().remove("FSInfo");
}
@@ -106,7 +111,7 @@ public class GitIgnoreBuilderTest extends
AbstractIgnoreBuilderTest {
DocumentName name = documentName.resolve(test);
assertThat(matcher.matches(name)).as(test).isTrue();
}
- for (String test: notMatching) {
+ for (String test : notMatching) {
DocumentName name = documentName.resolve(test);
assertThat(matcher.matches(name)).as(test).isFalse();
}
@@ -114,7 +119,12 @@ public class GitIgnoreBuilderTest extends
AbstractIgnoreBuilderTest {
@Test
public void test_RAT_335() {
- GitIgnoreBuilder underTest = new GitIgnoreBuilder();
+ GitIgnoreBuilder underTest = new GitIgnoreBuilder() {
+ @Override
+ protected Optional<File> globalGitIgnore() {
+ return Optional.empty();
+ }
+ };
URL url =
GitIgnoreBuilderTest.class.getClassLoader().getResource("GitIgnoreBuilderTest/src/");
File file = new File(url.getFile());
@@ -122,16 +132,49 @@ public class GitIgnoreBuilderTest extends
AbstractIgnoreBuilderTest {
List<MatcherSet> matcherSets = underTest.build(documentName);
DocumentNameMatcher matcher =
MatcherSet.merge(matcherSets).createMatcher();
- DocumentName candidate = DocumentName.builder()
-
.setName("/home/claude/apache/creadur-rat/apache-rat-core/target/test-classes/GitIgnoreBuilderTest/src/dir1/file1.log")
-
.setBaseName("home/claude/apache/creadur-rat/apache-rat-core/target/test-classes/GitIgnoreBuilderTest/src/").build();
- System.out.println("Decomposition for "+candidate);
-
assertThat(matcher.toString()).isEqualTo("matcherSet(or('included
dir1/.gitignore', 'included .gitignore'), or('excluded dir1/.gitignore',
**/.gitignore, 'excluded .gitignore'))");
- List<String> notMatching = Arrays.asList("README.txt", "dir1/dir1.md",
"dir2/dir2.txt", "dir3/file3.log", "dir1/file1.log");
+ // files that should be checked:
+ List<String> notMatching = Arrays.asList("README.txt", "dir1/dir1.md",
"dir2/dir2.txt", "dir3/file3.log", "dir1/file1.log",
"local-should-precede-global.xml");
+
+ // files that should be ignored:
+ List<String> matching = Arrays.asList(".gitignore", "root.md",
"dir1/.gitignore", "dir1/dir1.txt", "dir2/dir2.md", "dir3/dir3.log",
"local-should-precede-global.md");
+
+ assertCorrect(matcherSets, documentName.getBaseDocumentName(),
matching, notMatching);
+ }
+
+ /**
+ * Test that exclusions from a global gitignore are also applied
+ *
+ * https://issues.apache.org/jira/browse/RAT-473
+ */
+ @Test
+ public void test_global_gitignore() {
+ GitIgnoreBuilder underTest = new GitIgnoreBuilder() {
+ @Override
+ protected Optional<File> globalGitIgnore() {
+ URL globalGitIgnoreUrl =
GitIgnoreBuilderTest.class.getClassLoader().getResource("GitIgnoreBuilderTest/global-gitignore");
+ String globalGitIgnore = globalGitIgnoreUrl.getFile();
+
+ return Optional.of(new File(globalGitIgnore));
+ }
+ };
+ URL url =
GitIgnoreBuilderTest.class.getClassLoader().getResource("GitIgnoreBuilderTest/src/");
+ File file = new File(url.getFile());
+
+ DocumentName documentName = DocumentName.builder(file).build();
+ List<MatcherSet> matcherSets = underTest.build(documentName);
+ DocumentNameMatcher matcher =
MatcherSet.merge(matcherSets).createMatcher();
+
+ assertThat(matcher.toString()).isEqualTo("matcherSet(or('included
dir1/.gitignore', 'included .gitignore', 'included global gitignore'),
or('excluded dir1/.gitignore', **/.gitignore, 'excluded .gitignore', 'excluded
global gitignore'))");
+
+ // files that should be checked:
+ // "local-should-precede-global.md" should be 'matching'
+ // here, but that is a future improvement (RAT-476)
+ List<String> notMatching = Arrays.asList("dir1/dir1.md",
"dir2/dir2.txt", "dir3/file3.log", "dir1/file1.log",
"local-should-precede-global.md", "local-should-precede-global.xml");
- List<String> matching = Arrays.asList(".gitignore", "root.md",
"dir1/.gitignore", "dir1/dir1.txt", "dir2/dir2.md", "dir3/dir3.log");
+ // files that should be ignored:
+ List<String> matching = Arrays.asList(".gitignore", "README.txt",
"root.md", "dir1/.gitignore", "dir1/dir1.txt", "dir2/dir2.md", "dir3/dir3.log");
assertCorrect(matcherSets, documentName.getBaseDocumentName(),
matching, notMatching);
}
diff --git
a/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/global-gitignore
b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/global-gitignore
new file mode 100644
index 00000000..145b9982
--- /dev/null
+++ b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/global-gitignore
@@ -0,0 +1,3 @@
+/*.txt
+!/local-should-precede-global.md
+/local-should-precede-global.xml
diff --git
a/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/.gitignore
b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/.gitignore
index 8855fa80..fff8c14b 100644
--- a/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/.gitignore
+++ b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/.gitignore
@@ -5,3 +5,6 @@
# This makes it "unignore" dir3/file3.log
!file*.log
+
+# Don't ignore xml files
+!*.xml
diff --git
a/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/local-should-precede-global.md
b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/local-should-precede-global.md
new file mode 100644
index 00000000..d8de1725
--- /dev/null
+++
b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/local-should-precede-global.md
@@ -0,0 +1,5 @@
+The local .gitignore ignores '*.md'
+
+The global .gitignore un-ignores 'local-should-precede-global.md'
+
+The local .gitignore takes precedence over the global, so this file should be
ignored.
diff --git
a/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/local-should-precede-global.xml
b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/local-should-precede-global.xml
new file mode 100644
index 00000000..8e3d24e6
--- /dev/null
+++
b/apache-rat-core/src/test/resources/GitIgnoreBuilderTest/src/local-should-precede-global.xml
@@ -0,0 +1,5 @@
+The local .gitignore un-ignores '*.xml'
+
+The global .gitignore ignores 'local-should-precede-global.xml'
+
+The local .gitignore takes precedence over the global, so this file should not
be ignored.
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6b7b68a0..a1f15383 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -72,6 +72,9 @@ The <action> type attribute can be one of:
</release>
-->
<release version="0.17-SNAPSHOT" date="xxxx-yy-zz" description="Current
SNAPSHOT - release to be done">
+ <action issue="RAT-473" type="add" dev="engelen">
+ Take global gitignore into account when determining which files to
audit and which to skip.
+ </action>
<action issue="RAT-398" type="add" dev="claudenw">
Deprecated certain Ant report functionality in favour of new CLI
functionality. Deprecation information is printed to indicate how the new
options can be configured.
</action>