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.

ResolveMergerTest.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. /*
  2. * Copyright (C) 2012, Robin Stocker <robin@nibor.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.merge;
  44. import static org.junit.Assert.assertEquals;
  45. import static org.junit.Assert.assertFalse;
  46. import static org.junit.Assert.assertTrue;
  47. import java.io.File;
  48. import java.io.FileInputStream;
  49. import java.io.IOException;
  50. import org.eclipse.jgit.api.Git;
  51. import org.eclipse.jgit.api.MergeResult;
  52. import org.eclipse.jgit.api.MergeResult.MergeStatus;
  53. import org.eclipse.jgit.api.errors.CheckoutConflictException;
  54. import org.eclipse.jgit.dircache.DirCache;
  55. import org.eclipse.jgit.lib.RepositoryTestCase;
  56. import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
  57. import org.eclipse.jgit.revwalk.RevCommit;
  58. import org.eclipse.jgit.treewalk.FileTreeIterator;
  59. import org.eclipse.jgit.util.FileUtils;
  60. import org.junit.Assert;
  61. import org.junit.Test;
  62. public class ResolveMergerTest extends RepositoryTestCase {
  63. @Test
  64. public void failingPathsShouldNotResultInOKReturnValue() throws Exception {
  65. File folder1 = new File(db.getWorkTree(), "folder1");
  66. FileUtils.mkdir(folder1);
  67. File file = new File(folder1, "file1.txt");
  68. write(file, "folder1--file1.txt");
  69. file = new File(folder1, "file2.txt");
  70. write(file, "folder1--file2.txt");
  71. Git git = new Git(db);
  72. git.add().addFilepattern(folder1.getName()).call();
  73. RevCommit base = git.commit().setMessage("adding folder").call();
  74. recursiveDelete(folder1);
  75. git.rm().addFilepattern("folder1/file1.txt")
  76. .addFilepattern("folder1/file2.txt").call();
  77. RevCommit other = git.commit()
  78. .setMessage("removing folders on 'other'").call();
  79. git.checkout().setName(base.name()).call();
  80. file = new File(db.getWorkTree(), "unrelated.txt");
  81. write(file, "unrelated");
  82. git.add().addFilepattern("unrelated").call();
  83. RevCommit head = git.commit().setMessage("Adding another file").call();
  84. // Untracked file to cause failing path for delete() of folder1
  85. file = new File(folder1, "file3.txt");
  86. write(file, "folder1--file3.txt");
  87. ResolveMerger merger = new ResolveMerger(db, false);
  88. merger.setCommitNames(new String[] { "BASE", "HEAD", "other" });
  89. merger.setWorkingTreeIterator(new FileTreeIterator(db));
  90. boolean ok = merger.merge(head.getId(), other.getId());
  91. assertFalse(merger.getFailingPaths().isEmpty());
  92. assertFalse(ok);
  93. }
  94. /**
  95. * Merging two conflicting subtrees when the index does not contain any file
  96. * in that subtree should lead to a conflicting state.
  97. *
  98. * @throws Exception
  99. */
  100. @Test
  101. public void checkMergeConflictingTreesWithoutIndex() throws Exception {
  102. Git git = Git.wrap(db);
  103. writeTrashFile("d/1", "orig");
  104. git.add().addFilepattern("d/1").call();
  105. RevCommit first = git.commit().setMessage("added d/1").call();
  106. writeTrashFile("d/1", "master");
  107. RevCommit masterCommit = git.commit().setAll(true)
  108. .setMessage("modified d/1 on master").call();
  109. git.checkout().setCreateBranch(true).setStartPoint(first)
  110. .setName("side").call();
  111. writeTrashFile("d/1", "side");
  112. git.commit().setAll(true).setMessage("modified d/1 on side").call();
  113. git.rm().addFilepattern("d/1").call();
  114. git.rm().addFilepattern("d").call();
  115. MergeResult mergeRes = git.merge().include(masterCommit).call();
  116. assertTrue(MergeStatus.CONFLICTING.equals(mergeRes.getMergeStatus()));
  117. assertEquals(
  118. "[d/1, mode:100644, stage:1, content:orig][d/1, mode:100644, stage:2, content:side][d/1, mode:100644, stage:3, content:master]",
  119. indexState(CONTENT));
  120. }
  121. /**
  122. * Merging two different but mergeable subtrees when the index does not
  123. * contain any file in that subtree should lead to a merged state.
  124. *
  125. * @throws Exception
  126. */
  127. @Test
  128. public void checkMergeMergeableTreesWithoutIndex() throws Exception {
  129. Git git = Git.wrap(db);
  130. writeTrashFile("d/1", "1\n2\n3");
  131. git.add().addFilepattern("d/1").call();
  132. RevCommit first = git.commit().setMessage("added d/1").call();
  133. writeTrashFile("d/1", "1master\n2\n3");
  134. RevCommit masterCommit = git.commit().setAll(true)
  135. .setMessage("modified d/1 on master").call();
  136. git.checkout().setCreateBranch(true).setStartPoint(first)
  137. .setName("side").call();
  138. writeTrashFile("d/1", "1\n2\n3side");
  139. git.commit().setAll(true).setMessage("modified d/1 on side").call();
  140. git.rm().addFilepattern("d/1").call();
  141. git.rm().addFilepattern("d").call();
  142. MergeResult mergeRes = git.merge().include(masterCommit).call();
  143. assertTrue(MergeStatus.MERGED.equals(mergeRes.getMergeStatus()));
  144. assertEquals("[d/1, mode:100644, content:1master\n2\n3side\n]",
  145. indexState(CONTENT));
  146. }
  147. /**
  148. * Merging two equal subtrees when the index does not contain any file in
  149. * that subtree should lead to a merged state.
  150. *
  151. * @throws Exception
  152. */
  153. @Test
  154. public void checkMergeEqualTreesWithoutIndex() throws Exception {
  155. Git git = Git.wrap(db);
  156. writeTrashFile("d/1", "orig");
  157. git.add().addFilepattern("d/1").call();
  158. RevCommit first = git.commit().setMessage("added d/1").call();
  159. writeTrashFile("d/1", "modified");
  160. RevCommit masterCommit = git.commit().setAll(true)
  161. .setMessage("modified d/1 on master").call();
  162. git.checkout().setCreateBranch(true).setStartPoint(first)
  163. .setName("side").call();
  164. writeTrashFile("d/1", "modified");
  165. git.commit().setAll(true).setMessage("modified d/1 on side").call();
  166. git.rm().addFilepattern("d/1").call();
  167. git.rm().addFilepattern("d").call();
  168. MergeResult mergeRes = git.merge().include(masterCommit).call();
  169. assertTrue(MergeStatus.MERGED.equals(mergeRes.getMergeStatus()));
  170. assertEquals("[d/1, mode:100644, content:modified]",
  171. indexState(CONTENT));
  172. }
  173. /**
  174. * Merging two equal subtrees with an incore merger should lead to a merged
  175. * state (The 'Gerrit' use case).
  176. *
  177. * @throws Exception
  178. */
  179. @Test
  180. public void checkMergeEqualTreesInCore() throws Exception {
  181. Git git = Git.wrap(db);
  182. writeTrashFile("d/1", "orig");
  183. git.add().addFilepattern("d/1").call();
  184. RevCommit first = git.commit().setMessage("added d/1").call();
  185. writeTrashFile("d/1", "modified");
  186. RevCommit masterCommit = git.commit().setAll(true)
  187. .setMessage("modified d/1 on master").call();
  188. git.checkout().setCreateBranch(true).setStartPoint(first)
  189. .setName("side").call();
  190. writeTrashFile("d/1", "modified");
  191. RevCommit sideCommit = git.commit().setAll(true)
  192. .setMessage("modified d/1 on side").call();
  193. git.rm().addFilepattern("d/1").call();
  194. git.rm().addFilepattern("d").call();
  195. ThreeWayMerger resolveMerger = MergeStrategy.RESOLVE
  196. .newMerger(db, true);
  197. boolean noProblems = resolveMerger.merge(masterCommit, sideCommit);
  198. assertTrue(noProblems);
  199. }
  200. /**
  201. * Merging two equal subtrees when the index and HEAD does not contain any
  202. * file in that subtree should lead to a merged state.
  203. *
  204. * @throws Exception
  205. */
  206. @Test
  207. public void checkMergeEqualNewTrees() throws Exception {
  208. Git git = Git.wrap(db);
  209. writeTrashFile("2", "orig");
  210. git.add().addFilepattern("2").call();
  211. RevCommit first = git.commit().setMessage("added 2").call();
  212. writeTrashFile("d/1", "orig");
  213. git.add().addFilepattern("d/1").call();
  214. RevCommit masterCommit = git.commit().setAll(true)
  215. .setMessage("added d/1 on master").call();
  216. git.checkout().setCreateBranch(true).setStartPoint(first)
  217. .setName("side").call();
  218. writeTrashFile("d/1", "orig");
  219. git.add().addFilepattern("d/1").call();
  220. git.commit().setAll(true).setMessage("added d/1 on side").call();
  221. git.rm().addFilepattern("d/1").call();
  222. git.rm().addFilepattern("d").call();
  223. MergeResult mergeRes = git.merge().include(masterCommit).call();
  224. assertTrue(MergeStatus.MERGED.equals(mergeRes.getMergeStatus()));
  225. assertEquals(
  226. "[2, mode:100644, content:orig][d/1, mode:100644, content:orig]",
  227. indexState(CONTENT));
  228. }
  229. /**
  230. * Merging two conflicting subtrees when the index and HEAD does not contain
  231. * any file in that subtree should lead to a conflicting state.
  232. *
  233. * @throws Exception
  234. */
  235. @Test
  236. public void checkMergeConflictingNewTrees() throws Exception {
  237. Git git = Git.wrap(db);
  238. writeTrashFile("2", "orig");
  239. git.add().addFilepattern("2").call();
  240. RevCommit first = git.commit().setMessage("added 2").call();
  241. writeTrashFile("d/1", "master");
  242. git.add().addFilepattern("d/1").call();
  243. RevCommit masterCommit = git.commit().setAll(true)
  244. .setMessage("added d/1 on master").call();
  245. git.checkout().setCreateBranch(true).setStartPoint(first)
  246. .setName("side").call();
  247. writeTrashFile("d/1", "side");
  248. git.add().addFilepattern("d/1").call();
  249. git.commit().setAll(true).setMessage("added d/1 on side").call();
  250. git.rm().addFilepattern("d/1").call();
  251. git.rm().addFilepattern("d").call();
  252. MergeResult mergeRes = git.merge().include(masterCommit).call();
  253. assertTrue(MergeStatus.CONFLICTING.equals(mergeRes.getMergeStatus()));
  254. assertEquals(
  255. "[2, mode:100644, content:orig][d/1, mode:100644, stage:2, content:side][d/1, mode:100644, stage:3, content:master]",
  256. indexState(CONTENT));
  257. }
  258. /**
  259. * Merging two conflicting files when the index contains a tree for that
  260. * path should lead to a failed state.
  261. *
  262. * @throws Exception
  263. */
  264. @Test
  265. public void checkMergeConflictingFilesWithTreeInIndex() throws Exception {
  266. Git git = Git.wrap(db);
  267. writeTrashFile("0", "orig");
  268. git.add().addFilepattern("0").call();
  269. RevCommit first = git.commit().setMessage("added 0").call();
  270. writeTrashFile("0", "master");
  271. RevCommit masterCommit = git.commit().setAll(true)
  272. .setMessage("modified 0 on master").call();
  273. git.checkout().setCreateBranch(true).setStartPoint(first)
  274. .setName("side").call();
  275. writeTrashFile("0", "side");
  276. git.commit().setAll(true).setMessage("modified 0 on side").call();
  277. git.rm().addFilepattern("0").call();
  278. writeTrashFile("0/0", "side");
  279. git.add().addFilepattern("0/0").call();
  280. MergeResult mergeRes = git.merge().include(masterCommit).call();
  281. assertEquals(MergeStatus.FAILED, mergeRes.getMergeStatus());
  282. }
  283. /**
  284. * Merging two equal files when the index contains a tree for that path
  285. * should lead to a failed state.
  286. *
  287. * @throws Exception
  288. */
  289. @Test
  290. public void checkMergeMergeableFilesWithTreeInIndex() throws Exception {
  291. Git git = Git.wrap(db);
  292. writeTrashFile("0", "orig");
  293. writeTrashFile("1", "1\n2\n3");
  294. git.add().addFilepattern("0").addFilepattern("1").call();
  295. RevCommit first = git.commit().setMessage("added 0, 1").call();
  296. writeTrashFile("1", "1master\n2\n3");
  297. RevCommit masterCommit = git.commit().setAll(true)
  298. .setMessage("modified 1 on master").call();
  299. git.checkout().setCreateBranch(true).setStartPoint(first)
  300. .setName("side").call();
  301. writeTrashFile("1", "1\n2\n3side");
  302. git.commit().setAll(true).setMessage("modified 1 on side").call();
  303. git.rm().addFilepattern("0").call();
  304. writeTrashFile("0/0", "modified");
  305. git.add().addFilepattern("0/0").call();
  306. try {
  307. git.merge().include(masterCommit).call();
  308. Assert.fail("Didn't get the expected exception");
  309. } catch (CheckoutConflictException e) {
  310. assertEquals(1, e.getConflictingPaths().size());
  311. assertEquals("0/0", e.getConflictingPaths().get(0));
  312. }
  313. }
  314. @Test
  315. public void checkLockedFilesToBeDeleted() throws Exception {
  316. Git git = Git.wrap(db);
  317. writeTrashFile("a.txt", "orig");
  318. writeTrashFile("b.txt", "orig");
  319. git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
  320. RevCommit first = git.commit().setMessage("added a.txt, b.txt").call();
  321. // modify and delete files on the master branch
  322. writeTrashFile("a.txt", "master");
  323. git.rm().addFilepattern("b.txt").call();
  324. RevCommit masterCommit = git.commit()
  325. .setMessage("modified a.txt, deleted b.txt").setAll(true)
  326. .call();
  327. // switch back to a side branch
  328. git.checkout().setCreateBranch(true).setStartPoint(first)
  329. .setName("side").call();
  330. writeTrashFile("c.txt", "side");
  331. git.add().addFilepattern("c.txt").call();
  332. git.commit().setMessage("added c.txt").call();
  333. // Get a handle to the the file so on windows it can't be deleted.
  334. FileInputStream fis = new FileInputStream(new File(db.getWorkTree(),
  335. "b.txt"));
  336. MergeResult mergeRes = git.merge().include(masterCommit).call();
  337. if (mergeRes.getMergeStatus().equals(MergeStatus.FAILED)) {
  338. // probably windows
  339. assertEquals(1, mergeRes.getFailingPaths().size());
  340. assertEquals(MergeFailureReason.COULD_NOT_DELETE, mergeRes
  341. .getFailingPaths().get("b.txt"));
  342. }
  343. assertEquals("[a.txt, mode:100644, content:master]"
  344. + "[c.txt, mode:100644, content:side]", indexState(CONTENT));
  345. fis.close();
  346. }
  347. @Test
  348. public void checkForCorrectIndex() throws Exception {
  349. File f;
  350. long lastTs4, lastTsIndex;
  351. Git git = Git.wrap(db);
  352. File indexFile = db.getIndexFile();
  353. // Create initial content and remember when the last file was written.
  354. f = writeTrashFiles(false, "orig", "orig", "1\n2\n3", "orig", "orig");
  355. lastTs4 = f.lastModified();
  356. // add all files, commit and check this doesn't update any working tree
  357. // files and that the index is in a new file system timer tick. Make
  358. // sure to wait long enough before adding so the index doesn't contain
  359. // racily clean entries
  360. fsTick(f);
  361. git.add().addFilepattern(".").call();
  362. RevCommit firstCommit = git.commit().setMessage("initial commit")
  363. .call();
  364. checkConsistentLastModified("0", "1", "2", "3", "4");
  365. checkModificationTimeStampOrder("1", "2", "3", "4", "<.git/index");
  366. assertEquals("Commit should not touch working tree file 4", lastTs4,
  367. new File(db.getWorkTree(), "4").lastModified());
  368. lastTsIndex = indexFile.lastModified();
  369. // Do modifications on the master branch. Then add and commit. This
  370. // should touch only "0", "2 and "3"
  371. fsTick(indexFile);
  372. f = writeTrashFiles(false, "master", null, "1master\n2\n3", "master",
  373. null);
  374. fsTick(f);
  375. git.add().addFilepattern(".").call();
  376. RevCommit masterCommit = git.commit().setMessage("master commit")
  377. .call();
  378. checkConsistentLastModified("0", "1", "2", "3", "4");
  379. checkModificationTimeStampOrder("1", "4", "*" + lastTs4, "<*"
  380. + lastTsIndex, "<0", "2", "3", "<.git/index");
  381. lastTsIndex = indexFile.lastModified();
  382. // Checkout a side branch. This should touch only "0", "2 and "3"
  383. fsTick(indexFile);
  384. git.checkout().setCreateBranch(true).setStartPoint(firstCommit)
  385. .setName("side").call();
  386. checkConsistentLastModified("0", "1", "2", "3", "4");
  387. checkModificationTimeStampOrder("1", "4", "*" + lastTs4, "<*"
  388. + lastTsIndex, "<0", "2", "3", ".git/index");
  389. lastTsIndex = indexFile.lastModified();
  390. // This checkout may have populated worktree and index so fast that we
  391. // may have smudged entries now. Check that we have the right content
  392. // and then rewrite the index to get rid of smudged state
  393. assertEquals("[0, mode:100644, content:orig]" //
  394. + "[1, mode:100644, content:orig]" //
  395. + "[2, mode:100644, content:1\n2\n3]" //
  396. + "[3, mode:100644, content:orig]" //
  397. + "[4, mode:100644, content:orig]", //
  398. indexState(CONTENT));
  399. fsTick(indexFile);
  400. f = writeTrashFiles(false, "orig", "orig", "1\n2\n3", "orig", "orig");
  401. lastTs4 = f.lastModified();
  402. fsTick(f);
  403. git.add().addFilepattern(".").call();
  404. checkConsistentLastModified("0", "1", "2", "3", "4");
  405. checkModificationTimeStampOrder("*" + lastTsIndex, "<0", "1", "2", "3",
  406. "4", "<.git/index");
  407. lastTsIndex = indexFile.lastModified();
  408. // Do modifications on the side branch. Touch only "1", "2 and "3"
  409. fsTick(indexFile);
  410. f = writeTrashFiles(false, null, "side", "1\n2\n3side", "side", null);
  411. fsTick(f);
  412. git.add().addFilepattern(".").call();
  413. git.commit().setMessage("side commit").call();
  414. checkConsistentLastModified("0", "1", "2", "3", "4");
  415. checkModificationTimeStampOrder("0", "4", "*" + lastTs4, "<*"
  416. + lastTsIndex, "<1", "2", "3", "<.git/index");
  417. lastTsIndex = indexFile.lastModified();
  418. // merge master and side. Should only touch "0," "2" and "3"
  419. fsTick(indexFile);
  420. git.merge().include(masterCommit).call();
  421. checkConsistentLastModified("0", "1", "2", "4");
  422. checkModificationTimeStampOrder("4", "*" + lastTs4, "<1", "<*"
  423. + lastTsIndex, "<0", "2", "3", ".git/index");
  424. assertEquals(
  425. "[0, mode:100644, content:master]" //
  426. + "[1, mode:100644, content:side]" //
  427. + "[2, mode:100644, content:1master\n2\n3side\n]" //
  428. + "[3, mode:100644, stage:1, content:orig][3, mode:100644, stage:2, content:side][3, mode:100644, stage:3, content:master]" //
  429. + "[4, mode:100644, content:orig]", //
  430. indexState(CONTENT));
  431. }
  432. // Assert that every specified index entry has the same last modification
  433. // timestamp as the associated file
  434. private void checkConsistentLastModified(String... pathes)
  435. throws IOException {
  436. DirCache dc = db.readDirCache();
  437. File workTree = db.getWorkTree();
  438. for (String path : pathes)
  439. assertEquals(
  440. "IndexEntry with path "
  441. + path
  442. + " has lastmodified with is different from the worktree file",
  443. new File(workTree, path).lastModified(), dc.getEntry(path)
  444. .getLastModified());
  445. }
  446. // Assert that modification timestamps of working tree files are as
  447. // expected. You may specify n files. It is asserted that every file
  448. // i+1 is not older than file i. If a path of file i+1 is prefixed with "<"
  449. // then this file must be younger then file i. A path "*<modtime>"
  450. // represents a file with a modification time of <modtime>
  451. // E.g. ("a", "b", "<c", "f/a.txt") means: a<=b<c<=f/a.txt
  452. private void checkModificationTimeStampOrder(String... pathes) {
  453. long lastMod = Long.MIN_VALUE;
  454. for (String p : pathes) {
  455. boolean strong = p.startsWith("<");
  456. boolean fixed = p.charAt(strong ? 1 : 0) == '*';
  457. p = p.substring((strong ? 1 : 0) + (fixed ? 1 : 0));
  458. long curMod = fixed ? Long.valueOf(p).longValue() : new File(
  459. db.getWorkTree(), p).lastModified();
  460. if (strong)
  461. assertTrue("path " + p + " is not younger than predecesssor",
  462. curMod > lastMod);
  463. else
  464. assertTrue("path " + p + " is older than predecesssor",
  465. curMod >= lastMod);
  466. }
  467. }
  468. }