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 6fa30d7e1..b183b2260 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 void setUp() throws Exception { 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 void shutdownServer() throws Exception { 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 0945327ab..f816158b1 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 toClose = new HashSet<>(); private File tmp; + private File homeDir; + /** * The current test name. * @@ -119,6 +121,14 @@ public void setUp() throws Exception { 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 @@ private static String makePath(List objects) { @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 05a953e08..ab08c9979 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.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; @@ -697,6 +701,110 @@ public void testTrailingSpaces() throws IOException { endWalk(); } + @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()); 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 42bafb60c..3ccd0ef02 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 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 @@ * 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 void testCommitTemplatePathInHomeDirecory() 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 aa544606a..98d084661 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.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.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 @@ IgnoreNode load(IgnoreNode parent) throws IOException { 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 @@ IgnoreNode load(IgnoreNode parent) throws IOException { 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 @@ IgnoreNode load(IgnoreNode parent) throws IOException { 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 f504c66eb..158365a29 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.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 @@ protected File userHomeImpl() { } private File defaultUserHomeImpl() { - String home = AccessController.doPrivileged( - (PrivilegedAction) () -> 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 a8a77904a..991de51df 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 FileBasedConfig openUserConfig(Config parent, FS fs) { 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); @@ -390,6 +375,36 @@ public StoredConfig getSystemConfig() return c; } + /** + * 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 *