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.

ReadTreeTest.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  5. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
  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.assertNotNull;
  50. import static org.junit.Assert.assertTrue;
  51. import static org.junit.Assert.fail;
  52. import java.io.File;
  53. import java.io.FileInputStream;
  54. import java.io.IOException;
  55. import java.util.Arrays;
  56. import java.util.HashMap;
  57. import java.util.List;
  58. import java.util.Map;
  59. import org.eclipse.jgit.errors.CheckoutConflictException;
  60. import org.eclipse.jgit.errors.CorruptObjectException;
  61. import org.eclipse.jgit.treewalk.FileTreeIterator;
  62. import org.eclipse.jgit.treewalk.TreeWalk;
  63. import org.junit.Test;
  64. public abstract class ReadTreeTest extends RepositoryTestCase {
  65. protected Tree theHead;
  66. protected Tree theMerge;
  67. // Each of these rules are from the read-tree manpage
  68. // go there to see what they mean.
  69. // Rule 0 is left out for obvious reasons :)
  70. @Test
  71. public void testRules1thru3_NoIndexEntry() throws IOException {
  72. Tree head = new Tree(db);
  73. head = buildTree(mk("foo"));
  74. ObjectId objectId = head.findBlobMember("foo").getId();
  75. Tree merge = new Tree(db);
  76. prescanTwoTrees(head, merge);
  77. assertTrue(getRemoved().contains("foo"));
  78. prescanTwoTrees(merge, head);
  79. assertEquals(objectId, getUpdated().get("foo"));
  80. merge = buildTree(mkmap("foo", "a"));
  81. ObjectId anotherId = merge.findBlobMember("foo").getId();
  82. prescanTwoTrees(head, merge);
  83. assertEquals(anotherId, getUpdated().get("foo"));
  84. }
  85. void setupCase(HashMap<String, String> headEntries,
  86. HashMap<String, String> mergeEntries,
  87. HashMap<String, String> indexEntries) throws IOException {
  88. theHead = buildTree(headEntries);
  89. theMerge = buildTree(mergeEntries);
  90. buildIndex(indexEntries);
  91. }
  92. private void buildIndex(HashMap<String, String> indexEntries) throws IOException {
  93. GitIndex index = new GitIndex(db);
  94. if (indexEntries != null) {
  95. for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) {
  96. index.add(trash, writeTrashFile(e.getKey(), e.getValue())).forceRecheck();
  97. }
  98. }
  99. index.write();
  100. db.getIndex().read();
  101. }
  102. private Tree buildTree(HashMap<String, String> headEntries) throws IOException {
  103. Tree tree = new Tree(db);
  104. if (headEntries == null)
  105. return tree;
  106. FileTreeEntry fileEntry;
  107. Tree parent;
  108. ObjectInserter oi = db.newObjectInserter();
  109. try {
  110. for (java.util.Map.Entry<String, String> e : headEntries.entrySet()) {
  111. fileEntry = tree.addFile(e.getKey());
  112. fileEntry.setId(genSha1(e.getValue()));
  113. parent = fileEntry.getParent();
  114. while (parent != null) {
  115. parent.setId(oi.insert(Constants.OBJ_TREE, parent.format()));
  116. parent = parent.getParent();
  117. }
  118. }
  119. oi.flush();
  120. } finally {
  121. oi.release();
  122. }
  123. return tree;
  124. }
  125. ObjectId genSha1(String data) {
  126. ObjectInserter w = db.newObjectInserter();
  127. try {
  128. ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
  129. w.flush();
  130. return id;
  131. } catch (IOException e) {
  132. fail(e.toString());
  133. } finally {
  134. w.release();
  135. }
  136. return null;
  137. }
  138. protected void go() throws IllegalStateException, IOException {
  139. prescanTwoTrees(theHead, theMerge);
  140. }
  141. // for these rules, they all have clean yes/no options
  142. // but it doesn't matter if the entry is clean or not
  143. // so we can just ignore the state in the filesystem entirely
  144. @Test
  145. public void testRules4thru13_IndexEntryNotInHead() throws IOException {
  146. // rules 4 and 5
  147. HashMap<String, String> idxMap;
  148. idxMap = new HashMap<String, String>();
  149. idxMap.put("foo", "foo");
  150. setupCase(null, null, idxMap);
  151. go();
  152. assertTrue(getUpdated().isEmpty());
  153. assertTrue(getRemoved().isEmpty());
  154. assertTrue(getConflicts().isEmpty());
  155. // rules 6 and 7
  156. idxMap = new HashMap<String, String>();
  157. idxMap.put("foo", "foo");
  158. setupCase(null, idxMap, idxMap);
  159. go();
  160. assertAllEmpty();
  161. // rules 8 and 9
  162. HashMap<String, String> mergeMap;
  163. mergeMap = new HashMap<String, String>();
  164. mergeMap.put("foo", "merge");
  165. setupCase(null, mergeMap, idxMap);
  166. go();
  167. assertTrue(getUpdated().isEmpty());
  168. assertTrue(getRemoved().isEmpty());
  169. assertTrue(getConflicts().contains("foo"));
  170. // rule 10
  171. HashMap<String, String> headMap = new HashMap<String, String>();
  172. headMap.put("foo", "foo");
  173. setupCase(headMap, null, idxMap);
  174. go();
  175. assertTrue(getRemoved().contains("foo"));
  176. assertTrue(getUpdated().isEmpty());
  177. assertTrue(getConflicts().isEmpty());
  178. // rule 11
  179. setupCase(headMap, null, idxMap);
  180. assertTrue(new File(trash, "foo").delete());
  181. writeTrashFile("foo", "bar");
  182. db.getIndex().getMembers()[0].forceRecheck();
  183. go();
  184. assertTrue(getRemoved().isEmpty());
  185. assertTrue(getUpdated().isEmpty());
  186. assertTrue(getConflicts().contains("foo"));
  187. // rule 12 & 13
  188. headMap.put("foo", "head");
  189. setupCase(headMap, null, idxMap);
  190. go();
  191. assertTrue(getRemoved().isEmpty());
  192. assertTrue(getUpdated().isEmpty());
  193. assertTrue(getConflicts().contains("foo"));
  194. // rules 14 & 15
  195. setupCase(headMap, headMap, idxMap);
  196. go();
  197. assertAllEmpty();
  198. // rules 16 & 17
  199. setupCase(headMap, mergeMap, idxMap); go();
  200. assertTrue(getConflicts().contains("foo"));
  201. // rules 18 & 19
  202. setupCase(headMap, idxMap, idxMap); go();
  203. assertAllEmpty();
  204. // rule 20
  205. setupCase(idxMap, mergeMap, idxMap); go();
  206. assertTrue(getUpdated().containsKey("foo"));
  207. // rules 21
  208. setupCase(idxMap, mergeMap, idxMap);
  209. assertTrue(new File(trash, "foo").delete());
  210. writeTrashFile("foo", "bar");
  211. db.getIndex().getMembers()[0].forceRecheck();
  212. go();
  213. assertTrue(getConflicts().contains("foo"));
  214. }
  215. private void assertAllEmpty() {
  216. assertTrue(getRemoved().isEmpty());
  217. assertTrue(getUpdated().isEmpty());
  218. assertTrue(getConflicts().isEmpty());
  219. }
  220. @Test
  221. public void testDirectoryFileSimple() throws IOException {
  222. Tree treeDF = buildTree(mkmap("DF", "DF"));
  223. Tree treeDFDF = buildTree(mkmap("DF/DF", "DF/DF"));
  224. buildIndex(mkmap("DF", "DF"));
  225. prescanTwoTrees(treeDF, treeDFDF);
  226. assertTrue(getRemoved().contains("DF"));
  227. assertTrue(getUpdated().containsKey("DF/DF"));
  228. recursiveDelete(new File(trash, "DF"));
  229. buildIndex(mkmap("DF/DF", "DF/DF"));
  230. prescanTwoTrees(treeDFDF, treeDF);
  231. assertTrue(getRemoved().contains("DF/DF"));
  232. assertTrue(getUpdated().containsKey("DF"));
  233. }
  234. /*
  235. * Directory/File Conflict cases:
  236. * It's entirely possible that in practice a number of these may be equivalent
  237. * to the cases described in git-read-tree.txt. As long as it does the right thing,
  238. * that's all I care about. These are basically reverse-engineered from
  239. * what git currently does. If there are tests for these in git, it's kind of
  240. * hard to track them all down...
  241. *
  242. * H I M Clean H==M H==I I==M Result
  243. * ------------------------------------------------------------------
  244. *1 D D F Y N Y N Update
  245. *2 D D F N N Y N Conflict
  246. *3 D F D Y N N Update
  247. *4 D F D N N N Update
  248. *5 D F F Y N N Y Keep
  249. *6 D F F N N N Y Keep
  250. *7 F D F Y Y N N Update
  251. *8 F D F N Y N N Conflict
  252. *9 F D F Y N N N Update
  253. *10 F D D N N Y Keep
  254. *11 F D D N N N Conflict
  255. *12 F F D Y N Y N Update
  256. *13 F F D N N Y N Conflict
  257. *14 F F D N N N Conflict
  258. *15 0 F D N N N Conflict
  259. *16 0 D F Y N N N Update
  260. *17 0 D F N N N Conflict
  261. *18 F 0 D Update
  262. *19 D 0 F Update
  263. */
  264. @Test
  265. public void testDirectoryFileConflicts_1() throws Exception {
  266. // 1
  267. doit(mk("DF/DF"), mk("DF"), mk("DF/DF"));
  268. assertNoConflicts();
  269. assertUpdated("DF");
  270. assertRemoved("DF/DF");
  271. }
  272. @Test
  273. public void testDirectoryFileConflicts_2() throws Exception {
  274. // 2
  275. setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF"));
  276. writeTrashFile("DF/DF", "different");
  277. go();
  278. assertConflict("DF/DF");
  279. }
  280. @Test
  281. public void testDirectoryFileConflicts_3() throws Exception {
  282. // 3 - the first to break!
  283. doit(mk("DF/DF"), mk("DF/DF"), mk("DF"));
  284. assertUpdated("DF/DF");
  285. assertRemoved("DF");
  286. }
  287. @Test
  288. public void testDirectoryFileConflicts_4() throws Exception {
  289. // 4 (basically same as 3, just with H and M different)
  290. doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF"));
  291. assertUpdated("DF/DF");
  292. assertRemoved("DF");
  293. }
  294. @Test
  295. public void testDirectoryFileConflicts_5() throws Exception {
  296. // 5
  297. doit(mk("DF/DF"), mk("DF"), mk("DF"));
  298. assertRemoved("DF/DF");
  299. }
  300. @Test
  301. public void testDirectoryFileConflicts_6() throws Exception {
  302. // 6
  303. setupCase(mk("DF/DF"), mk("DF"), mk("DF"));
  304. writeTrashFile("DF", "different");
  305. go();
  306. assertRemoved("DF/DF");
  307. }
  308. @Test
  309. public void testDirectoryFileConflicts_7() throws Exception {
  310. // 7
  311. doit(mk("DF"), mk("DF"), mk("DF/DF"));
  312. assertUpdated("DF");
  313. assertRemoved("DF/DF");
  314. cleanUpDF();
  315. setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
  316. go();
  317. assertRemoved("DF/DF/DF/DF/DF");
  318. assertUpdated("DF/DF");
  319. cleanUpDF();
  320. setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
  321. writeTrashFile("DF/DF/DF/DF/DF", "diff");
  322. go();
  323. assertConflict("DF/DF/DF/DF/DF");
  324. // assertUpdated("DF/DF");
  325. // Why do we expect an update on DF/DF. H==M,
  326. // H&M are files and index contains a dir, index
  327. // is dirty: that case is not in the table but
  328. // we cannot update DF/DF to a file, this would
  329. // require that we delete DF/DF/DF/DF/DF in workdir
  330. // throwing away unsaved contents.
  331. // This test would fail in DirCacheCheckoutTests.
  332. }
  333. @Test
  334. public void testDirectoryFileConflicts_8() throws Exception {
  335. // 8
  336. setupCase(mk("DF"), mk("DF"), mk("DF/DF"));
  337. recursiveDelete(new File(db.getWorkTree(), "DF"));
  338. writeTrashFile("DF", "xy");
  339. go();
  340. assertConflict("DF/DF");
  341. }
  342. @Test
  343. public void testDirectoryFileConflicts_9() throws Exception {
  344. // 9
  345. doit(mk("DF"), mkmap("DF", "QP"), mk("DF/DF"));
  346. assertRemoved("DF/DF");
  347. assertUpdated("DF");
  348. }
  349. @Test
  350. public void testDirectoryFileConflicts_10() throws Exception {
  351. // 10
  352. cleanUpDF();
  353. doit(mk("DF"), mk("DF/DF"), mk("DF/DF"));
  354. assertNoConflicts();
  355. }
  356. @Test
  357. public void testDirectoryFileConflicts_11() throws Exception {
  358. // 11
  359. doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf"));
  360. assertConflict("DF/DF");
  361. }
  362. @Test
  363. public void testDirectoryFileConflicts_12() throws Exception {
  364. // 12
  365. cleanUpDF();
  366. doit(mk("DF"), mk("DF/DF"), mk("DF"));
  367. assertRemoved("DF");
  368. assertUpdated("DF/DF");
  369. }
  370. @Test
  371. public void testDirectoryFileConflicts_13() throws Exception {
  372. // 13
  373. cleanUpDF();
  374. setupCase(mk("DF"), mk("DF/DF"), mk("DF"));
  375. writeTrashFile("DF", "asdfsdf");
  376. go();
  377. assertConflict("DF");
  378. assertUpdated("DF/DF");
  379. }
  380. @Test
  381. public void testDirectoryFileConflicts_14() throws Exception {
  382. // 14
  383. cleanUpDF();
  384. doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo"));
  385. assertConflict("DF");
  386. assertUpdated("DF/DF");
  387. }
  388. @Test
  389. public void testDirectoryFileConflicts_15() throws Exception {
  390. // 15
  391. doit(mkmap(), mk("DF/DF"), mk("DF"));
  392. // This test would fail in DirCacheCheckoutTests. I think this test is wrong,
  393. // it should check for conflicts according to rule 15
  394. // assertRemoved("DF");
  395. assertUpdated("DF/DF");
  396. }
  397. @Test
  398. public void testDirectoryFileConflicts_15b() throws Exception {
  399. // 15, take 2, just to check multi-leveled
  400. doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF"));
  401. // I think this test is wrong, it should
  402. // check for conflicts according to rule 15
  403. // This test would fail in DirCacheCheckouts
  404. // assertRemoved("DF");
  405. assertUpdated("DF/DF/DF/DF");
  406. }
  407. @Test
  408. public void testDirectoryFileConflicts_16() throws Exception {
  409. // 16
  410. cleanUpDF();
  411. doit(mkmap(), mk("DF"), mk("DF/DF/DF"));
  412. assertRemoved("DF/DF/DF");
  413. assertUpdated("DF");
  414. }
  415. @Test
  416. public void testDirectoryFileConflicts_17() throws Exception {
  417. // 17
  418. cleanUpDF();
  419. setupCase(mkmap(), mk("DF"), mk("DF/DF/DF"));
  420. writeTrashFile("DF/DF/DF", "asdf");
  421. go();
  422. assertConflict("DF/DF/DF");
  423. // Why do we expect an update on DF. If we really update
  424. // DF and update also the working tree we would have to
  425. // overwrite a dirty file in the work-tree DF/DF/DF
  426. // This test would fail in DirCacheCheckout
  427. // assertUpdated("DF");
  428. }
  429. @Test
  430. public void testDirectoryFileConflicts_18() throws Exception {
  431. // 18
  432. cleanUpDF();
  433. doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null);
  434. assertRemoved("DF/DF");
  435. assertUpdated("DF/DF/DF/DF");
  436. }
  437. @Test
  438. public void testDirectoryFileConflicts_19() throws Exception {
  439. // 19
  440. cleanUpDF();
  441. doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null);
  442. assertRemoved("DF/DF/DF/DF");
  443. assertUpdated("DF/DF/DF");
  444. }
  445. protected void cleanUpDF() throws Exception {
  446. tearDown();
  447. setUp();
  448. recursiveDelete(new File(trash, "DF"));
  449. }
  450. protected void assertConflict(String s) {
  451. assertTrue(getConflicts().contains(s));
  452. }
  453. protected void assertUpdated(String s) {
  454. assertTrue(getUpdated().containsKey(s));
  455. }
  456. protected void assertRemoved(String s) {
  457. assertTrue(getRemoved().contains(s));
  458. }
  459. protected void assertNoConflicts() {
  460. assertTrue(getConflicts().isEmpty());
  461. }
  462. protected void doit(HashMap<String, String> h, HashMap<String, String> m,
  463. HashMap<String, String> i) throws IOException {
  464. setupCase(h, m, i);
  465. go();
  466. }
  467. protected static HashMap<String, String> mk(String a) {
  468. return mkmap(a, a);
  469. }
  470. protected static HashMap<String, String> mkmap(String... args) {
  471. if ((args.length % 2) > 0)
  472. throw new IllegalArgumentException("needs to be pairs");
  473. HashMap<String, String> map = new HashMap<String, String>();
  474. for (int i = 0; i < args.length; i += 2) {
  475. map.put(args[i], args[i+1]);
  476. }
  477. return map;
  478. }
  479. @Test
  480. public void testUntrackedConflicts() throws IOException {
  481. setupCase(null, mk("foo"), null);
  482. writeTrashFile("foo", "foo");
  483. go();
  484. // test that we don't overwrite untracked files when there is a HEAD
  485. recursiveDelete(new File(trash, "foo"));
  486. setupCase(mk("other"), mkmap("other", "other", "foo", "foo"),
  487. mk("other"));
  488. writeTrashFile("foo", "bar");
  489. try {
  490. checkout();
  491. fail("didn't get the expected exception");
  492. } catch (CheckoutConflictException e) {
  493. assertConflict("foo");
  494. assertWorkDir(mkmap("foo", "bar", "other", "other"));
  495. assertIndex(mk("other"));
  496. }
  497. // test that we don't overwrite untracked files when there is no HEAD
  498. recursiveDelete(new File(trash, "other"));
  499. recursiveDelete(new File(trash, "foo"));
  500. setupCase(null, mk("foo"), null);
  501. writeTrashFile("foo", "bar");
  502. try {
  503. checkout();
  504. fail("didn't get the expected exception");
  505. } catch (CheckoutConflictException e) {
  506. assertConflict("foo");
  507. assertWorkDir(mkmap("foo", "bar"));
  508. assertIndex(mkmap());
  509. }
  510. // TODO: Why should we expect conflicts here?
  511. // H and M are emtpy and according to rule #5 of
  512. // the carry-over rules a dirty index is no reason
  513. // for a conflict. (I also feel it should be a
  514. // conflict because we are going to overwrite
  515. // unsaved content in the working tree
  516. // This test would fail in DirCacheCheckoutTest
  517. // assertConflict("foo");
  518. recursiveDelete(new File(trash, "foo"));
  519. recursiveDelete(new File(trash, "other"));
  520. setupCase(null, mk("foo"), null);
  521. writeTrashFile("foo/bar/baz", "");
  522. writeTrashFile("foo/blahblah", "");
  523. go();
  524. // TODO: In DirCacheCheckout the following assertion would pass. But
  525. // old WorkDirCheckout fails on this. For now I leave it out. Find out
  526. // what's the correct behavior.
  527. // assertConflict("foo");
  528. assertConflict("foo/bar/baz");
  529. assertConflict("foo/blahblah");
  530. recursiveDelete(new File(trash, "foo"));
  531. setupCase(mkmap("foo/bar", "", "foo/baz", ""),
  532. mk("foo"), mkmap("foo/bar", "", "foo/baz", ""));
  533. assertTrue(new File(trash, "foo/bar").exists());
  534. go();
  535. assertNoConflicts();
  536. }
  537. @Test
  538. public void testCloseNameConflictsX0() throws IOException {
  539. setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "b.b/b.b","b.b/b.bs"), mkmap("a/a", "a/a-c") );
  540. checkout();
  541. assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
  542. assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
  543. go();
  544. assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
  545. assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
  546. assertNoConflicts();
  547. }
  548. @Test
  549. public void testCloseNameConflicts1() throws IOException {
  550. setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "a.a/a.a","a.a/a.a"), mkmap("a/a", "a/a-c") );
  551. checkout();
  552. assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
  553. assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
  554. go();
  555. assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
  556. assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
  557. assertNoConflicts();
  558. }
  559. @Test
  560. public void testCheckoutHierarchy() throws IOException {
  561. setupCase(
  562. mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
  563. "e/g"),
  564. mkmap("a", "a2", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
  565. "e/g2"),
  566. mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
  567. "e/g3"));
  568. try {
  569. checkout();
  570. } catch (CheckoutConflictException e) {
  571. assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f",
  572. "e/f", "e/g", "e/g3"));
  573. assertConflict("e/g");
  574. }
  575. }
  576. @Test
  577. public void testCheckoutOutChanges() throws IOException {
  578. setupCase(mk("foo"), mk("foo/bar"), mk("foo"));
  579. checkout();
  580. assertIndex(mk("foo/bar"));
  581. assertWorkDir(mk("foo/bar"));
  582. assertFalse(new File(trash, "foo").isFile());
  583. assertTrue(new File(trash, "foo/bar").isFile());
  584. recursiveDelete(new File(trash, "foo"));
  585. assertWorkDir(mkmap());
  586. setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar"));
  587. checkout();
  588. assertIndex(mk("foo"));
  589. assertWorkDir(mk("foo"));
  590. assertFalse(new File(trash, "foo/bar").isFile());
  591. assertTrue(new File(trash, "foo").isFile());
  592. setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar"));
  593. assertIndex(mkmap("foo", "bar"));
  594. assertWorkDir(mkmap("foo", "bar"));
  595. try {
  596. checkout();
  597. fail("did not throw exception");
  598. } catch (CheckoutConflictException e) {
  599. assertIndex(mkmap("foo", "bar"));
  600. assertWorkDir(mkmap("foo", "bar"));
  601. }
  602. }
  603. @Test
  604. public void testCheckoutUncachedChanges() throws IOException {
  605. setupCase(mk("foo"), mk("foo"), mk("foo"));
  606. writeTrashFile("foo", "otherData");
  607. checkout();
  608. assertIndex(mk("foo"));
  609. assertWorkDir(mkmap("foo", "otherData"));
  610. assertTrue(new File(trash, "foo").isFile());
  611. }
  612. @Test
  613. public void testDontOverwriteDirtyFile() throws IOException {
  614. setupCase(mk("foo"), mk("other"), mk("foo"));
  615. writeTrashFile("foo", "different");
  616. try {
  617. checkout();
  618. fail("Didn't got the expected conflict");
  619. } catch (CheckoutConflictException e) {
  620. assertIndex(mk("foo"));
  621. assertWorkDir(mkmap("foo", "different"));
  622. assertTrue(getConflicts().equals(Arrays.asList("foo")));
  623. assertTrue(new File(trash, "foo").isFile());
  624. }
  625. }
  626. public void assertWorkDir(HashMap<String, String> i)
  627. throws CorruptObjectException, IOException {
  628. TreeWalk walk = new TreeWalk(db);
  629. walk.setRecursive(true);
  630. walk.addTree(new FileTreeIterator(db));
  631. String expectedValue;
  632. String path;
  633. int nrFiles = 0;
  634. FileTreeIterator ft;
  635. while (walk.next()) {
  636. ft = walk.getTree(0, FileTreeIterator.class);
  637. path = ft.getEntryPathString();
  638. expectedValue = i.get(path);
  639. assertNotNull("found unexpected file for path "
  640. + path + " in workdir", expectedValue);
  641. File file = new File(db.getWorkTree(), path);
  642. assertTrue(file.exists());
  643. if (file.isFile()) {
  644. FileInputStream is = new FileInputStream(file);
  645. byte[] buffer = new byte[(int) file.length()];
  646. int offset = 0;
  647. int numRead = 0;
  648. while (offset < buffer.length
  649. && (numRead = is.read(buffer, offset, buffer.length
  650. - offset)) >= 0) {
  651. offset += numRead;
  652. }
  653. is.close();
  654. assertTrue("unexpected content for path " + path
  655. + " in workDir. Expected: <" + expectedValue + ">",
  656. Arrays.equals(buffer, i.get(path).getBytes()));
  657. nrFiles++;
  658. }
  659. }
  660. assertEquals("WorkDir has not the right size.", i.size(), nrFiles);
  661. }
  662. public void assertIndex(HashMap<String, String> i)
  663. throws CorruptObjectException, IOException {
  664. String expectedValue;
  665. String path;
  666. GitIndex theIndex=db.getIndex();
  667. // Without an explicit refresh we might miss index updates. If the index
  668. // is updated multiple times inside a FileSystemTimer tick db.getIndex will
  669. // not reload the index and return a cached (stale) index.
  670. theIndex.read();
  671. assertEquals("Index has not the right size.", i.size(),
  672. theIndex.getMembers().length);
  673. for (int j = 0; j < theIndex.getMembers().length; j++) {
  674. path = theIndex.getMembers()[j].getName();
  675. expectedValue = i.get(path);
  676. assertNotNull("found unexpected entry for path " + path
  677. + " in index", expectedValue);
  678. assertTrue("unexpected content for path " + path
  679. + " in index. Expected: <" + expectedValue + ">",
  680. Arrays.equals(
  681. db.open(theIndex.getMembers()[j].getObjectId())
  682. .getCachedBytes(), i.get(path).getBytes()));
  683. }
  684. }
  685. public abstract void prescanTwoTrees(Tree head, Tree merge) throws IllegalStateException, IOException;
  686. public abstract void checkout() throws IOException;
  687. public abstract List<String> getRemoved();
  688. public abstract Map<String, ObjectId> getUpdated();
  689. public abstract List<String> getConflicts();
  690. }