aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf <twolf@apache.org>2023-06-15 22:08:06 +0200
committerThomas Wolf <twolf@apache.org>2023-06-19 08:19:29 +0200
commitfaefa90f990858db7bec199501cb37f2631c43d0 (patch)
tree0a0500f2e2cd11b8582ee01a4582f5dcaa756bc2
parent7b955048eb86e1c12114554beacb27b329252b15 (diff)
downloadjgit-faefa90f990858db7bec199501cb37f2631c43d0.tar.gz
jgit-faefa90f990858db7bec199501cb37f2631c43d0.zip
Default for global (user) git ignore file
C git has a default for git config core.excludesfile: "Its default value is $XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore is used instead." [1] Implement this in the WorkingTreeIterator$RootIgnoreNode. To make this testable, mock the "user.home" directory for all JGit tests, otherwise tests might pick up a real user's git ignore file. Also ensure that JGit code always reads "user.home" via the SystemReader. Add tests for both locations. [1] https://git-scm.com/docs/gitignore#_description Bug: 436127 Change-Id: Ie510259320286c3c13a6464a37da1bd9ca1e373a Signed-off-by: Thomas Wolf <twolf@apache.org>
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java12
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java24
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java108
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java47
7 files changed, 193 insertions, 45 deletions
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
index 6fa30d7e1e..b183b22603 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
@@ -89,8 +89,6 @@ public abstract class SshTestHarness extends RepositoryTestCase {
protected File knownHosts;
- private File homeDir;
-
@Override
public void setUp() throws Exception {
super.setUp();
@@ -99,13 +97,8 @@ public abstract class SshTestHarness extends RepositoryTestCase {
git.add().addFilepattern("file.txt").call();
git.commit().setMessage("Initial commit").call();
}
- mockSystemReader.setProperty("user.home",
- getTemporaryDirectory().getAbsolutePath());
- mockSystemReader.setProperty("HOME",
- getTemporaryDirectory().getAbsolutePath());
- homeDir = FS.DETECTED.userHome();
- FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile());
- sshDir = new File(getTemporaryDirectory(), ".ssh");
+ // The home directory is mocked here
+ sshDir = new File(FS.DETECTED.userHome(), ".ssh");
assertTrue(sshDir.mkdir());
File serverDir = new File(getTemporaryDirectory(), "srv");
assertTrue(serverDir.mkdir());
@@ -236,7 +229,6 @@ public abstract class SshTestHarness extends RepositoryTestCase {
server.stop();
server = null;
}
- FS.DETECTED.setUserHome(homeDir);
SshSessionFactory.setInstance(null);
factory = null;
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 0945327ab3..f816158b10 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -85,6 +85,8 @@ public abstract class LocalDiskRepositoryTestCase {
private final Set<Repository> toClose = new HashSet<>();
private File tmp;
+ private File homeDir;
+
/**
* The current test name.
*
@@ -119,6 +121,14 @@ public abstract class LocalDiskRepositoryTestCase {
mockSystemReader = new MockSystemReader();
SystemReader.setInstance(mockSystemReader);
+ // Mock the home directory. We don't want to pick up the real user's git
+ // config, or global git ignore.
+ // XDG_CONFIG_HOME isn't set in the MockSystemReader.
+ mockSystemReader.setProperty("user.home", tmp.getAbsolutePath());
+ mockSystemReader.setProperty("HOME", tmp.getAbsolutePath());
+ homeDir = FS.DETECTED.userHome();
+ FS.DETECTED.setUserHome(tmp.getAbsoluteFile());
+
// Measure timer resolution before the test to avoid time critical tests
// are affected by time needed for measurement.
// The MockSystemReader must be configured first since we need to use
@@ -195,21 +205,25 @@ public abstract class LocalDiskRepositoryTestCase {
@After
public void tearDown() throws Exception {
RepositoryCache.clear();
- for (Repository r : toClose)
+ for (Repository r : toClose) {
r.close();
+ }
toClose.clear();
// Since memory mapping is controlled by the GC we need to
// tell it this is a good time to clean up and unlock
// memory mapped files.
//
- if (useMMAP)
+ if (useMMAP) {
System.gc();
- if (tmp != null)
+ }
+ FS.DETECTED.setUserHome(homeDir);
+ if (tmp != null) {
recursiveDelete(tmp, false, true);
- if (tmp != null && !tmp.exists())
+ }
+ if (tmp != null && !tmp.exists()) {
CleanupThread.removed(tmp);
-
+ }
SystemReader.setInstance(null);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
index 05a953e081..ab08c99796 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
@@ -20,14 +20,18 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
@@ -698,6 +702,110 @@ public class IgnoreNodeTest extends RepositoryTestCase {
}
@Test
+ public void testUserGitIgnoreFound() throws IOException {
+ File homeDir = FS.DETECTED.userHome();
+ Path userIgnore = homeDir.toPath().resolve(".config").resolve("git")
+ .resolve("ignore");
+ Files.createDirectories(userIgnore.getParent());
+ Files.writeString(userIgnore, "x");
+ try {
+ writeTrashFile(".foo", "");
+ writeTrashFile("a/x/file", "");
+ writeTrashFile("b/x", "");
+ writeTrashFile("x/file", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".foo");
+ assertEntry(D, tracked, "a");
+ assertEntry(D, ignored, "a/x");
+ assertEntry(F, ignored, "a/x/file");
+ assertEntry(D, tracked, "b");
+ assertEntry(F, ignored, "b/x");
+ assertEntry(D, ignored, "x");
+ assertEntry(F, ignored, "x/file");
+ endWalk();
+ } finally {
+ Files.deleteIfExists(userIgnore);
+ }
+ }
+
+ @Test
+ public void testXdgIgnoreFound() throws IOException {
+ File tmp = getTemporaryDirectory();
+ Path xdg = tmp.toPath().resolve("xdg");
+ Path userIgnore = xdg.resolve("git").resolve("ignore");
+ Files.createDirectories(userIgnore.getParent());
+ Files.writeString(userIgnore, "x");
+ SystemReader system = SystemReader.getInstance();
+ assertTrue(system instanceof MockSystemReader);
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME",
+ xdg.toAbsolutePath().toString());
+ // Also create the one in the home directory -- it should not be active
+ File homeDir = FS.DETECTED.userHome();
+ Path userIgnore2 = homeDir.toPath().resolve(".config").resolve("git")
+ .resolve("ignore");
+ Files.createDirectories(userIgnore2.getParent());
+ Files.writeString(userIgnore2, "a");
+ try {
+ writeTrashFile(".foo", "");
+ writeTrashFile("a/x/file", "");
+ writeTrashFile("b/x", "");
+ writeTrashFile("x/file", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".foo");
+ assertEntry(D, tracked, "a");
+ assertEntry(D, ignored, "a/x");
+ assertEntry(F, ignored, "a/x/file");
+ assertEntry(D, tracked, "b");
+ assertEntry(F, ignored, "b/x");
+ assertEntry(D, ignored, "x");
+ assertEntry(F, ignored, "x/file");
+ endWalk();
+ } finally {
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME", null);
+ Files.deleteIfExists(userIgnore2);
+ }
+ }
+
+ @Test
+ public void testXdgWrong() throws IOException {
+ File tmp = getTemporaryDirectory();
+ Path xdg = tmp.toPath().resolve("xdg");
+ SystemReader system = SystemReader.getInstance();
+ assertTrue(system instanceof MockSystemReader);
+ // Valid value, but the directory doesn't exist
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME",
+ xdg.toAbsolutePath().toString());
+ // Also create the one in the home directory -- it should not be active
+ File homeDir = FS.DETECTED.userHome();
+ Path userIgnore2 = homeDir.toPath().resolve(".config").resolve("git")
+ .resolve("ignore");
+ Files.createDirectories(userIgnore2.getParent());
+ Files.writeString(userIgnore2, "x");
+ try {
+ writeTrashFile(".foo", "");
+ writeTrashFile("a/x/file", "");
+ writeTrashFile("b/x", "");
+ writeTrashFile("x/file", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".foo");
+ assertEntry(D, tracked, "a");
+ assertEntry(D, tracked, "a/x");
+ assertEntry(F, tracked, "a/x/file");
+ assertEntry(D, tracked, "b");
+ assertEntry(F, tracked, "b/x");
+ assertEntry(D, tracked, "x");
+ assertEntry(F, tracked, "x/file");
+ endWalk();
+ } finally {
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME", null);
+ Files.deleteIfExists(userIgnore2);
+ }
+ }
+
+ @Test
public void testToString() throws Exception {
assertEquals(Arrays.asList("").toString(), new IgnoreNode().toString());
assertEquals(Arrays.asList("hello").toString(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java
index 42bafb60ca..3ccd0ef021 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java
@@ -17,7 +17,9 @@ import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.util.FS;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -27,7 +29,7 @@ import org.junit.rules.TemporaryFolder;
* test using bazel which doesn't allow tests to create files in the home
* directory
*/
-public class CommitTemplateConfigTest {
+public class CommitTemplateConfigTest extends LocalDiskRepositoryTestCase {
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
@@ -42,9 +44,11 @@ public class CommitTemplateConfigTest {
String templateContent = "content of the template";
JGitTestUtil.write(tempFile, templateContent);
// proper evaluation of the ~/ directory
- String homeDir = System.getProperty("user.home");
+ File homeDir = FS.DETECTED.userHome();
File tempFileInHomeDirectory = File.createTempFile("fileInHomeFolder",
- ".tmp", new File(homeDir));
+ ".tmp", homeDir);
+ // The home directory should be a mocked temporary directory, but
+ // still...
tempFileInHomeDirectory.deleteOnExit();
JGitTestUtil.write(tempFileInHomeDirectory, templateContent);
String expectedTemplatePath = "~/" + tempFileInHomeDirectory.getName();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index aa544606af..98d0846612 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -25,6 +25,7 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetEncoder;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.time.Instant;
@@ -68,6 +69,7 @@ import org.eclipse.jgit.util.Holder;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.Paths;
import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.TemporaryBuffer;
import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
@@ -1329,7 +1331,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_EXCLUDESFILE, fs, null, null);
if (path != null) {
- loadRulesFromFile(coreExclude, path.toFile());
+ if (Files.exists(path)) {
+ loadRulesFromFile(coreExclude, path.toFile());
+ }
+ } else {
+ loadRulesFromDefaultFile(coreExclude, fs);
}
if (coreExclude.getRules().isEmpty()) {
coreExclude = parent;
@@ -1339,7 +1345,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
coreExclude);
File exclude = fs.resolve(repository.getDirectory(),
Constants.INFO_EXCLUDE);
- loadRulesFromFile(infoExclude, exclude);
+ if (fs.exists(exclude)) {
+ loadRulesFromFile(infoExclude, exclude);
+ }
if (infoExclude.getRules().isEmpty()) {
infoExclude = null;
}
@@ -1361,9 +1369,19 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private static void loadRulesFromFile(IgnoreNode r, File exclude)
throws FileNotFoundException, IOException {
- if (FS.DETECTED.exists(exclude)) {
- try (FileInputStream in = new FileInputStream(exclude)) {
- r.parse(exclude.getAbsolutePath(), in);
+ try (FileInputStream in = new FileInputStream(exclude)) {
+ r.parse(exclude.getAbsolutePath(), in);
+ }
+ }
+
+ private static void loadRulesFromDefaultFile(IgnoreNode r,
+ FS fileSystem) throws FileNotFoundException, IOException {
+ Path cfg = SystemReader.getInstance()
+ .getXdgConfigDirectory(fileSystem);
+ if (cfg != null) {
+ Path cfgPath = cfg.resolve("git").resolve("ignore"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (Files.exists(cfgPath)) {
+ loadRulesFromFile(r, cfgPath.toFile());
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index f504c66eb3..158365a299 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -31,8 +31,6 @@ import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.security.AccessControlException;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
@@ -1294,11 +1292,10 @@ public abstract class FS {
}
private File defaultUserHomeImpl() {
- String home = AccessController.doPrivileged(
- (PrivilegedAction<String>) () -> System.getProperty("user.home") //$NON-NLS-1$
- );
- if (home == null || home.length() == 0)
+ String home = SystemReader.getInstance().getProperty("user.home"); //$NON-NLS-1$
+ if (StringUtils.isEmptyOrNull(home)) {
return null;
+ }
return new File(home).getAbsoluteFile();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index a8a77904a2..991de51df7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -128,24 +128,9 @@ public abstract class SystemReader {
fs);
}
- private Path getXDGConfigHome(FS fs) {
- String configHomePath = getenv(Constants.XDG_CONFIG_HOME);
- if (StringUtils.isEmptyOrNull(configHomePath)) {
- configHomePath = new File(fs.userHome(), ".config") //$NON-NLS-1$
- .getAbsolutePath();
- }
- try {
- return Paths.get(configHomePath);
- } catch (InvalidPathException e) {
- LOG.error(JGitText.get().logXDGConfigHomeInvalid,
- configHomePath, e);
- }
- return null;
- }
-
@Override
public FileBasedConfig openJGitConfig(Config parent, FS fs) {
- Path xdgPath = getXDGConfigHome(fs);
+ Path xdgPath = getXdgConfigDirectory(fs);
if (xdgPath != null) {
Path configPath = xdgPath.resolve("jgit") //$NON-NLS-1$
.resolve(Constants.CONFIG);
@@ -391,6 +376,36 @@ public abstract class SystemReader {
}
/**
+ * Gets the directory denoted by environment variable XDG_CONFIG_HOME. If
+ * the variable is not set or empty, return a path for
+ * {@code $HOME/.config}.
+ *
+ * @param fileSystem
+ * {@link FS} to get the user's home directory
+ * @return a {@link Path} denoting the directory, which may exist or not, or
+ * {@code null} if the environment variable is not set and there is
+ * no home directory, or the path is invalid.
+ * @since 6.7
+ */
+ public Path getXdgConfigDirectory(FS fileSystem) {
+ String configHomePath = getenv(Constants.XDG_CONFIG_HOME);
+ if (StringUtils.isEmptyOrNull(configHomePath)) {
+ File home = fileSystem.userHome();
+ if (home == null) {
+ return null;
+ }
+ configHomePath = new File(home, ".config").getAbsolutePath(); //$NON-NLS-1$
+ }
+ try {
+ return Paths.get(configHomePath);
+ } catch (InvalidPathException e) {
+ LOG.error(JGitText.get().logXDGConfigHomeInvalid, configHomePath,
+ e);
+ }
+ return null;
+ }
+
+ /**
* Update config and its parents if they seem modified
*
* @param config