You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CGitIgnoreTest.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. * Copyright (C) 2017 Thomas Wolf <thomas.wolf@paranor.ch> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.ignore;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.junit.Assert.assertArrayEquals;
  13. import static org.junit.Assert.assertEquals;
  14. import static org.junit.Assert.assertFalse;
  15. import java.io.BufferedInputStream;
  16. import java.io.BufferedReader;
  17. import java.io.ByteArrayInputStream;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStreamReader;
  21. import java.util.LinkedHashSet;
  22. import java.util.Set;
  23. import org.eclipse.jgit.junit.RepositoryTestCase;
  24. import org.eclipse.jgit.lib.StoredConfig;
  25. import org.eclipse.jgit.treewalk.FileTreeIterator;
  26. import org.eclipse.jgit.treewalk.TreeWalk;
  27. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  28. import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter;
  29. import org.eclipse.jgit.util.FS;
  30. import org.eclipse.jgit.util.FS.ExecutionResult;
  31. import org.eclipse.jgit.util.RawParseUtils;
  32. import org.eclipse.jgit.util.TemporaryBuffer;
  33. import org.junit.Before;
  34. import org.junit.Test;
  35. /**
  36. * Tests that verify that the set of ignore files in a repository is the same in
  37. * JGit and in C-git.
  38. */
  39. public class CGitIgnoreTest extends RepositoryTestCase {
  40. @Before
  41. public void initRepo() throws IOException {
  42. // These tests focus on .gitignore files inside the repository. Because
  43. // we run C-git, we must ensure that global or user exclude files cannot
  44. // influence the tests. So we set core.excludesFile to an empty file
  45. // inside the repository.
  46. File fakeUserGitignore = writeTrashFile(".fake_user_gitignore", "");
  47. StoredConfig config = db.getConfig();
  48. config.setString("core", null, "excludesFile",
  49. fakeUserGitignore.getAbsolutePath());
  50. // Disable case-insensitivity -- JGit doesn't handle that yet.
  51. config.setBoolean("core", null, "ignoreCase", false);
  52. config.save();
  53. }
  54. private void createFiles(String... paths) throws IOException {
  55. for (String path : paths) {
  56. writeTrashFile(path, "x");
  57. }
  58. }
  59. private String toString(TemporaryBuffer b) throws IOException {
  60. return RawParseUtils.decode(b.toByteArray());
  61. }
  62. private String[] cgitIgnored() throws Exception {
  63. FS fs = db.getFS();
  64. ProcessBuilder builder = fs.runInShell("git", new String[] { "ls-files",
  65. "--ignored", "--exclude-standard", "-o" });
  66. builder.directory(db.getWorkTree());
  67. builder.environment().put("HOME", fs.userHome().getAbsolutePath());
  68. ExecutionResult result = fs.execute(builder,
  69. new ByteArrayInputStream(new byte[0]));
  70. String errorOut = toString(result.getStderr());
  71. assertEquals("External git failed", "exit 0\n",
  72. "exit " + result.getRc() + '\n' + errorOut);
  73. try (BufferedReader r = new BufferedReader(new InputStreamReader(
  74. new BufferedInputStream(result.getStdout().openInputStream()),
  75. UTF_8))) {
  76. return r.lines().toArray(String[]::new);
  77. }
  78. }
  79. private String[] cgitUntracked() throws Exception {
  80. FS fs = db.getFS();
  81. ProcessBuilder builder = fs.runInShell("git",
  82. new String[] { "ls-files", "--exclude-standard", "-o" });
  83. builder.directory(db.getWorkTree());
  84. builder.environment().put("HOME", fs.userHome().getAbsolutePath());
  85. ExecutionResult result = fs.execute(builder,
  86. new ByteArrayInputStream(new byte[0]));
  87. String errorOut = toString(result.getStderr());
  88. assertEquals("External git failed", "exit 0\n",
  89. "exit " + result.getRc() + '\n' + errorOut);
  90. try (BufferedReader r = new BufferedReader(new InputStreamReader(
  91. new BufferedInputStream(result.getStdout().openInputStream()),
  92. UTF_8))) {
  93. return r.lines().toArray(String[]::new);
  94. }
  95. }
  96. private void jgitIgnoredAndUntracked(LinkedHashSet<String> ignored,
  97. LinkedHashSet<String> untracked) throws IOException {
  98. // Do a tree walk that does descend into ignored directories and return
  99. // a list of all ignored files
  100. try (TreeWalk walk = new TreeWalk(db)) {
  101. FileTreeIterator iter = new FileTreeIterator(db);
  102. iter.setWalkIgnoredDirectories(true);
  103. walk.addTree(iter);
  104. walk.setRecursive(true);
  105. while (walk.next()) {
  106. if (walk.getTree(WorkingTreeIterator.class).isEntryIgnored()) {
  107. ignored.add(walk.getPathString());
  108. } else {
  109. // tests of this class won't add any files to the index,
  110. // hence everything what is not ignored is untracked
  111. untracked.add(walk.getPathString());
  112. }
  113. }
  114. }
  115. }
  116. private void assertNoIgnoredVisited(Set<String> ignored) throws Exception {
  117. // Do a recursive tree walk with a NotIgnoredFilter and verify that none
  118. // of the files visited is in the ignored set
  119. try (TreeWalk walk = new TreeWalk(db)) {
  120. walk.addTree(new FileTreeIterator(db));
  121. walk.setFilter(new NotIgnoredFilter(0));
  122. walk.setRecursive(true);
  123. while (walk.next()) {
  124. String path = walk.getPathString();
  125. assertFalse("File " + path + " is ignored, should not appear",
  126. ignored.contains(path));
  127. }
  128. }
  129. }
  130. private void assertSameAsCGit(String... notIgnored) throws Exception {
  131. LinkedHashSet<String> ignored = new LinkedHashSet<>();
  132. LinkedHashSet<String> untracked = new LinkedHashSet<>();
  133. jgitIgnoredAndUntracked(ignored, untracked);
  134. String[] cgit = cgitIgnored();
  135. String[] cgitUntracked = cgitUntracked();
  136. assertArrayEquals(cgit, ignored.toArray());
  137. assertArrayEquals(cgitUntracked, untracked.toArray());
  138. for (String notExcluded : notIgnored) {
  139. assertFalse("File " + notExcluded + " should not be ignored",
  140. ignored.contains(notExcluded));
  141. }
  142. assertNoIgnoredVisited(ignored);
  143. }
  144. @Test
  145. public void testSimpleIgnored() throws Exception {
  146. createFiles("a.txt", "a.tmp", "src/sub/a.txt", "src/a.tmp",
  147. "src/a.txt/b.tmp", "ignored/a.tmp", "ignored/not_ignored/a.tmp",
  148. "ignored/other/a.tmp");
  149. writeTrashFile(".gitignore",
  150. "*.txt\n" + "/ignored/*\n" + "!/ignored/not_ignored");
  151. assertSameAsCGit("ignored/not_ignored/a.tmp");
  152. }
  153. @Test
  154. public void testDirOnlyMatch() throws Exception {
  155. createFiles("a.txt", "src/foo/a.txt", "src/a.txt", "foo/a.txt");
  156. writeTrashFile(".gitignore", "foo/");
  157. assertSameAsCGit();
  158. }
  159. @Test
  160. public void testDirOnlyMatchDeep() throws Exception {
  161. createFiles("a.txt", "src/foo/a.txt", "src/a.txt", "foo/a.txt");
  162. writeTrashFile(".gitignore", "**/foo/");
  163. assertSameAsCGit();
  164. }
  165. @Test
  166. public void testStarMatchOnSlashNot() throws Exception {
  167. createFiles("sub/a.txt", "foo/sext", "foo/s.txt");
  168. writeTrashFile(".gitignore", "s*xt");
  169. assertSameAsCGit("sub/a.txt");
  170. }
  171. @Test
  172. public void testPrefixMatch() throws Exception {
  173. createFiles("src/new/foo.txt");
  174. writeTrashFile(".gitignore", "src/new");
  175. assertSameAsCGit();
  176. }
  177. @Test
  178. public void testDirectoryMatchSubRecursive() throws Exception {
  179. createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
  180. writeTrashFile(".gitignore", "**/src/new/");
  181. assertSameAsCGit();
  182. }
  183. @Test
  184. public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
  185. createFiles("src/new/foo.txt", "src/src/new/foo.txt");
  186. writeTrashFile(".gitignore", "**/src/new/");
  187. assertSameAsCGit();
  188. }
  189. @Test
  190. public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
  191. createFiles("src/new/foo.txt", "src/src/new/foo.txt");
  192. writeTrashFile(".gitignore", "**/**/src/new/");
  193. assertSameAsCGit();
  194. }
  195. @Test
  196. public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception {
  197. createFiles("x/a/a/b/foo.txt");
  198. writeTrashFile(".gitignore", "**/*/a/b/");
  199. assertSameAsCGit();
  200. }
  201. @Test
  202. public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception {
  203. createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt",
  204. "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt");
  205. writeTrashFile(".gitignore", "**/*/a/b bar\n");
  206. assertSameAsCGit();
  207. }
  208. @Test
  209. public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception {
  210. createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt");
  211. writeTrashFile(".gitignore", "**/*/**/a/b bar\n");
  212. assertSameAsCGit();
  213. }
  214. @Test
  215. public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception {
  216. createFiles("a", "dir/b", "dir/sub/c");
  217. writeTrashFile(".gitignore", "**/\n");
  218. assertSameAsCGit();
  219. }
  220. @Test
  221. public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception {
  222. createFiles("a", "dir/b", "dir/sub/c");
  223. writeTrashFile(".gitignore", "**/**/\n");
  224. assertSameAsCGit();
  225. }
  226. @Test
  227. public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception {
  228. createFiles("a", "x/b", "sub/x/c", "sub/x/d/e");
  229. writeTrashFile(".gitignore", "x/**/\n");
  230. assertSameAsCGit();
  231. }
  232. @Test
  233. public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception {
  234. createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y");
  235. writeTrashFile(".gitignore", "**/x/\n");
  236. assertSameAsCGit();
  237. }
  238. @Test
  239. public void testUnescapedBracketsInGroup() throws Exception {
  240. createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
  241. writeTrashFile(".gitignore", "[[]]\n");
  242. assertSameAsCGit();
  243. }
  244. @Test
  245. public void testEscapedFirstBracketInGroup() throws Exception {
  246. createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
  247. writeTrashFile(".gitignore", "[\\[]]\n");
  248. assertSameAsCGit();
  249. }
  250. @Test
  251. public void testEscapedSecondBracketInGroup() throws Exception {
  252. createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
  253. writeTrashFile(".gitignore", "[[\\]]\n");
  254. assertSameAsCGit();
  255. }
  256. @Test
  257. public void testEscapedBothBracketsInGroup() throws Exception {
  258. createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
  259. writeTrashFile(".gitignore", "[\\[\\]]\n");
  260. assertSameAsCGit();
  261. }
  262. @Test
  263. public void testSimpleRootGitIgnoreGlobalNegation1() throws Exception {
  264. // see IgnoreNodeTest.testSimpleRootGitIgnoreGlobalNegation1
  265. createFiles("x1", "a/x2", "x3/y");
  266. writeTrashFile(".gitignore", "*\n!x*");
  267. assertSameAsCGit();
  268. }
  269. @Test
  270. public void testRepeatedNegationInDifferentFiles5() throws Exception {
  271. // see IgnoreNodeTest.testRepeatedNegationInDifferentFiles5
  272. createFiles("a/b/e/nothere.o");
  273. writeTrashFile(".gitignore", "e");
  274. writeTrashFile("a/.gitignore", "e");
  275. writeTrashFile("a/b/.gitignore", "!e");
  276. assertSameAsCGit();
  277. }
  278. @Test
  279. public void testRepeatedNegationInDifferentFilesWithWildmatcher1()
  280. throws Exception {
  281. createFiles("e", "x/e/f", "a/e/x1", "a/e/x2", "a/e/y", "a/e/sub/y");
  282. writeTrashFile(".gitignore", "a/e/**");
  283. writeTrashFile("a/.gitignore", "!e/x*");
  284. assertSameAsCGit();
  285. }
  286. @Test
  287. public void testRepeatedNegationInDifferentFilesWithWildmatcher2()
  288. throws Exception {
  289. createFiles("e", "dir/f", "dir/g/h", "a/dir/i", "a/dir/j/k",
  290. "a/b/dir/l", "a/b/dir/m/n", "a/b/dir/m/o/p", "a/q/dir/r",
  291. "a/q/dir/dir/s", "c/d/dir/x", "c/d/dir/y");
  292. writeTrashFile(".gitignore", "**/dir/*");
  293. writeTrashFile("a/.gitignore", "!dir/*");
  294. writeTrashFile("a/b/.gitignore", "!**/dir/*");
  295. writeTrashFile("c/.gitignore", "!d/dir/x");
  296. assertSameAsCGit();
  297. }
  298. @Test
  299. public void testNegationForSubDirectoryWithinIgnoredDirectoryHasNoEffect1()
  300. throws Exception {
  301. createFiles("e", "a/f", "a/b/g", "a/b/h/i");
  302. writeTrashFile(".gitignore", "a/b");
  303. writeTrashFile("a/.gitignore", "!b/*");
  304. assertSameAsCGit();
  305. }
  306. /*
  307. * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=407475
  308. */
  309. @Test
  310. public void testNegationAllExceptJavaInSrcAndExceptChildDirInSrc()
  311. throws Exception {
  312. // see
  313. // IgnoreNodeTest.testNegationAllExceptJavaInSrcAndExceptChildDirInSrc
  314. createFiles("nothere.o", "src/keep.java", "src/nothere.o",
  315. "src/a/keep.java", "src/a/keep.o");
  316. writeTrashFile(".gitignore", "/*\n!/src/");
  317. writeTrashFile("src/.gitignore", "*\n!*.java\n!*/");
  318. assertSameAsCGit();
  319. }
  320. }