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.

IndexDiffTest.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  5. * Copyright (C) 2013, Robin Stocker <robin@nibor.org>
  6. * and other copyright owners as documented in the project's IP log.
  7. *
  8. * This program and the accompanying materials are made available
  9. * under the terms of the Eclipse Distribution License v1.0 which
  10. * accompanies this distribution, is reproduced below, and is
  11. * available at http://www.eclipse.org/org/documents/edl-v10.php
  12. *
  13. * All rights reserved.
  14. *
  15. * Redistribution and use in source and binary forms, with or
  16. * without modification, are permitted provided that the following
  17. * conditions are met:
  18. *
  19. * - Redistributions of source code must retain the above copyright
  20. * notice, this list of conditions and the following disclaimer.
  21. *
  22. * - Redistributions in binary form must reproduce the above
  23. * copyright notice, this list of conditions and the following
  24. * disclaimer in the documentation and/or other materials provided
  25. * with the distribution.
  26. *
  27. * - Neither the name of the Eclipse Foundation, Inc. nor the
  28. * names of its contributors may be used to endorse or promote
  29. * products derived from this software without specific prior
  30. * written permission.
  31. *
  32. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  33. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  34. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  36. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  37. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  39. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  41. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  42. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  43. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  44. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45. */
  46. package org.eclipse.jgit.lib;
  47. import static org.junit.Assert.assertEquals;
  48. import static org.junit.Assert.assertFalse;
  49. import static org.junit.Assert.assertTrue;
  50. import java.io.File;
  51. import java.io.FileNotFoundException;
  52. import java.io.IOException;
  53. import java.util.Arrays;
  54. import java.util.Collections;
  55. import java.util.HashSet;
  56. import java.util.TreeSet;
  57. import org.eclipse.jgit.api.Git;
  58. import org.eclipse.jgit.api.MergeResult;
  59. import org.eclipse.jgit.api.MergeResult.MergeStatus;
  60. import org.eclipse.jgit.api.errors.GitAPIException;
  61. import org.eclipse.jgit.dircache.DirCache;
  62. import org.eclipse.jgit.dircache.DirCacheBuilder;
  63. import org.eclipse.jgit.dircache.DirCacheEditor;
  64. import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
  65. import org.eclipse.jgit.dircache.DirCacheEntry;
  66. import org.eclipse.jgit.junit.RepositoryTestCase;
  67. import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
  68. import org.eclipse.jgit.lib.IndexDiff.StageState;
  69. import org.eclipse.jgit.merge.MergeStrategy;
  70. import org.eclipse.jgit.revwalk.RevCommit;
  71. import org.eclipse.jgit.storage.file.FileBasedConfig;
  72. import org.eclipse.jgit.treewalk.FileTreeIterator;
  73. import org.eclipse.jgit.util.IO;
  74. import org.junit.Test;
  75. public class IndexDiffTest extends RepositoryTestCase {
  76. static PathEdit add(final Repository db, final File workdir,
  77. final String path) throws FileNotFoundException, IOException {
  78. ObjectInserter inserter = db.newObjectInserter();
  79. final File f = new File(workdir, path);
  80. final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
  81. IO.readFully(f));
  82. return new PathEdit(path) {
  83. @Override
  84. public void apply(DirCacheEntry ent) {
  85. ent.setFileMode(FileMode.REGULAR_FILE);
  86. ent.setLength(f.length());
  87. ent.setObjectId(id);
  88. }
  89. };
  90. }
  91. @Test
  92. public void testAdded() throws IOException {
  93. writeTrashFile("file1", "file1");
  94. writeTrashFile("dir/subfile", "dir/subfile");
  95. ObjectId tree = insertTree(new TreeFormatter());
  96. DirCache index = db.lockDirCache();
  97. DirCacheEditor editor = index.editor();
  98. editor.add(add(db, trash, "file1"));
  99. editor.add(add(db, trash, "dir/subfile"));
  100. editor.commit();
  101. FileTreeIterator iterator = new FileTreeIterator(db);
  102. IndexDiff diff = new IndexDiff(db, tree, iterator);
  103. diff.diff();
  104. assertEquals(2, diff.getAdded().size());
  105. assertTrue(diff.getAdded().contains("file1"));
  106. assertTrue(diff.getAdded().contains("dir/subfile"));
  107. assertEquals(0, diff.getChanged().size());
  108. assertEquals(0, diff.getModified().size());
  109. assertEquals(0, diff.getRemoved().size());
  110. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  111. }
  112. @Test
  113. public void testMissing() throws Exception {
  114. File file2 = writeTrashFile("file2", "file2");
  115. File file3 = writeTrashFile("dir/file3", "dir/file3");
  116. Git git = Git.wrap(db);
  117. git.add().addFilepattern("file2").addFilepattern("dir/file3").call();
  118. git.commit().setMessage("commit").call();
  119. assertTrue(file2.delete());
  120. assertTrue(file3.delete());
  121. IndexDiff diff = new IndexDiff(db, Constants.HEAD,
  122. new FileTreeIterator(db));
  123. diff.diff();
  124. assertEquals(2, diff.getMissing().size());
  125. assertTrue(diff.getMissing().contains("file2"));
  126. assertTrue(diff.getMissing().contains("dir/file3"));
  127. assertEquals(0, diff.getChanged().size());
  128. assertEquals(0, diff.getModified().size());
  129. assertEquals(0, diff.getAdded().size());
  130. assertEquals(0, diff.getRemoved().size());
  131. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  132. }
  133. @Test
  134. public void testRemoved() throws IOException {
  135. writeTrashFile("file2", "file2");
  136. writeTrashFile("dir/file3", "dir/file3");
  137. TreeFormatter dir = new TreeFormatter();
  138. dir.append("file3", FileMode.REGULAR_FILE, ObjectId.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b"));
  139. TreeFormatter tree = new TreeFormatter();
  140. tree.append("file2", FileMode.REGULAR_FILE, ObjectId.fromString("30d67d4672d5c05833b7192cc77a79eaafb5c7ad"));
  141. tree.append("dir", FileMode.TREE, insertTree(dir));
  142. ObjectId treeId = insertTree(tree);
  143. FileTreeIterator iterator = new FileTreeIterator(db);
  144. IndexDiff diff = new IndexDiff(db, treeId, iterator);
  145. diff.diff();
  146. assertEquals(2, diff.getRemoved().size());
  147. assertTrue(diff.getRemoved().contains("file2"));
  148. assertTrue(diff.getRemoved().contains("dir/file3"));
  149. assertEquals(0, diff.getChanged().size());
  150. assertEquals(0, diff.getModified().size());
  151. assertEquals(0, diff.getAdded().size());
  152. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  153. }
  154. @Test
  155. public void testModified() throws IOException, GitAPIException {
  156. writeTrashFile("file2", "file2");
  157. writeTrashFile("dir/file3", "dir/file3");
  158. try (Git git = new Git(db)) {
  159. git.add().addFilepattern("file2").addFilepattern("dir/file3").call();
  160. }
  161. writeTrashFile("dir/file3", "changed");
  162. TreeFormatter dir = new TreeFormatter();
  163. dir.append("file3", FileMode.REGULAR_FILE, ObjectId.fromString("0123456789012345678901234567890123456789"));
  164. TreeFormatter tree = new TreeFormatter();
  165. tree.append("dir", FileMode.TREE, insertTree(dir));
  166. tree.append("file2", FileMode.REGULAR_FILE, ObjectId.fromString("0123456789012345678901234567890123456789"));
  167. ObjectId treeId = insertTree(tree);
  168. FileTreeIterator iterator = new FileTreeIterator(db);
  169. IndexDiff diff = new IndexDiff(db, treeId, iterator);
  170. diff.diff();
  171. assertEquals(2, diff.getChanged().size());
  172. assertTrue(diff.getChanged().contains("file2"));
  173. assertTrue(diff.getChanged().contains("dir/file3"));
  174. assertEquals(1, diff.getModified().size());
  175. assertTrue(diff.getModified().contains("dir/file3"));
  176. assertEquals(0, diff.getAdded().size());
  177. assertEquals(0, diff.getRemoved().size());
  178. assertEquals(0, diff.getMissing().size());
  179. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  180. }
  181. @Test
  182. public void testConflicting() throws Exception {
  183. try (Git git = new Git(db)) {
  184. writeTrashFile("a", "1\na\n3\n");
  185. writeTrashFile("b", "1\nb\n3\n");
  186. git.add().addFilepattern("a").addFilepattern("b").call();
  187. RevCommit initialCommit = git.commit().setMessage("initial").call();
  188. // create side branch with two modifications
  189. createBranch(initialCommit, "refs/heads/side");
  190. checkoutBranch("refs/heads/side");
  191. writeTrashFile("a", "1\na(side)\n3\n");
  192. writeTrashFile("b", "1\nb\n3\n(side)");
  193. git.add().addFilepattern("a").addFilepattern("b").call();
  194. RevCommit secondCommit = git.commit().setMessage("side").call();
  195. // update a on master to generate conflict
  196. checkoutBranch("refs/heads/master");
  197. writeTrashFile("a", "1\na(main)\n3\n");
  198. git.add().addFilepattern("a").call();
  199. git.commit().setMessage("main").call();
  200. // merge side with master
  201. MergeResult result = git.merge().include(secondCommit.getId())
  202. .setStrategy(MergeStrategy.RESOLVE).call();
  203. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  204. }
  205. FileTreeIterator iterator = new FileTreeIterator(db);
  206. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  207. diff.diff();
  208. assertEquals("[b]",
  209. new TreeSet<>(diff.getChanged()).toString());
  210. assertEquals("[]", diff.getAdded().toString());
  211. assertEquals("[]", diff.getRemoved().toString());
  212. assertEquals("[]", diff.getMissing().toString());
  213. assertEquals("[]", diff.getModified().toString());
  214. assertEquals("[a]", diff.getConflicting().toString());
  215. assertEquals(StageState.BOTH_MODIFIED,
  216. diff.getConflictingStageStates().get("a"));
  217. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  218. }
  219. @Test
  220. public void testConflictingDeletedAndModified() throws Exception {
  221. try (Git git = new Git(db)) {
  222. writeTrashFile("a", "1\na\n3\n");
  223. writeTrashFile("b", "1\nb\n3\n");
  224. git.add().addFilepattern("a").addFilepattern("b").call();
  225. RevCommit initialCommit = git.commit().setMessage("initial").call();
  226. // create side branch and delete "a"
  227. createBranch(initialCommit, "refs/heads/side");
  228. checkoutBranch("refs/heads/side");
  229. git.rm().addFilepattern("a").call();
  230. RevCommit secondCommit = git.commit().setMessage("side").call();
  231. // update a on master to generate conflict
  232. checkoutBranch("refs/heads/master");
  233. writeTrashFile("a", "1\na(main)\n3\n");
  234. git.add().addFilepattern("a").call();
  235. git.commit().setMessage("main").call();
  236. // merge side with master
  237. MergeResult result = git.merge().include(secondCommit.getId())
  238. .setStrategy(MergeStrategy.RESOLVE).call();
  239. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  240. }
  241. FileTreeIterator iterator = new FileTreeIterator(db);
  242. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  243. diff.diff();
  244. assertEquals("[]", new TreeSet<>(diff.getChanged()).toString());
  245. assertEquals("[]", diff.getAdded().toString());
  246. assertEquals("[]", diff.getRemoved().toString());
  247. assertEquals("[]", diff.getMissing().toString());
  248. assertEquals("[]", diff.getModified().toString());
  249. assertEquals("[a]", diff.getConflicting().toString());
  250. assertEquals(StageState.DELETED_BY_THEM,
  251. diff.getConflictingStageStates().get("a"));
  252. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  253. }
  254. @Test
  255. public void testConflictingFromMultipleCreations() throws Exception {
  256. try (Git git = new Git(db)) {
  257. writeTrashFile("a", "1\na\n3\n");
  258. git.add().addFilepattern("a").call();
  259. RevCommit initialCommit = git.commit().setMessage("initial").call();
  260. createBranch(initialCommit, "refs/heads/side");
  261. checkoutBranch("refs/heads/side");
  262. writeTrashFile("b", "1\nb(side)\n3\n");
  263. git.add().addFilepattern("b").call();
  264. RevCommit secondCommit = git.commit().setMessage("side").call();
  265. checkoutBranch("refs/heads/master");
  266. writeTrashFile("b", "1\nb(main)\n3\n");
  267. git.add().addFilepattern("b").call();
  268. git.commit().setMessage("main").call();
  269. MergeResult result = git.merge().include(secondCommit.getId())
  270. .setStrategy(MergeStrategy.RESOLVE).call();
  271. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  272. }
  273. FileTreeIterator iterator = new FileTreeIterator(db);
  274. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  275. diff.diff();
  276. assertEquals("[]", new TreeSet<>(diff.getChanged()).toString());
  277. assertEquals("[]", diff.getAdded().toString());
  278. assertEquals("[]", diff.getRemoved().toString());
  279. assertEquals("[]", diff.getMissing().toString());
  280. assertEquals("[]", diff.getModified().toString());
  281. assertEquals("[b]", diff.getConflicting().toString());
  282. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  283. }
  284. @Test
  285. public void testUnchangedSimple() throws IOException, GitAPIException {
  286. writeTrashFile("a.b", "a.b");
  287. writeTrashFile("a.c", "a.c");
  288. writeTrashFile("a=c", "a=c");
  289. writeTrashFile("a=d", "a=d");
  290. try (Git git = new Git(db)) {
  291. git.add().addFilepattern("a.b").call();
  292. git.add().addFilepattern("a.c").call();
  293. git.add().addFilepattern("a=c").call();
  294. git.add().addFilepattern("a=d").call();
  295. }
  296. TreeFormatter tree = new TreeFormatter();
  297. // got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin
  298. tree.append("a.b", FileMode.REGULAR_FILE, ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851"));
  299. tree.append("a.c", FileMode.REGULAR_FILE, ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca"));
  300. tree.append("a=c", FileMode.REGULAR_FILE, ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714"));
  301. tree.append("a=d", FileMode.REGULAR_FILE, ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2"));
  302. ObjectId treeId = insertTree(tree);
  303. FileTreeIterator iterator = new FileTreeIterator(db);
  304. IndexDiff diff = new IndexDiff(db, treeId, iterator);
  305. diff.diff();
  306. assertEquals(0, diff.getChanged().size());
  307. assertEquals(0, diff.getAdded().size());
  308. assertEquals(0, diff.getRemoved().size());
  309. assertEquals(0, diff.getMissing().size());
  310. assertEquals(0, diff.getModified().size());
  311. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  312. }
  313. /**
  314. * This test has both files and directories that involve the tricky ordering
  315. * used by Git.
  316. *
  317. * @throws IOException
  318. * @throws GitAPIException
  319. */
  320. @Test
  321. public void testUnchangedComplex() throws IOException, GitAPIException {
  322. writeTrashFile("a.b", "a.b");
  323. writeTrashFile("a.c", "a.c");
  324. writeTrashFile("a/b.b/b", "a/b.b/b");
  325. writeTrashFile("a/b", "a/b");
  326. writeTrashFile("a/c", "a/c");
  327. writeTrashFile("a=c", "a=c");
  328. writeTrashFile("a=d", "a=d");
  329. try (Git git = new Git(db)) {
  330. git.add().addFilepattern("a.b").addFilepattern("a.c")
  331. .addFilepattern("a/b.b/b").addFilepattern("a/b")
  332. .addFilepattern("a/c").addFilepattern("a=c")
  333. .addFilepattern("a=d").call();
  334. }
  335. // got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin
  336. TreeFormatter bb = new TreeFormatter();
  337. bb.append("b", FileMode.REGULAR_FILE, ObjectId.fromString("8d840bd4e2f3a48ff417c8e927d94996849933fd"));
  338. TreeFormatter a = new TreeFormatter();
  339. a.append("b", FileMode.REGULAR_FILE, ObjectId
  340. .fromString("db89c972fc57862eae378f45b74aca228037d415"));
  341. a.append("b.b", FileMode.TREE, insertTree(bb));
  342. a.append("c", FileMode.REGULAR_FILE, ObjectId.fromString("52ad142a008aeb39694bafff8e8f1be75ed7f007"));
  343. TreeFormatter tree = new TreeFormatter();
  344. tree.append("a.b", FileMode.REGULAR_FILE, ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851"));
  345. tree.append("a.c", FileMode.REGULAR_FILE, ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca"));
  346. tree.append("a", FileMode.TREE, insertTree(a));
  347. tree.append("a=c", FileMode.REGULAR_FILE, ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714"));
  348. tree.append("a=d", FileMode.REGULAR_FILE, ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2"));
  349. ObjectId treeId = insertTree(tree);
  350. FileTreeIterator iterator = new FileTreeIterator(db);
  351. IndexDiff diff = new IndexDiff(db, treeId, iterator);
  352. diff.diff();
  353. assertEquals(0, diff.getChanged().size());
  354. assertEquals(0, diff.getAdded().size());
  355. assertEquals(0, diff.getRemoved().size());
  356. assertEquals(0, diff.getMissing().size());
  357. assertEquals(0, diff.getModified().size());
  358. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  359. }
  360. private ObjectId insertTree(TreeFormatter tree) throws IOException {
  361. try (ObjectInserter oi = db.newObjectInserter()) {
  362. ObjectId id = oi.insert(tree);
  363. oi.flush();
  364. return id;
  365. }
  366. }
  367. /**
  368. * A file is removed from the index but stays in the working directory. It
  369. * is checked if IndexDiff detects this file as removed and untracked.
  370. *
  371. * @throws Exception
  372. */
  373. @Test
  374. public void testRemovedUntracked() throws Exception{
  375. String path = "file";
  376. try (Git git = new Git(db)) {
  377. writeTrashFile(path, "content");
  378. git.add().addFilepattern(path).call();
  379. git.commit().setMessage("commit").call();
  380. }
  381. removeFromIndex(path);
  382. FileTreeIterator iterator = new FileTreeIterator(db);
  383. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  384. diff.diff();
  385. assertTrue(diff.getRemoved().contains(path));
  386. assertTrue(diff.getUntracked().contains(path));
  387. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  388. }
  389. /**
  390. *
  391. * @throws Exception
  392. */
  393. @Test
  394. public void testUntrackedFolders() throws Exception {
  395. try (Git git = new Git(db)) {
  396. IndexDiff diff = new IndexDiff(db, Constants.HEAD,
  397. new FileTreeIterator(db));
  398. diff.diff();
  399. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  400. writeTrashFile("readme", "");
  401. writeTrashFile("src/com/A.java", "");
  402. writeTrashFile("src/com/B.java", "");
  403. writeTrashFile("src/org/A.java", "");
  404. writeTrashFile("src/org/B.java", "");
  405. writeTrashFile("target/com/A.java", "");
  406. writeTrashFile("target/com/B.java", "");
  407. writeTrashFile("target/org/A.java", "");
  408. writeTrashFile("target/org/B.java", "");
  409. git.add().addFilepattern("src").addFilepattern("readme").call();
  410. git.commit().setMessage("initial").call();
  411. diff = new IndexDiff(db, Constants.HEAD,
  412. new FileTreeIterator(db));
  413. diff.diff();
  414. assertEquals(new HashSet<>(Arrays.asList("target")),
  415. diff.getUntrackedFolders());
  416. writeTrashFile("src/tst/A.java", "");
  417. writeTrashFile("src/tst/B.java", "");
  418. diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
  419. diff.diff();
  420. assertEquals(new HashSet<>(Arrays.asList("target", "src/tst")),
  421. diff.getUntrackedFolders());
  422. git.rm().addFilepattern("src/com/B.java").addFilepattern("src/org")
  423. .call();
  424. git.commit().setMessage("second").call();
  425. writeTrashFile("src/org/C.java", "");
  426. diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
  427. diff.diff();
  428. assertEquals(
  429. new HashSet<>(Arrays.asList("src/org", "src/tst",
  430. "target")),
  431. diff.getUntrackedFolders());
  432. }
  433. }
  434. /**
  435. * Test that ignored folders aren't listed as untracked, but are listed as
  436. * ignored.
  437. *
  438. * @throws Exception
  439. */
  440. @Test
  441. public void testUntrackedNotIgnoredFolders() throws Exception {
  442. try (Git git = new Git(db)) {
  443. IndexDiff diff = new IndexDiff(db, Constants.HEAD,
  444. new FileTreeIterator(db));
  445. diff.diff();
  446. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  447. writeTrashFile("readme", "");
  448. writeTrashFile("sr/com/X.java", "");
  449. writeTrashFile("src/com/A.java", "");
  450. writeTrashFile("src/org/B.java", "");
  451. writeTrashFile("srcs/org/Y.java", "");
  452. writeTrashFile("target/com/A.java", "");
  453. writeTrashFile("target/org/B.java", "");
  454. writeTrashFile(".gitignore", "/target\n/sr");
  455. git.add().addFilepattern("readme").addFilepattern(".gitignore")
  456. .addFilepattern("srcs/").call();
  457. git.commit().setMessage("initial").call();
  458. diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
  459. diff.diff();
  460. assertEquals(new HashSet<>(Arrays.asList("src")),
  461. diff.getUntrackedFolders());
  462. assertEquals(new HashSet<>(Arrays.asList("sr", "target")),
  463. diff.getIgnoredNotInIndex());
  464. git.add().addFilepattern("src").call();
  465. writeTrashFile("sr/com/X1.java", "");
  466. writeTrashFile("src/tst/A.java", "");
  467. writeTrashFile("src/tst/B.java", "");
  468. writeTrashFile("srcs/com/Y1.java", "");
  469. deleteTrashFile(".gitignore");
  470. diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
  471. diff.diff();
  472. assertEquals(
  473. new HashSet<>(Arrays.asList("srcs/com", "sr", "src/tst",
  474. "target")),
  475. diff.getUntrackedFolders());
  476. }
  477. }
  478. @Test
  479. public void testAssumeUnchanged() throws Exception {
  480. try (Git git = new Git(db)) {
  481. String path = "file";
  482. writeTrashFile(path, "content");
  483. git.add().addFilepattern(path).call();
  484. String path2 = "file2";
  485. writeTrashFile(path2, "content");
  486. String path3 = "file3";
  487. writeTrashFile(path3, "some content");
  488. git.add().addFilepattern(path2).addFilepattern(path3).call();
  489. git.commit().setMessage("commit").call();
  490. assumeUnchanged(path2);
  491. assumeUnchanged(path3);
  492. writeTrashFile(path, "more content");
  493. deleteTrashFile(path3);
  494. FileTreeIterator iterator = new FileTreeIterator(db);
  495. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  496. diff.diff();
  497. assertEquals(2, diff.getAssumeUnchanged().size());
  498. assertEquals(1, diff.getModified().size());
  499. assertEquals(0, diff.getChanged().size());
  500. assertTrue(diff.getAssumeUnchanged().contains("file2"));
  501. assertTrue(diff.getAssumeUnchanged().contains("file3"));
  502. assertTrue(diff.getModified().contains("file"));
  503. git.add().addFilepattern(".").call();
  504. iterator = new FileTreeIterator(db);
  505. diff = new IndexDiff(db, Constants.HEAD, iterator);
  506. diff.diff();
  507. assertEquals(2, diff.getAssumeUnchanged().size());
  508. assertEquals(0, diff.getModified().size());
  509. assertEquals(1, diff.getChanged().size());
  510. assertTrue(diff.getAssumeUnchanged().contains("file2"));
  511. assertTrue(diff.getAssumeUnchanged().contains("file3"));
  512. assertTrue(diff.getChanged().contains("file"));
  513. assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
  514. }
  515. }
  516. @Test
  517. public void testStageState() throws IOException {
  518. final int base = DirCacheEntry.STAGE_1;
  519. final int ours = DirCacheEntry.STAGE_2;
  520. final int theirs = DirCacheEntry.STAGE_3;
  521. verifyStageState(StageState.BOTH_DELETED, base);
  522. verifyStageState(StageState.DELETED_BY_THEM, ours, base);
  523. verifyStageState(StageState.DELETED_BY_US, base, theirs);
  524. verifyStageState(StageState.BOTH_MODIFIED, base, ours, theirs);
  525. verifyStageState(StageState.ADDED_BY_US, ours);
  526. verifyStageState(StageState.BOTH_ADDED, ours, theirs);
  527. verifyStageState(StageState.ADDED_BY_THEM, theirs);
  528. assertTrue(StageState.BOTH_DELETED.hasBase());
  529. assertFalse(StageState.BOTH_DELETED.hasOurs());
  530. assertFalse(StageState.BOTH_DELETED.hasTheirs());
  531. assertFalse(StageState.BOTH_ADDED.hasBase());
  532. assertTrue(StageState.BOTH_ADDED.hasOurs());
  533. assertTrue(StageState.BOTH_ADDED.hasTheirs());
  534. }
  535. @Test
  536. public void testStageState_mergeAndReset_bug() throws Exception {
  537. try (Git git = new Git(db)) {
  538. writeTrashFile("a", "content");
  539. git.add().addFilepattern("a").call();
  540. RevCommit initialCommit = git.commit().setMessage("initial commit")
  541. .call();
  542. // create branch and add a new file
  543. final String branchName = Constants.R_HEADS + "branch";
  544. createBranch(initialCommit, branchName);
  545. checkoutBranch(branchName);
  546. writeTrashFile("b", "second file content - branch");
  547. git.add().addFilepattern("b").call();
  548. RevCommit branchCommit = git.commit().setMessage("branch commit")
  549. .call();
  550. // checkout master and add the same new file
  551. checkoutBranch(Constants.R_HEADS + Constants.MASTER);
  552. writeTrashFile("b", "second file content - master");
  553. git.add().addFilepattern("b").call();
  554. git.commit().setMessage("master commit").call();
  555. // try and merge
  556. MergeResult result = git.merge().include(branchCommit).call();
  557. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  558. FileTreeIterator iterator = new FileTreeIterator(db);
  559. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  560. diff.diff();
  561. assertTrue(diff.getChanged().isEmpty());
  562. assertTrue(diff.getAdded().isEmpty());
  563. assertTrue(diff.getRemoved().isEmpty());
  564. assertTrue(diff.getMissing().isEmpty());
  565. assertTrue(diff.getModified().isEmpty());
  566. assertEquals(1, diff.getConflicting().size());
  567. assertTrue(diff.getConflicting().contains("b"));
  568. assertEquals(StageState.BOTH_ADDED, diff.getConflictingStageStates()
  569. .get("b"));
  570. assertTrue(diff.getUntrackedFolders().isEmpty());
  571. // reset file b to its master state without altering the index
  572. writeTrashFile("b", "second file content - master");
  573. // we should have the same result
  574. iterator = new FileTreeIterator(db);
  575. diff = new IndexDiff(db, Constants.HEAD, iterator);
  576. diff.diff();
  577. assertTrue(diff.getChanged().isEmpty());
  578. assertTrue(diff.getAdded().isEmpty());
  579. assertTrue(diff.getRemoved().isEmpty());
  580. assertTrue(diff.getMissing().isEmpty());
  581. assertTrue(diff.getModified().isEmpty());
  582. assertEquals(1, diff.getConflicting().size());
  583. assertTrue(diff.getConflicting().contains("b"));
  584. assertEquals(StageState.BOTH_ADDED, diff.getConflictingStageStates()
  585. .get("b"));
  586. assertTrue(diff.getUntrackedFolders().isEmpty());
  587. }
  588. }
  589. @Test
  590. public void testStageState_simulated_bug() throws Exception {
  591. try (Git git = new Git(db)) {
  592. writeTrashFile("a", "content");
  593. git.add().addFilepattern("a").call();
  594. RevCommit initialCommit = git.commit().setMessage("initial commit")
  595. .call();
  596. // create branch and add a new file
  597. final String branchName = Constants.R_HEADS + "branch";
  598. createBranch(initialCommit, branchName);
  599. checkoutBranch(branchName);
  600. writeTrashFile("b", "second file content - branch");
  601. git.add().addFilepattern("b").call();
  602. git.commit().setMessage("branch commit")
  603. .call();
  604. // checkout master and add the same new file
  605. checkoutBranch(Constants.R_HEADS + Constants.MASTER);
  606. writeTrashFile("b", "second file content - master");
  607. git.add().addFilepattern("b").call();
  608. git.commit().setMessage("master commit").call();
  609. // Simulate a failed merge of branch into master
  610. DirCacheBuilder builder = db.lockDirCache().builder();
  611. DirCacheEntry entry = createEntry("a", FileMode.REGULAR_FILE, 0,
  612. "content");
  613. builder.add(entry);
  614. entry = createEntry("b", FileMode.REGULAR_FILE, 2,
  615. "second file content - master");
  616. builder.add(entry);
  617. entry = createEntry("b", FileMode.REGULAR_FILE, 3,
  618. "second file content - branch");
  619. builder.add(entry);
  620. builder.commit();
  621. FileTreeIterator iterator = new FileTreeIterator(db);
  622. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  623. diff.diff();
  624. assertTrue(diff.getChanged().isEmpty());
  625. assertTrue(diff.getAdded().isEmpty());
  626. assertTrue(diff.getRemoved().isEmpty());
  627. assertTrue(diff.getMissing().isEmpty());
  628. assertTrue(diff.getModified().isEmpty());
  629. assertEquals(1, diff.getConflicting().size());
  630. assertTrue(diff.getConflicting().contains("b"));
  631. assertEquals(StageState.BOTH_ADDED, diff.getConflictingStageStates()
  632. .get("b"));
  633. assertTrue(diff.getUntrackedFolders().isEmpty());
  634. }
  635. }
  636. @Test
  637. public void testAutoCRLFInput() throws Exception {
  638. try (Git git = new Git(db)) {
  639. FileBasedConfig config = db.getConfig();
  640. // Make sure core.autocrlf is false before adding
  641. config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
  642. ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.FALSE);
  643. config.save();
  644. // File is already in repository with CRLF
  645. writeTrashFile("crlf.txt", "this\r\ncontains\r\ncrlf\r\n");
  646. git.add().addFilepattern("crlf.txt").call();
  647. git.commit().setMessage("Add crlf.txt").call();
  648. // Now set core.autocrlf to input
  649. config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
  650. ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.INPUT);
  651. config.save();
  652. FileTreeIterator iterator = new FileTreeIterator(db);
  653. IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
  654. diff.diff();
  655. assertTrue(
  656. "Expected no modified files, but there were: "
  657. + diff.getModified(), diff.getModified().isEmpty());
  658. }
  659. }
  660. private void verifyStageState(StageState expected, int... stages)
  661. throws IOException {
  662. DirCacheBuilder builder = db.lockDirCache().builder();
  663. for (int stage : stages) {
  664. DirCacheEntry entry = createEntry("a", FileMode.REGULAR_FILE,
  665. stage, "content");
  666. builder.add(entry);
  667. }
  668. builder.commit();
  669. IndexDiff diff = new IndexDiff(db, Constants.HEAD,
  670. new FileTreeIterator(db));
  671. diff.diff();
  672. assertEquals(
  673. "Conflict for entries in stages " + Arrays.toString(stages),
  674. expected, diff.getConflictingStageStates().get("a"));
  675. }
  676. private void removeFromIndex(String path) throws IOException {
  677. final DirCache dirc = db.lockDirCache();
  678. final DirCacheEditor edit = dirc.editor();
  679. edit.add(new DirCacheEditor.DeletePath(path));
  680. if (!edit.commit())
  681. throw new IOException("could not commit");
  682. }
  683. private void assumeUnchanged(String path) throws IOException {
  684. final DirCache dirc = db.lockDirCache();
  685. final DirCacheEntry ent = dirc.getEntry(path);
  686. if (ent != null)
  687. ent.setAssumeValid(true);
  688. dirc.write();
  689. if (!dirc.commit())
  690. throw new IOException("could not commit");
  691. }
  692. }