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 22KB

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