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.

DiffEntryTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * Copyright (C) 2011, 2013 Dariusz Luksza <dariusz@luksza.org>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.diff;
  44. import static org.eclipse.jgit.diff.DiffEntry.DEV_NULL;
  45. import static org.eclipse.jgit.util.FileUtils.delete;
  46. import static org.hamcrest.CoreMatchers.is;
  47. import static org.hamcrest.CoreMatchers.notNullValue;
  48. import static org.junit.Assert.assertEquals;
  49. import static org.junit.Assert.assertFalse;
  50. import static org.junit.Assert.assertThat;
  51. import static org.junit.Assert.assertTrue;
  52. import java.io.File;
  53. import java.util.List;
  54. import org.eclipse.jgit.api.Git;
  55. import org.eclipse.jgit.diff.DiffEntry.ChangeType;
  56. import org.eclipse.jgit.dircache.DirCache;
  57. import org.eclipse.jgit.dircache.DirCacheEditor;
  58. import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
  59. import org.eclipse.jgit.internal.storage.file.FileRepository;
  60. import org.eclipse.jgit.dircache.DirCacheEntry;
  61. import org.eclipse.jgit.junit.JGitTestUtil;
  62. import org.eclipse.jgit.junit.RepositoryTestCase;
  63. import org.eclipse.jgit.lib.FileMode;
  64. import org.eclipse.jgit.lib.Repository;
  65. import org.eclipse.jgit.revwalk.RevCommit;
  66. import org.eclipse.jgit.treewalk.EmptyTreeIterator;
  67. import org.eclipse.jgit.treewalk.FileTreeIterator;
  68. import org.eclipse.jgit.treewalk.TreeWalk;
  69. import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
  70. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  71. import org.eclipse.jgit.util.FileUtils;
  72. import org.junit.Test;
  73. public class DiffEntryTest extends RepositoryTestCase {
  74. @Test
  75. public void shouldListAddedFileInInitialCommit() throws Exception {
  76. // given
  77. writeTrashFile("a.txt", "content");
  78. try (Git git = new Git(db);
  79. TreeWalk walk = new TreeWalk(db)) {
  80. git.add().addFilepattern("a.txt").call();
  81. RevCommit c = git.commit().setMessage("initial commit").call();
  82. // when
  83. walk.addTree(new EmptyTreeIterator());
  84. walk.addTree(c.getTree());
  85. List<DiffEntry> result = DiffEntry.scan(walk);
  86. // then
  87. assertThat(result, notNullValue());
  88. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
  89. DiffEntry entry = result.get(0);
  90. assertThat(entry.getChangeType(), is(ChangeType.ADD));
  91. assertThat(entry.getNewPath(), is("a.txt"));
  92. assertThat(entry.getOldPath(), is(DEV_NULL));
  93. }
  94. }
  95. @Test
  96. public void shouldListAddedFileBetweenTwoCommits() throws Exception {
  97. // given
  98. try (Git git = new Git(db);
  99. TreeWalk walk = new TreeWalk(db)) {
  100. RevCommit c1 = git.commit().setMessage("initial commit").call();
  101. writeTrashFile("a.txt", "content");
  102. git.add().addFilepattern("a.txt").call();
  103. RevCommit c2 = git.commit().setMessage("second commit").call();
  104. // when
  105. walk.addTree(c1.getTree());
  106. walk.addTree(c2.getTree());
  107. List<DiffEntry> result = DiffEntry.scan(walk);
  108. // then
  109. assertThat(result, notNullValue());
  110. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
  111. DiffEntry entry = result.get(0);
  112. assertThat(entry.getChangeType(), is(ChangeType.ADD));
  113. assertThat(entry.getNewPath(), is("a.txt"));
  114. assertThat(entry.getOldPath(), is(DEV_NULL));
  115. }
  116. }
  117. @Test
  118. public void shouldListModificationBetweenTwoCommits() throws Exception {
  119. // given
  120. try (Git git = new Git(db);
  121. TreeWalk walk = new TreeWalk(db)) {
  122. File file = writeTrashFile("a.txt", "content");
  123. git.add().addFilepattern("a.txt").call();
  124. RevCommit c1 = git.commit().setMessage("initial commit").call();
  125. write(file, "new content");
  126. RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
  127. .call();
  128. // when
  129. walk.addTree(c1.getTree());
  130. walk.addTree(c2.getTree());
  131. List<DiffEntry> result = DiffEntry.scan(walk);
  132. // then
  133. assertThat(result, notNullValue());
  134. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
  135. DiffEntry entry = result.get(0);
  136. assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
  137. assertThat(entry.getNewPath(), is("a.txt"));
  138. }
  139. }
  140. @Test
  141. public void shouldListDeletionBetweenTwoCommits() throws Exception {
  142. // given
  143. try (Git git = new Git(db);
  144. TreeWalk walk = new TreeWalk(db)) {
  145. File file = writeTrashFile("a.txt", "content");
  146. git.add().addFilepattern("a.txt").call();
  147. RevCommit c1 = git.commit().setMessage("initial commit").call();
  148. delete(file);
  149. RevCommit c2 = git.commit().setAll(true).setMessage("delete a.txt")
  150. .call();
  151. // when
  152. walk.addTree(c1.getTree());
  153. walk.addTree(c2.getTree());
  154. List<DiffEntry> result = DiffEntry.scan(walk);
  155. // then
  156. assertThat(result, notNullValue());
  157. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
  158. DiffEntry entry = result.get(0);
  159. assertThat(entry.getOldPath(), is("a.txt"));
  160. assertThat(entry.getNewPath(), is(DEV_NULL));
  161. assertThat(entry.getChangeType(), is(ChangeType.DELETE));
  162. }
  163. }
  164. @Test
  165. public void shouldListModificationInDirWithoutModifiedTrees()
  166. throws Exception {
  167. // given
  168. try (Git git = new Git(db);
  169. TreeWalk walk = new TreeWalk(db)) {
  170. File tree = new File(new File(db.getWorkTree(), "a"), "b");
  171. FileUtils.mkdirs(tree);
  172. File file = new File(tree, "c.txt");
  173. FileUtils.createNewFile(file);
  174. write(file, "content");
  175. git.add().addFilepattern("a").call();
  176. RevCommit c1 = git.commit().setMessage("initial commit").call();
  177. write(file, "new line");
  178. RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
  179. .call();
  180. // when
  181. walk.addTree(c1.getTree());
  182. walk.addTree(c2.getTree());
  183. walk.setRecursive(true);
  184. List<DiffEntry> result = DiffEntry.scan(walk);
  185. // then
  186. assertThat(result, notNullValue());
  187. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
  188. DiffEntry entry = result.get(0);
  189. assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
  190. assertThat(entry.getNewPath(), is("a/b/c.txt"));
  191. }
  192. }
  193. @Test
  194. public void shouldListModificationInDirWithModifiedTrees() throws Exception {
  195. // given
  196. try (Git git = new Git(db);
  197. TreeWalk walk = new TreeWalk(db)) {
  198. File tree = new File(new File(db.getWorkTree(), "a"), "b");
  199. FileUtils.mkdirs(tree);
  200. File file = new File(tree, "c.txt");
  201. FileUtils.createNewFile(file);
  202. write(file, "content");
  203. git.add().addFilepattern("a").call();
  204. RevCommit c1 = git.commit().setMessage("initial commit").call();
  205. write(file, "new line");
  206. RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
  207. .call();
  208. // when
  209. walk.addTree(c1.getTree());
  210. walk.addTree(c2.getTree());
  211. List<DiffEntry> result = DiffEntry.scan(walk, true);
  212. // then
  213. assertThat(result, notNullValue());
  214. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(3)));
  215. DiffEntry entry = result.get(0);
  216. assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
  217. assertThat(entry.getNewPath(), is("a"));
  218. entry = result.get(1);
  219. assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
  220. assertThat(entry.getNewPath(), is("a/b"));
  221. entry = result.get(2);
  222. assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
  223. assertThat(entry.getNewPath(), is("a/b/c.txt"));
  224. }
  225. }
  226. @Test
  227. public void shouldListChangesInWorkingTree() throws Exception {
  228. // given
  229. writeTrashFile("a.txt", "content");
  230. try (Git git = new Git(db);
  231. TreeWalk walk = new TreeWalk(db)) {
  232. git.add().addFilepattern("a.txt").call();
  233. RevCommit c = git.commit().setMessage("initial commit").call();
  234. writeTrashFile("b.txt", "new line");
  235. // when
  236. walk.addTree(c.getTree());
  237. walk.addTree(new FileTreeIterator(db));
  238. List<DiffEntry> result = DiffEntry.scan(walk, true);
  239. // then
  240. assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
  241. DiffEntry entry = result.get(0);
  242. assertThat(entry.getChangeType(), is(ChangeType.ADD));
  243. assertThat(entry.getNewPath(), is("b.txt"));
  244. }
  245. }
  246. @Test
  247. public void shouldMarkEntriesWhenGivenMarkTreeFilter() throws Exception {
  248. // given
  249. try (Git git = new Git(db);
  250. TreeWalk walk = new TreeWalk(db)) {
  251. RevCommit c1 = git.commit().setMessage("initial commit").call();
  252. FileUtils.mkdir(new File(db.getWorkTree(), "b"));
  253. writeTrashFile("a.txt", "a");
  254. writeTrashFile("b/1.txt", "b1");
  255. writeTrashFile("b/2.txt", "b2");
  256. writeTrashFile("c.txt", "c");
  257. git.add().addFilepattern("a.txt").addFilepattern("b")
  258. .addFilepattern("c.txt").call();
  259. RevCommit c2 = git.commit().setMessage("second commit").call();
  260. TreeFilter filterA = PathFilterGroup.createFromStrings("a.txt");
  261. TreeFilter filterB = PathFilterGroup.createFromStrings("b");
  262. TreeFilter filterB2 = PathFilterGroup.createFromStrings("b/2.txt");
  263. // when
  264. walk.addTree(c1.getTree());
  265. walk.addTree(c2.getTree());
  266. List<DiffEntry> result = DiffEntry.scan(walk, true, new TreeFilter[] {
  267. filterA, filterB, filterB2 });
  268. // then
  269. assertThat(result, notNullValue());
  270. assertEquals(5, result.size());
  271. DiffEntry entryA = result.get(0);
  272. DiffEntry entryB = result.get(1);
  273. DiffEntry entryB1 = result.get(2);
  274. DiffEntry entryB2 = result.get(3);
  275. DiffEntry entryC = result.get(4);
  276. assertThat(entryA.getNewPath(), is("a.txt"));
  277. assertTrue(entryA.isMarked(0));
  278. assertFalse(entryA.isMarked(1));
  279. assertFalse(entryA.isMarked(2));
  280. assertEquals(1, entryA.getTreeFilterMarks());
  281. assertThat(entryB.getNewPath(), is("b"));
  282. assertFalse(entryB.isMarked(0));
  283. assertTrue(entryB.isMarked(1));
  284. assertTrue(entryB.isMarked(2));
  285. assertEquals(6, entryB.getTreeFilterMarks());
  286. assertThat(entryB1.getNewPath(), is("b/1.txt"));
  287. assertFalse(entryB1.isMarked(0));
  288. assertTrue(entryB1.isMarked(1));
  289. assertFalse(entryB1.isMarked(2));
  290. assertEquals(2, entryB1.getTreeFilterMarks());
  291. assertThat(entryB2.getNewPath(), is("b/2.txt"));
  292. assertFalse(entryB2.isMarked(0));
  293. assertTrue(entryB2.isMarked(1));
  294. assertTrue(entryB2.isMarked(2));
  295. assertEquals(6, entryB2.getTreeFilterMarks());
  296. assertThat(entryC.getNewPath(), is("c.txt"));
  297. assertFalse(entryC.isMarked(0));
  298. assertFalse(entryC.isMarked(1));
  299. assertFalse(entryC.isMarked(2));
  300. assertEquals(0, entryC.getTreeFilterMarks());
  301. }
  302. }
  303. @Test(expected = IllegalArgumentException.class)
  304. public void shouldThrowIAEWhenTreeWalkHasLessThanTwoTrees()
  305. throws Exception {
  306. // given - we don't need anything here
  307. // when
  308. try (TreeWalk walk = new TreeWalk(db)) {
  309. walk.addTree(new EmptyTreeIterator());
  310. DiffEntry.scan(walk);
  311. }
  312. }
  313. @Test(expected = IllegalArgumentException.class)
  314. public void shouldThrowIAEWhenTreeWalkHasMoreThanTwoTrees()
  315. throws Exception {
  316. // given - we don't need anything here
  317. // when
  318. try (TreeWalk walk = new TreeWalk(db)) {
  319. walk.addTree(new EmptyTreeIterator());
  320. walk.addTree(new EmptyTreeIterator());
  321. walk.addTree(new EmptyTreeIterator());
  322. DiffEntry.scan(walk);
  323. }
  324. }
  325. @Test(expected = IllegalArgumentException.class)
  326. public void shouldThrowIAEWhenScanShouldIncludeTreesAndWalkIsRecursive()
  327. throws Exception {
  328. // given - we don't need anything here
  329. // when
  330. try (TreeWalk walk = new TreeWalk(db)) {
  331. walk.addTree(new EmptyTreeIterator());
  332. walk.addTree(new EmptyTreeIterator());
  333. walk.setRecursive(true);
  334. DiffEntry.scan(walk, true);
  335. }
  336. }
  337. @Test
  338. public void shouldReportFileModeChange() throws Exception {
  339. writeTrashFile("a.txt", "content");
  340. try (Git git = new Git(db);
  341. TreeWalk walk = new TreeWalk(db)) {
  342. git.add().addFilepattern("a.txt").call();
  343. RevCommit c1 = git.commit().setMessage("initial commit").call();
  344. DirCache cache = db.lockDirCache();
  345. DirCacheEditor editor = cache.editor();
  346. walk.addTree(c1.getTree());
  347. walk.setRecursive(true);
  348. assertTrue(walk.next());
  349. editor.add(new PathEdit("a.txt") {
  350. @Override
  351. public void apply(DirCacheEntry ent) {
  352. ent.setFileMode(FileMode.EXECUTABLE_FILE);
  353. ent.setObjectId(walk.getObjectId(0));
  354. }
  355. });
  356. assertTrue(editor.commit());
  357. RevCommit c2 = git.commit().setMessage("second commit").call();
  358. walk.reset();
  359. walk.addTree(c1.getTree());
  360. walk.addTree(c2.getTree());
  361. List<DiffEntry> diffs = DiffEntry.scan(walk, false);
  362. assertEquals(1, diffs.size());
  363. DiffEntry diff = diffs.get(0);
  364. assertEquals(ChangeType.MODIFY,diff.getChangeType());
  365. assertEquals(diff.getOldId(), diff.getNewId());
  366. assertEquals("a.txt", diff.getOldPath());
  367. assertEquals(diff.getOldPath(), diff.getNewPath());
  368. assertEquals(FileMode.EXECUTABLE_FILE, diff.getNewMode());
  369. assertEquals(FileMode.REGULAR_FILE, diff.getOldMode());
  370. }
  371. }
  372. @Test
  373. public void shouldReportSubmoduleReplacedByFileMove() throws Exception {
  374. // Create a submodule
  375. FileRepository submoduleStandalone = createWorkRepository();
  376. JGitTestUtil.writeTrashFile(submoduleStandalone, "fileInSubmodule",
  377. "submodule");
  378. Git submoduleStandaloneGit = Git.wrap(submoduleStandalone);
  379. submoduleStandaloneGit.add().addFilepattern("fileInSubmodule").call();
  380. submoduleStandaloneGit.commit().setMessage("add file to submodule")
  381. .call();
  382. Repository submodule_db = Git.wrap(db).submoduleAdd()
  383. .setPath("modules/submodule")
  384. .setURI(submoduleStandalone.getDirectory().toURI().toString())
  385. .call();
  386. File submodule_trash = submodule_db.getWorkTree();
  387. addRepoToClose(submodule_db);
  388. writeTrashFile("fileInRoot", "root");
  389. Git rootGit = Git.wrap(db);
  390. rootGit.add().addFilepattern("fileInRoot").call();
  391. rootGit.commit().setMessage("add submodule and root file").call();
  392. // Dummy change on fileInRoot
  393. writeTrashFile("fileInRoot", "changed");
  394. rootGit.add().addFilepattern("fileInRoot").call();
  395. RevCommit firstCommit = rootGit.commit().setMessage("change root file")
  396. .call();
  397. // Remove the submodule again and move fileInRoot into that subfolder
  398. rootGit.rm().setCached(true).addFilepattern("modules/submodule").call();
  399. recursiveDelete(submodule_trash);
  400. JGitTestUtil.deleteTrashFile(db, "fileInRoot");
  401. // Move the fileInRoot file
  402. writeTrashFile("modules/submodule/fileInRoot", "changed");
  403. rootGit.rm().addFilepattern("fileInRoot").addFilepattern("modules/")
  404. .call();
  405. rootGit.add().addFilepattern("modules/").call();
  406. RevCommit secondCommit = rootGit.commit()
  407. .setMessage("remove submodule and move root file")
  408. .call();
  409. // Diff should report submodule having been deleted and file moved
  410. // (deleted and added)
  411. try (TreeWalk walk = new TreeWalk(db)) {
  412. walk.addTree(firstCommit.getTree());
  413. walk.addTree(secondCommit.getTree());
  414. walk.setRecursive(true);
  415. List<DiffEntry> diffs = DiffEntry.scan(walk);
  416. assertEquals(3, diffs.size());
  417. DiffEntry e = diffs.get(0);
  418. assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType());
  419. assertEquals("fileInRoot", e.getOldPath());
  420. e = diffs.get(1);
  421. assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType());
  422. assertEquals("modules/submodule", e.getOldPath());
  423. assertEquals(FileMode.GITLINK, e.getOldMode());
  424. e = diffs.get(2);
  425. assertEquals(DiffEntry.ChangeType.ADD, e.getChangeType());
  426. assertEquals("modules/submodule/fileInRoot", e.getNewPath());
  427. }
  428. }
  429. }