View Javadoc
1   /*
2    * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3    * Copyright (C) 2008-2011, Shawn O. Pearce <spearce@spearce.org>
4    * Copyright (C) 2008-2011, Robin Rosenberg <robin.rosenberg@dewire.com>
5    * Copyright (C) 2010-2011, 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 under the
9    * terms of the Eclipse Distribution License v1.0 which accompanies this
10   * distribution, is reproduced below, and is available at
11   * 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 without
16   * modification, are permitted provided that the following conditions are met:
17   *
18   * - Redistributions of source code must retain the above copyright notice, this
19   * list of conditions and the following disclaimer.
20   *
21   * - Redistributions in binary form must reproduce the above copyright notice,
22   * this list of conditions and the following disclaimer in the documentation
23   * and/or other materials provided with the distribution.
24   *
25   * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
26   * contributors may be used to endorse or promote products derived from this
27   * software without specific prior written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
33   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39   * POSSIBILITY OF SUCH DAMAGE.
40   */
41  package org.eclipse.jgit.lib;
42  
43  import static org.junit.Assert.assertArrayEquals;
44  import static org.junit.Assert.assertEquals;
45  import static org.junit.Assert.assertFalse;
46  import static org.junit.Assert.assertNotNull;
47  import static org.junit.Assert.assertTrue;
48  import static org.junit.Assert.fail;
49  
50  import java.io.File;
51  import java.io.FileInputStream;
52  import java.io.IOException;
53  import java.util.Arrays;
54  import java.util.HashMap;
55  import java.util.List;
56  import java.util.Map;
57  
58  import org.eclipse.jgit.api.CheckoutCommand;
59  import org.eclipse.jgit.api.CheckoutResult;
60  import org.eclipse.jgit.api.Git;
61  import org.eclipse.jgit.api.MergeResult.MergeStatus;
62  import org.eclipse.jgit.api.ResetCommand.ResetType;
63  import org.eclipse.jgit.api.Status;
64  import org.eclipse.jgit.api.errors.GitAPIException;
65  import org.eclipse.jgit.api.errors.NoFilepatternException;
66  import org.eclipse.jgit.dircache.DirCache;
67  import org.eclipse.jgit.dircache.DirCacheCheckout;
68  import org.eclipse.jgit.dircache.DirCacheEditor;
69  import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
70  import org.eclipse.jgit.dircache.DirCacheEntry;
71  import org.eclipse.jgit.errors.CheckoutConflictException;
72  import org.eclipse.jgit.errors.CorruptObjectException;
73  import org.eclipse.jgit.errors.NoWorkTreeException;
74  import org.eclipse.jgit.junit.RepositoryTestCase;
75  import org.eclipse.jgit.junit.TestRepository;
76  import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
77  import org.eclipse.jgit.revwalk.RevCommit;
78  import org.eclipse.jgit.treewalk.FileTreeIterator;
79  import org.eclipse.jgit.treewalk.TreeWalk;
80  import org.eclipse.jgit.util.FS;
81  import org.junit.Test;
82  
83  public class DirCacheCheckoutTest extends RepositoryTestCase {
84  	private DirCacheCheckout dco;
85  	protected ObjectId theHead;
86  	protected ObjectId theMerge;
87  	private DirCache dirCache;
88  
89  	private void prescanTwoTrees(ObjectId head, ObjectId merge)
90  			throws IllegalStateException, IOException {
91  		DirCache dc = db.lockDirCache();
92  		try {
93  			dco = new DirCacheCheckout(db, head, dc, merge);
94  			dco.preScanTwoTrees();
95  		} finally {
96  			dc.unlock();
97  		}
98  	}
99  
100 	private void checkout() throws IOException {
101 		DirCache dc = db.lockDirCache();
102 		try {
103 			dco = new DirCacheCheckout(db, theHead, dc, theMerge);
104 			dco.checkout();
105 		} finally {
106 			dc.unlock();
107 		}
108 	}
109 
110 	private List<String> getRemoved() {
111 		return dco.getRemoved();
112 	}
113 
114 	private Map<String, ObjectId> getUpdated() {
115 		return dco.getUpdated();
116 	}
117 
118 	private List<String> getConflicts() {
119 		return dco.getConflicts();
120 	}
121 
122 	private static HashMap<String, String> mk(String a) {
123 		return mkmap(a, a);
124 	}
125 
126 	private static HashMap<String, String> mkmap(String... args) {
127 		if ((args.length % 2) > 0)
128 			throw new IllegalArgumentException("needs to be pairs");
129 
130 		HashMap<String, String> map = new HashMap<String, String>();
131 		for (int i = 0; i < args.length; i += 2) {
132 			map.put(args[i], args[i + 1]);
133 		}
134 
135 		return map;
136 	}
137 
138 	@Test
139 	public void testResetHard() throws IOException, NoFilepatternException,
140 			GitAPIException {
141 		Git git = new Git(db);
142 		writeTrashFile("f", "f()");
143 		writeTrashFile("D/g", "g()");
144 		git.add().addFilepattern(".").call();
145 		git.commit().setMessage("inital").call();
146 		assertIndex(mkmap("f", "f()", "D/g", "g()"));
147 
148 		git.branchCreate().setName("topic").call();
149 
150 		writeTrashFile("f", "f()\nmaster");
151 		writeTrashFile("D/g", "g()\ng2()");
152 		writeTrashFile("E/h", "h()");
153 		git.add().addFilepattern(".").call();
154 		RevCommit master = git.commit().setMessage("master-1").call();
155 		assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
156 
157 		checkoutBranch("refs/heads/topic");
158 		assertIndex(mkmap("f", "f()", "D/g", "g()"));
159 
160 		writeTrashFile("f", "f()\nside");
161 		assertTrue(new File(db.getWorkTree(), "D/g").delete());
162 		writeTrashFile("G/i", "i()");
163 		git.add().addFilepattern(".").call();
164 		git.add().addFilepattern(".").setUpdate(true).call();
165 		RevCommit topic = git.commit().setMessage("topic-1").call();
166 		assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
167 
168 		writeTrashFile("untracked", "untracked");
169 
170 		resetHard(master);
171 		assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
172 		resetHard(topic);
173 		assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
174 		assertWorkDir(mkmap("f", "f()\nside", "G/i", "i()", "untracked",
175 				"untracked"));
176 
177 		assertEquals(MergeStatus.CONFLICTING, git.merge().include(master)
178 				.call().getMergeStatus());
179 		assertEquals(
180 				"[D/g, mode:100644, stage:1][D/g, mode:100644, stage:3][E/h, mode:100644][G/i, mode:100644][f, mode:100644, stage:1][f, mode:100644, stage:2][f, mode:100644, stage:3]",
181 				indexState(0));
182 
183 		resetHard(master);
184 		assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
185 		assertWorkDir(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h",
186 				"h()", "untracked", "untracked"));
187 	}
188 
189 	/**
190 	 * Reset hard from unclean condition.
191 	 * <p>
192 	 * WorkDir: Empty <br/>
193 	 * Index: f/g <br/>
194 	 * Merge: x
195 	 *
196 	 * @throws Exception
197 	 */
198 	@Test
199 	public void testResetHardFromIndexEntryWithoutFileToTreeWithoutFile()
200 			throws Exception {
201 		Git git = new Git(db);
202 		writeTrashFile("x", "x");
203 		git.add().addFilepattern("x").call();
204 		RevCommit id1 = git.commit().setMessage("c1").call();
205 
206 		writeTrashFile("f/g", "f/g");
207 		git.rm().addFilepattern("x").call();
208 		git.add().addFilepattern("f/g").call();
209 		git.commit().setMessage("c2").call();
210 		deleteTrashFile("f/g");
211 		deleteTrashFile("f");
212 
213 		// The actual test
214 		git.reset().setMode(ResetType.HARD).setRef(id1.getName()).call();
215 		assertIndex(mkmap("x", "x"));
216 	}
217 
218 	/**
219 	 * Test first checkout in a repo
220 	 *
221 	 * @throws Exception
222 	 */
223 	@Test
224 	public void testInitialCheckout() throws Exception {
225 		Git git = new Git(db);
226 
227 		TestRepository<Repository> db_t = new TestRepository<Repository>(db);
228 		BranchBuilder master = db_t.branch("master");
229 		master.commit().add("f", "1").message("m0").create();
230 		assertFalse(new File(db.getWorkTree(), "f").exists());
231 		git.checkout().setName("master").call();
232 		assertTrue(new File(db.getWorkTree(), "f").exists());
233 	}
234 
235 	private DirCacheCheckout resetHard(RevCommit commit)
236 			throws NoWorkTreeException,
237 			CorruptObjectException, IOException {
238 		DirCacheCheckout dc;
239 		dc = new DirCacheCheckout(db, null, db.lockDirCache(),
240 				commit.getTree());
241 		dc.setFailOnConflict(true);
242 		assertTrue(dc.checkout());
243 		return dc;
244 	}
245 
246 	private void assertIndex(HashMap<String, String> i)
247 			throws CorruptObjectException, IOException {
248 		String expectedValue;
249 		String path;
250 		DirCache read = DirCache.read(db.getIndexFile(), db.getFS());
251 
252 		assertEquals("Index has not the right size.", i.size(),
253 				read.getEntryCount());
254 		for (int j = 0; j < read.getEntryCount(); j++) {
255 			path = read.getEntry(j).getPathString();
256 			expectedValue = i.get(path);
257 			assertNotNull("found unexpected entry for path " + path
258 					+ " in index", expectedValue);
259 			assertTrue("unexpected content for path " + path
260 					+ " in index. Expected: <" + expectedValue + ">",
261 					Arrays.equals(db.open(read.getEntry(j).getObjectId())
262 							.getCachedBytes(), i.get(path).getBytes()));
263 		}
264 	}
265 
266 	@Test
267 	public void testRules1thru3_NoIndexEntry() throws IOException {
268 		ObjectId head = buildTree(mk("foo"));
269 		TreeWalk tw = TreeWalk.forPath(db, "foo", head);
270 		ObjectId objectId = tw.getObjectId(0);
271 		ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE,
272 				new byte[0]);
273 
274 		prescanTwoTrees(head, merge);
275 
276 		assertTrue(getRemoved().contains("foo"));
277 
278 		prescanTwoTrees(merge, head);
279 
280 		assertEquals(objectId, getUpdated().get("foo"));
281 
282 		merge = buildTree(mkmap("foo", "a"));
283 		tw = TreeWalk.forPath(db, "foo", merge);
284 
285 		prescanTwoTrees(head, merge);
286 
287 		assertConflict("foo");
288 	}
289 
290 	void setupCase(HashMap<String, String> headEntries, HashMap<String, String> mergeEntries, HashMap<String, String> indexEntries) throws IOException {
291 		theHead = buildTree(headEntries);
292 		theMerge = buildTree(mergeEntries);
293 		buildIndex(indexEntries);
294 	}
295 
296 	private void buildIndex(HashMap<String, String> indexEntries) throws IOException {
297 		dirCache = new DirCache(db.getIndexFile(), db.getFS());
298 		if (indexEntries != null) {
299 			assertTrue(dirCache.lock());
300 			DirCacheEditor editor = dirCache.editor();
301 			for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) {
302 				writeTrashFile(e.getKey(), e.getValue());
303 				ObjectInserter inserter = db.newObjectInserter();
304 				final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
305 						Constants.encode(e.getValue()));
306 				editor.add(new DirCacheEditor.DeletePath(e.getKey()));
307 				editor.add(new DirCacheEditor.PathEdit(e.getKey()) {
308 					@Override
309 					public void apply(DirCacheEntry ent) {
310 						ent.setFileMode(FileMode.REGULAR_FILE);
311 						ent.setObjectId(id);
312 						ent.setUpdateNeeded(false);
313 					}
314 				});
315 			}
316 			assertTrue(editor.commit());
317 		}
318 
319 	}
320 
321 	static final class AddEdit extends PathEdit {
322 
323 		private final ObjectId data;
324 
325 		private final long length;
326 
327 		public AddEdit(String entryPath, ObjectId data, long length) {
328 			super(entryPath);
329 			this.data = data;
330 			this.length = length;
331 		}
332 
333 		@Override
334 		public void apply(DirCacheEntry ent) {
335 			ent.setFileMode(FileMode.REGULAR_FILE);
336 			ent.setLength(length);
337 			ent.setObjectId(data);
338 		}
339 
340 	}
341 
342 	private ObjectId buildTree(HashMap<String, String> headEntries)
343 			throws IOException {
344 		DirCache lockDirCache = DirCache.newInCore();
345 		// assertTrue(lockDirCache.lock());
346 		DirCacheEditor editor = lockDirCache.editor();
347 		if (headEntries != null) {
348 			for (java.util.Map.Entry<String, String> e : headEntries.entrySet()) {
349 				AddEdit addEdit = new AddEdit(e.getKey(),
350 						genSha1(e.getValue()), e.getValue().length());
351 				editor.add(addEdit);
352 			}
353 		}
354 		editor.finish();
355 		return lockDirCache.writeTree(db.newObjectInserter());
356 	}
357 
358 	ObjectId genSha1(String data) {
359 		try (ObjectInserter w = db.newObjectInserter()) {
360 			ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
361 			w.flush();
362 			return id;
363 		} catch (IOException e) {
364 			fail(e.toString());
365 		}
366 		return null;
367 	}
368 
369 	protected void go() throws IllegalStateException, IOException {
370 		prescanTwoTrees(theHead, theMerge);
371 	}
372 
373 	@Test
374 	public void testRules4thru13_IndexEntryNotInHead() throws IOException {
375 		// rules 4 and 5
376 		HashMap<String, String> idxMap;
377 
378 		idxMap = new HashMap<String, String>();
379 		idxMap.put("foo", "foo");
380 		setupCase(null, null, idxMap);
381 		go();
382 
383 		assertTrue(getUpdated().isEmpty());
384 		assertTrue(getRemoved().isEmpty());
385 		assertTrue(getConflicts().isEmpty());
386 
387 		// rules 6 and 7
388 		idxMap = new HashMap<String, String>();
389 		idxMap.put("foo", "foo");
390 		setupCase(null, idxMap, idxMap);
391 		go();
392 
393 		assertAllEmpty();
394 
395 		// rules 8 and 9
396 		HashMap<String, String> mergeMap;
397 		mergeMap = new HashMap<String, String>();
398 
399 		mergeMap.put("foo", "merge");
400 		setupCase(null, mergeMap, idxMap);
401 		go();
402 
403 		assertTrue(getUpdated().isEmpty());
404 		assertTrue(getRemoved().isEmpty());
405 		assertTrue(getConflicts().contains("foo"));
406 
407 		// rule 10
408 
409 		HashMap<String, String> headMap = new HashMap<String, String>();
410 		headMap.put("foo", "foo");
411 		setupCase(headMap, null, idxMap);
412 		go();
413 
414 		assertTrue(getRemoved().contains("foo"));
415 		assertTrue(getUpdated().isEmpty());
416 		assertTrue(getConflicts().isEmpty());
417 
418 		// rule 11
419 		setupCase(headMap, null, idxMap);
420 		assertTrue(new File(trash, "foo").delete());
421 		writeTrashFile("foo", "bar");
422 		db.readDirCache().getEntry(0).setUpdateNeeded(true);
423 		go();
424 
425 		assertTrue(getRemoved().isEmpty());
426 		assertTrue(getUpdated().isEmpty());
427 		assertTrue(getConflicts().contains("foo"));
428 
429 		// rule 12 & 13
430 		headMap.put("foo", "head");
431 		setupCase(headMap, null, idxMap);
432 		go();
433 
434 		assertTrue(getRemoved().isEmpty());
435 		assertTrue(getUpdated().isEmpty());
436 		assertTrue(getConflicts().contains("foo"));
437 
438 		// rules 14 & 15
439 		setupCase(headMap, headMap, idxMap);
440 		go();
441 
442 		assertAllEmpty();
443 
444 		// rules 16 & 17
445 		setupCase(headMap, mergeMap, idxMap); go();
446 		assertTrue(getConflicts().contains("foo"));
447 
448 		// rules 18 & 19
449 		setupCase(headMap, idxMap, idxMap); go();
450 		assertAllEmpty();
451 
452 		// rule 20
453 		setupCase(idxMap, mergeMap, idxMap); go();
454 		assertTrue(getUpdated().containsKey("foo"));
455 
456 		// rules 21
457 		setupCase(idxMap, mergeMap, idxMap);
458 		assertTrue(new File(trash, "foo").delete());
459 		writeTrashFile("foo", "bar");
460 		db.readDirCache().getEntry(0).setUpdateNeeded(true);
461 		go();
462 		assertTrue(getConflicts().contains("foo"));
463 	}
464 
465 	private void assertAllEmpty() {
466 		assertTrue(getRemoved().isEmpty());
467 		assertTrue(getUpdated().isEmpty());
468 		assertTrue(getConflicts().isEmpty());
469 	}
470 
471 	/*-
472 	 * Directory/File Conflict cases:
473 	 * It's entirely possible that in practice a number of these may be equivalent
474 	 * to the cases described in git-read-tree.txt. As long as it does the right thing,
475 	 * that's all I care about. These are basically reverse-engineered from
476 	 * what git currently does. If there are tests for these in git, it's kind of
477 	 * hard to track them all down...
478 	 *
479 	 *     H        I       M     Clean     H==M     H==I    I==M         Result
480 	 *     ------------------------------------------------------------------
481 	 *1    D        D       F       Y         N       Y       N           Update
482 	 *2    D        D       F       N         N       Y       N           Conflict
483 	 *3    D        F       D                 Y       N       N           Keep
484 	 *4    D        F       D                 N       N       N           Conflict
485 	 *5    D        F       F       Y         N       N       Y           Keep
486 	 *5b   D        F       F       Y         N       N       N           Conflict
487 	 *6    D        F       F       N         N       N       Y           Keep
488 	 *6b   D        F       F       N         N       N       N           Conflict
489 	 *7    F        D       F       Y         Y       N       N           Update
490 	 *8    F        D       F       N         Y       N       N           Conflict
491 	 *9    F        D       F       Y         N       N       N           Update
492 	 *10   F        D       D                 N       N       Y           Keep
493 	 *11   F        D       D                 N       N       N           Conflict
494 	 *12   F        F       D       Y         N       Y       N           Update
495 	 *13   F        F       D       N         N       Y       N           Conflict
496 	 *14   F        F       D                 N       N       N           Conflict
497 	 *15   0        F       D                 N       N       N           Conflict
498 	 *16   0        D       F       Y         N       N       N           Update
499 	 *17   0        D       F                 N       N       N           Conflict
500 	 *18   F        0       D                                             Update
501 	 *19   D        0       F                                             Update
502 	 */
503 	@Test
504 	public void testDirectoryFileSimple() throws IOException {
505 		ObjectId treeDF = buildTree(mkmap("DF", "DF"));
506 		ObjectId treeDFDF = buildTree(mkmap("DF/DF", "DF/DF"));
507 		buildIndex(mkmap("DF", "DF"));
508 
509 		prescanTwoTrees(treeDF, treeDFDF);
510 
511 		assertTrue(getRemoved().contains("DF"));
512 		assertTrue(getUpdated().containsKey("DF/DF"));
513 
514 		recursiveDelete(new File(trash, "DF"));
515 		buildIndex(mkmap("DF/DF", "DF/DF"));
516 
517 		prescanTwoTrees(treeDFDF, treeDF);
518 		assertTrue(getRemoved().contains("DF/DF"));
519 		assertTrue(getUpdated().containsKey("DF"));
520 	}
521 
522 	@Test
523 	public void testDirectoryFileConflicts_1() throws Exception {
524 		// 1
525 		doit(mk("DF/DF"), mk("DF"), mk("DF/DF"));
526 		assertNoConflicts();
527 		assertUpdated("DF");
528 		assertRemoved("DF/DF");
529 	}
530 
531 	@Test
532 	public void testDirectoryFileConflicts_2() throws Exception {
533 		// 2
534 		setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF"));
535 		writeTrashFile("DF/DF", "different");
536 		go();
537 		assertConflict("DF/DF");
538 
539 	}
540 
541 	@Test
542 	public void testDirectoryFileConflicts_3() throws Exception {
543 		// 3
544 		doit(mk("DF/DF"), mk("DF/DF"), mk("DF"));
545 		assertNoConflicts();
546 	}
547 
548 	@Test
549 	public void testDirectoryFileConflicts_4() throws Exception {
550 		// 4 (basically same as 3, just with H and M different)
551 		doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF"));
552 		assertConflict("DF/DF");
553 
554 	}
555 
556 	@Test
557 	public void testDirectoryFileConflicts_5() throws Exception {
558 		// 5
559 		doit(mk("DF/DF"), mk("DF"), mk("DF"));
560 		assertRemoved("DF/DF");
561 		assertEquals(0, dco.getConflicts().size());
562 		assertEquals(0, dco.getUpdated().size());
563 	}
564 
565 	@Test
566 	public void testDirectoryFileConflicts_5b() throws Exception {
567 		// 5
568 		doit(mk("DF/DF"), mkmap("DF", "different"), mk("DF"));
569 		assertRemoved("DF/DF");
570 		assertConflict("DF");
571 		assertEquals(0, dco.getUpdated().size());
572 	}
573 
574 	@Test
575 	public void testDirectoryFileConflicts_6() throws Exception {
576 		// 6
577 		setupCase(mk("DF/DF"), mk("DF"), mk("DF"));
578 		writeTrashFile("DF", "different");
579 		go();
580 		assertRemoved("DF/DF");
581 		assertEquals(0, dco.getConflicts().size());
582 		assertEquals(0, dco.getUpdated().size());
583 	}
584 
585 	@Test
586 	public void testDirectoryFileConflicts_6b() throws Exception {
587 		// 6
588 		setupCase(mk("DF/DF"), mk("DF"), mkmap("DF", "different"));
589 		writeTrashFile("DF", "again different");
590 		go();
591 		assertRemoved("DF/DF");
592 		assertConflict("DF");
593 		assertEquals(0, dco.getUpdated().size());
594 	}
595 
596 	@Test
597 	public void testDirectoryFileConflicts_7() throws Exception {
598 		// 7
599 		doit(mk("DF"), mk("DF"), mk("DF/DF"));
600 		assertUpdated("DF");
601 		assertRemoved("DF/DF");
602 
603 		cleanUpDF();
604 		setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
605 		go();
606 		assertRemoved("DF/DF/DF/DF/DF");
607 		assertUpdated("DF/DF");
608 
609 		cleanUpDF();
610 		setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
611 		writeTrashFile("DF/DF/DF/DF/DF", "diff");
612 		go();
613 		assertConflict("DF/DF/DF/DF/DF");
614 
615 		// assertUpdated("DF/DF");
616 		// Why do we expect an update on DF/DF. H==M,
617 		// H&M are files and index contains a dir, index
618 		// is dirty: that case is not in the table but
619 		// we cannot update DF/DF to a file, this would
620 		// require that we delete DF/DF/DF/DF/DF in workdir
621 		// throwing away unsaved contents.
622 		// This test would fail in DirCacheCheckoutTests.
623 	}
624 
625 	@Test
626 	public void testDirectoryFileConflicts_8() throws Exception {
627 		// 8
628 		setupCase(mk("DF"), mk("DF"), mk("DF/DF"));
629 		recursiveDelete(new File(db.getWorkTree(), "DF"));
630 		writeTrashFile("DF", "xy");
631 		go();
632 		assertConflict("DF/DF");
633 	}
634 
635 	@Test
636 	public void testDirectoryFileConflicts_9() throws Exception {
637 		// 9
638 		doit(mkmap("DF", "QP"), mkmap("DF", "QP"), mkmap("DF/DF", "DF/DF"));
639 		assertRemoved("DF/DF");
640 		assertUpdated("DF");
641 	}
642 
643 	@Test
644 	public void testDirectoryFileConflicts_10() throws Exception {
645 		// 10
646 		cleanUpDF();
647 		doit(mk("DF"), mk("DF/DF"), mk("DF/DF"));
648 		assertNoConflicts();
649 	}
650 
651 	@Test
652 	public void testDirectoryFileConflicts_11() throws Exception {
653 		// 11
654 		doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf"));
655 		assertConflict("DF/DF");
656 	}
657 
658 	@Test
659 	public void testDirectoryFileConflicts_12() throws Exception {
660 		// 12
661 		cleanUpDF();
662 		doit(mk("DF"), mk("DF/DF"), mk("DF"));
663 		assertRemoved("DF");
664 		assertUpdated("DF/DF");
665 	}
666 
667 	@Test
668 	public void testDirectoryFileConflicts_13() throws Exception {
669 		// 13
670 		cleanUpDF();
671 		setupCase(mk("DF"), mk("DF/DF"), mk("DF"));
672 		writeTrashFile("DF", "asdfsdf");
673 		go();
674 		assertConflict("DF");
675 		assertUpdated("DF/DF");
676 	}
677 
678 	@Test
679 	public void testDirectoryFileConflicts_14() throws Exception {
680 		// 14
681 		cleanUpDF();
682 		doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo"));
683 		assertConflict("DF");
684 		assertUpdated("DF/DF");
685 	}
686 
687 	@Test
688 	public void testDirectoryFileConflicts_15() throws Exception {
689 		// 15
690 		doit(mkmap(), mk("DF/DF"), mk("DF"));
691 
692 		// This test would fail in DirCacheCheckoutTests. I think this test is wrong,
693 		// it should check for conflicts according to rule 15
694 		// assertRemoved("DF");
695 
696 		assertUpdated("DF/DF");
697 	}
698 
699 	@Test
700 	public void testDirectoryFileConflicts_15b() throws Exception {
701 		// 15, take 2, just to check multi-leveled
702 		doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF"));
703 
704 		// I think this test is wrong, it should
705 		// check for conflicts according to rule 15
706 		// This test would fail in DirCacheCheckouts
707 		// assertRemoved("DF");
708 
709 		assertUpdated("DF/DF/DF/DF");
710 	}
711 
712 	@Test
713 	public void testDirectoryFileConflicts_16() throws Exception {
714 		// 16
715 		cleanUpDF();
716 		doit(mkmap(), mk("DF"), mk("DF/DF/DF"));
717 		assertRemoved("DF/DF/DF");
718 		assertUpdated("DF");
719 	}
720 
721 	@Test
722 	public void testDirectoryFileConflicts_17() throws Exception {
723 		// 17
724 		cleanUpDF();
725 		setupCase(mkmap(), mk("DF"), mk("DF/DF/DF"));
726 		writeTrashFile("DF/DF/DF", "asdf");
727 		go();
728 		assertConflict("DF/DF/DF");
729 
730 		// Why do we expect an update on DF. If we really update
731 		// DF and update also the working tree we would have to
732 		// overwrite a dirty file in the work-tree DF/DF/DF
733 		// This test would fail in DirCacheCheckout
734 		// assertUpdated("DF");
735 	}
736 
737 	@Test
738 	public void testDirectoryFileConflicts_18() throws Exception {
739 		// 18
740 		cleanUpDF();
741 		doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null);
742 		assertRemoved("DF/DF");
743 		assertUpdated("DF/DF/DF/DF");
744 	}
745 
746 	@Test
747 	public void testDirectoryFileConflicts_19() throws Exception {
748 		// 19
749 		cleanUpDF();
750 		doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null);
751 		assertRemoved("DF/DF/DF/DF");
752 		assertUpdated("DF/DF/DF");
753 	}
754 
755 	protected void cleanUpDF() throws Exception {
756 		tearDown();
757 		setUp();
758 		recursiveDelete(new File(trash, "DF"));
759 	}
760 
761 	protected void assertConflict(String s) {
762 		assertTrue(getConflicts().contains(s));
763 	}
764 
765 	protected void assertUpdated(String s) {
766 		assertTrue(getUpdated().containsKey(s));
767 	}
768 
769 	protected void assertRemoved(String s) {
770 		assertTrue(getRemoved().contains(s));
771 	}
772 
773 	protected void assertNoConflicts() {
774 		assertTrue(getConflicts().isEmpty());
775 	}
776 
777 	protected void doit(HashMap<String, String> h, HashMap<String, String> m, HashMap<String, String> i)
778 			throws IOException {
779 				setupCase(h, m, i);
780 				go();
781 			}
782 
783 	@Test
784 	public void testUntrackedConflicts() throws IOException {
785 		setupCase(null, mk("foo"), null);
786 		writeTrashFile("foo", "foo");
787 		go();
788 
789 		// test that we don't overwrite untracked files when there is a HEAD
790 		recursiveDelete(new File(trash, "foo"));
791 		setupCase(mk("other"), mkmap("other", "other", "foo", "foo"),
792 				mk("other"));
793 		writeTrashFile("foo", "bar");
794 		try {
795 			checkout();
796 			fail("didn't get the expected exception");
797 		} catch (CheckoutConflictException e) {
798 			assertConflict("foo");
799 			assertWorkDir(mkmap("foo", "bar", "other", "other"));
800 			assertIndex(mk("other"));
801 		}
802 
803 		// test that we don't overwrite untracked files when there is no HEAD
804 		recursiveDelete(new File(trash, "other"));
805 		recursiveDelete(new File(trash, "foo"));
806 		setupCase(null, mk("foo"), null);
807 		writeTrashFile("foo", "bar");
808 		try {
809 			checkout();
810 			fail("didn't get the expected exception");
811 		} catch (CheckoutConflictException e) {
812 			assertConflict("foo");
813 			assertWorkDir(mkmap("foo", "bar"));
814 			assertIndex(mkmap("other", "other"));
815 		}
816 
817 		// TODO: Why should we expect conflicts here?
818 		// H and M are empty and according to rule #5 of
819 		// the carry-over rules a dirty index is no reason
820 		// for a conflict. (I also feel it should be a
821 		// conflict because we are going to overwrite
822 		// unsaved content in the working tree
823 		// This test would fail in DirCacheCheckoutTest
824 		// assertConflict("foo");
825 
826 		recursiveDelete(new File(trash, "foo"));
827 		recursiveDelete(new File(trash, "other"));
828 		setupCase(null, mk("foo"), null);
829 		writeTrashFile("foo/bar/baz", "");
830 		writeTrashFile("foo/blahblah", "");
831 		go();
832 
833 		assertConflict("foo");
834 		assertConflict("foo/bar/baz");
835 		assertConflict("foo/blahblah");
836 
837 		recursiveDelete(new File(trash, "foo"));
838 
839 		setupCase(mkmap("foo/bar", "", "foo/baz", ""),
840 				mk("foo"), mkmap("foo/bar", "", "foo/baz", ""));
841 		assertTrue(new File(trash, "foo/bar").exists());
842 		go();
843 
844 		assertNoConflicts();
845 	}
846 
847 	@Test
848 	public void testCloseNameConflictsX0() throws IOException {
849 		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") );
850 		checkout();
851 		assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
852 		assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
853 		go();
854 		assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
855 		assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
856 		assertNoConflicts();
857 	}
858 
859 	@Test
860 	public void testCloseNameConflicts1() throws IOException {
861 		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") );
862 		checkout();
863 		assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
864 		assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
865 		go();
866 		assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
867 		assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
868 		assertNoConflicts();
869 	}
870 
871 	@Test
872 	public void testCheckoutHierarchy() throws IOException {
873 		setupCase(
874 				mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
875 						"e/g"),
876 				mkmap("a", "a2", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
877 						"e/g2"),
878 				mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
879 						"e/g3"));
880 		try {
881 			checkout();
882 		} catch (CheckoutConflictException e) {
883 			assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f",
884 					"e/f", "e/g", "e/g3"));
885 			assertConflict("e/g");
886 		}
887 	}
888 
889 	@Test
890 	public void testCheckoutOutChanges() throws IOException {
891 		setupCase(mk("foo"), mk("foo/bar"), mk("foo"));
892 		checkout();
893 		assertIndex(mk("foo/bar"));
894 		assertWorkDir(mk("foo/bar"));
895 
896 		assertFalse(new File(trash, "foo").isFile());
897 		assertTrue(new File(trash, "foo/bar").isFile());
898 		recursiveDelete(new File(trash, "foo"));
899 
900 		assertWorkDir(mkmap());
901 
902 		setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar"));
903 		checkout();
904 
905 		assertIndex(mk("foo"));
906 		assertWorkDir(mk("foo"));
907 
908 		assertFalse(new File(trash, "foo/bar").isFile());
909 		assertTrue(new File(trash, "foo").isFile());
910 
911 		setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar"));
912 
913 		assertIndex(mkmap("foo", "bar"));
914 		assertWorkDir(mkmap("foo", "bar"));
915 
916 		try {
917 			checkout();
918 			fail("did not throw exception");
919 		} catch (CheckoutConflictException e) {
920 			assertIndex(mkmap("foo", "bar"));
921 			assertWorkDir(mkmap("foo", "bar"));
922 		}
923 	}
924 
925 	@Test
926 	public void testCheckoutOutChangesAutoCRLFfalse() throws IOException {
927 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
928 		checkout();
929 		assertIndex(mkmap("foo/bar", "foo\nbar"));
930 		assertWorkDir(mkmap("foo/bar", "foo\nbar"));
931 	}
932 
933 	@Test
934 	public void testCheckoutOutChangesAutoCRLFInput() throws IOException {
935 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
936 		db.getConfig().setString("core", null, "autocrlf", "input");
937 		checkout();
938 		assertIndex(mkmap("foo/bar", "foo\nbar"));
939 		assertWorkDir(mkmap("foo/bar", "foo\nbar"));
940 	}
941 
942 	@Test
943 	public void testCheckoutOutChangesAutoCRLFtrue() throws IOException {
944 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
945 		db.getConfig().setString("core", null, "autocrlf", "true");
946 		checkout();
947 		assertIndex(mkmap("foo/bar", "foo\nbar"));
948 		assertWorkDir(mkmap("foo/bar", "foo\r\nbar"));
949 	}
950 
951 	@Test
952 	public void testCheckoutOutChangesAutoCRLFtrueBinary() throws IOException {
953 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nb\u0000ar"), mk("foo"));
954 		db.getConfig().setString("core", null, "autocrlf", "true");
955 		checkout();
956 		assertIndex(mkmap("foo/bar", "foo\nb\u0000ar"));
957 		assertWorkDir(mkmap("foo/bar", "foo\nb\u0000ar"));
958 	}
959 
960 	@Test
961 	public void testCheckoutUncachedChanges() throws IOException {
962 		setupCase(mk("foo"), mk("foo"), mk("foo"));
963 		writeTrashFile("foo", "otherData");
964 		checkout();
965 		assertIndex(mk("foo"));
966 		assertWorkDir(mkmap("foo", "otherData"));
967 		assertTrue(new File(trash, "foo").isFile());
968 	}
969 
970 	@Test
971 	public void testDontOverwriteDirtyFile() throws IOException {
972 		setupCase(mk("foo"), mk("other"), mk("foo"));
973 		writeTrashFile("foo", "different");
974 		try {
975 			checkout();
976 			fail("Didn't got the expected conflict");
977 		} catch (CheckoutConflictException e) {
978 			assertIndex(mk("foo"));
979 			assertWorkDir(mkmap("foo", "different"));
980 			assertEquals(Arrays.asList("foo"), getConflicts());
981 			assertTrue(new File(trash, "foo").isFile());
982 		}
983 	}
984 
985 	@Test
986 	public void testOverwriteUntrackedIgnoredFile() throws IOException,
987 			GitAPIException {
988 		String fname="file.txt";
989 		Git git = Git.wrap(db);
990 
991 		// Add a file
992 		writeTrashFile(fname, "a");
993 		git.add().addFilepattern(fname).call();
994 		git.commit().setMessage("create file").call();
995 
996 		// Create branch
997 		git.branchCreate().setName("side").call();
998 
999 		// Modify file
1000 		writeTrashFile(fname, "b");
1001 		git.add().addFilepattern(fname).call();
1002 		git.commit().setMessage("modify file").call();
1003 
1004 		// Switch branches
1005 		git.checkout().setName("side").call();
1006 		git.rm().addFilepattern(fname).call();
1007 		writeTrashFile(".gitignore", fname);
1008 		git.add().addFilepattern(".gitignore").call();
1009 		git.commit().setMessage("delete and ignore file").call();
1010 
1011 		writeTrashFile(fname, "Something different");
1012 		git.checkout().setName("master").call();
1013 		assertWorkDir(mkmap(fname, "b"));
1014 		assertTrue(git.status().call().isClean());
1015 	}
1016 
1017 	@Test
1018 	public void testFileModeChangeWithNoContentChangeUpdate() throws Exception {
1019 		if (!FS.DETECTED.supportsExecute())
1020 			return;
1021 
1022 		Git git = Git.wrap(db);
1023 
1024 		// Add non-executable file
1025 		File file = writeTrashFile("file.txt", "a");
1026 		git.add().addFilepattern("file.txt").call();
1027 		git.commit().setMessage("commit1").call();
1028 		assertFalse(db.getFS().canExecute(file));
1029 
1030 		// Create branch
1031 		git.branchCreate().setName("b1").call();
1032 
1033 		// Make file executable
1034 		db.getFS().setExecute(file, true);
1035 		git.add().addFilepattern("file.txt").call();
1036 		git.commit().setMessage("commit2").call();
1037 
1038 		// Verify executable and working directory is clean
1039 		Status status = git.status().call();
1040 		assertTrue(status.getModified().isEmpty());
1041 		assertTrue(status.getChanged().isEmpty());
1042 		assertTrue(db.getFS().canExecute(file));
1043 
1044 		// Switch branches
1045 		git.checkout().setName("b1").call();
1046 
1047 		// Verify not executable and working directory is clean
1048 		status = git.status().call();
1049 		assertTrue(status.getModified().isEmpty());
1050 		assertTrue(status.getChanged().isEmpty());
1051 		assertFalse(db.getFS().canExecute(file));
1052 	}
1053 
1054 	@Test
1055 	public void testFileModeChangeAndContentChangeConflict() throws Exception {
1056 		if (!FS.DETECTED.supportsExecute())
1057 			return;
1058 
1059 		Git git = Git.wrap(db);
1060 
1061 		// Add non-executable file
1062 		File file = writeTrashFile("file.txt", "a");
1063 		git.add().addFilepattern("file.txt").call();
1064 		git.commit().setMessage("commit1").call();
1065 		assertFalse(db.getFS().canExecute(file));
1066 
1067 		// Create branch
1068 		git.branchCreate().setName("b1").call();
1069 
1070 		// Make file executable
1071 		db.getFS().setExecute(file, true);
1072 		git.add().addFilepattern("file.txt").call();
1073 		git.commit().setMessage("commit2").call();
1074 
1075 		// Verify executable and working directory is clean
1076 		Status status = git.status().call();
1077 		assertTrue(status.getModified().isEmpty());
1078 		assertTrue(status.getChanged().isEmpty());
1079 		assertTrue(db.getFS().canExecute(file));
1080 
1081 		writeTrashFile("file.txt", "b");
1082 
1083 		// Switch branches
1084 		CheckoutCommand checkout = git.checkout().setName("b1");
1085 		try {
1086 			checkout.call();
1087 			fail("Checkout exception not thrown");
1088 		} catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
1089 			CheckoutResult result = checkout.getResult();
1090 			assertNotNull(result);
1091 			assertNotNull(result.getConflictList());
1092 			assertEquals(1, result.getConflictList().size());
1093 			assertTrue(result.getConflictList().contains("file.txt"));
1094 		}
1095 	}
1096 
1097 	@Test
1098 	public void testDirtyFileModeEqualHeadMerge()
1099 			throws Exception {
1100 		if (!FS.DETECTED.supportsExecute())
1101 			return;
1102 
1103 		Git git = Git.wrap(db);
1104 
1105 		// Add non-executable file
1106 		File file = writeTrashFile("file.txt", "a");
1107 		git.add().addFilepattern("file.txt").call();
1108 		git.commit().setMessage("commit1").call();
1109 		assertFalse(db.getFS().canExecute(file));
1110 
1111 		// Create branch
1112 		git.branchCreate().setName("b1").call();
1113 
1114 		// Create second commit and don't touch file
1115 		writeTrashFile("file2.txt", "");
1116 		git.add().addFilepattern("file2.txt").call();
1117 		git.commit().setMessage("commit2").call();
1118 
1119 		// stage a mode change
1120 		writeTrashFile("file.txt", "a");
1121 		db.getFS().setExecute(file, true);
1122 		git.add().addFilepattern("file.txt").call();
1123 
1124 		// dirty the file
1125 		writeTrashFile("file.txt", "b");
1126 
1127 		assertEquals(
1128 				"[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
1129 				indexState(CONTENT));
1130 		assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
1131 
1132 		// Switch branches and check that the dirty file survived in worktree
1133 		// and index
1134 		git.checkout().setName("b1").call();
1135 		assertEquals("[file.txt, mode:100755, content:a]", indexState(CONTENT));
1136 		assertWorkDir(mkmap("file.txt", "b"));
1137 	}
1138 
1139 	@Test
1140 	public void testDirtyFileModeEqualIndexMerge()
1141 			throws Exception {
1142 		if (!FS.DETECTED.supportsExecute())
1143 			return;
1144 
1145 		Git git = Git.wrap(db);
1146 
1147 		// Add non-executable file
1148 		File file = writeTrashFile("file.txt", "a");
1149 		git.add().addFilepattern("file.txt").call();
1150 		git.commit().setMessage("commit1").call();
1151 		assertFalse(db.getFS().canExecute(file));
1152 
1153 		// Create branch
1154 		git.branchCreate().setName("b1").call();
1155 
1156 		// Create second commit with executable file
1157 		file = writeTrashFile("file.txt", "b");
1158 		db.getFS().setExecute(file, true);
1159 		git.add().addFilepattern("file.txt").call();
1160 		git.commit().setMessage("commit2").call();
1161 
1162 		// stage the same content as in the branch we want to switch to
1163 		writeTrashFile("file.txt", "a");
1164 		db.getFS().setExecute(file, false);
1165 		git.add().addFilepattern("file.txt").call();
1166 
1167 		// dirty the file
1168 		writeTrashFile("file.txt", "c");
1169 		db.getFS().setExecute(file, true);
1170 
1171 		assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
1172 		assertWorkDir(mkmap("file.txt", "c"));
1173 
1174 		// Switch branches and check that the dirty file survived in worktree
1175 		// and index
1176 		git.checkout().setName("b1").call();
1177 		assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
1178 		assertWorkDir(mkmap("file.txt", "c"));
1179 	}
1180 
1181 	@Test
1182 	public void testFileModeChangeAndContentChangeNoConflict() throws Exception {
1183 		if (!FS.DETECTED.supportsExecute())
1184 			return;
1185 
1186 		Git git = Git.wrap(db);
1187 
1188 		// Add first file
1189 		File file1 = writeTrashFile("file1.txt", "a");
1190 		git.add().addFilepattern("file1.txt").call();
1191 		git.commit().setMessage("commit1").call();
1192 		assertFalse(db.getFS().canExecute(file1));
1193 
1194 		// Add second file
1195 		File file2 = writeTrashFile("file2.txt", "b");
1196 		git.add().addFilepattern("file2.txt").call();
1197 		git.commit().setMessage("commit2").call();
1198 		assertFalse(db.getFS().canExecute(file2));
1199 
1200 		// Create branch from first commit
1201 		assertNotNull(git.checkout().setCreateBranch(true).setName("b1")
1202 				.setStartPoint(Constants.HEAD + "~1").call());
1203 
1204 		// Change content and file mode in working directory and index
1205 		file1 = writeTrashFile("file1.txt", "c");
1206 		db.getFS().setExecute(file1, true);
1207 		git.add().addFilepattern("file1.txt").call();
1208 
1209 		// Switch back to 'master'
1210 		assertNotNull(git.checkout().setName(Constants.MASTER).call());
1211 	}
1212 
1213 	public void assertWorkDir(HashMap<String, String> i) throws CorruptObjectException,
1214 			IOException {
1215 		TreeWalk walk = new TreeWalk(db);
1216 		walk.setRecursive(true);
1217 		walk.addTree(new FileTreeIterator(db));
1218 		String expectedValue;
1219 		String path;
1220 		int nrFiles = 0;
1221 		FileTreeIterator ft;
1222 		while (walk.next()) {
1223 			ft = walk.getTree(0, FileTreeIterator.class);
1224 			path = ft.getEntryPathString();
1225 			expectedValue = i.get(path);
1226 			assertNotNull("found unexpected file for path " + path
1227 					+ " in workdir", expectedValue);
1228 			File file = new File(db.getWorkTree(), path);
1229 			assertTrue(file.exists());
1230 			if (file.isFile()) {
1231 				FileInputStream is = new FileInputStream(file);
1232 				byte[] buffer = new byte[(int) file.length()];
1233 				int offset = 0;
1234 				int numRead = 0;
1235 				while (offset < buffer.length
1236 						&& (numRead = is.read(buffer, offset, buffer.length
1237 								- offset)) >= 0) {
1238 					offset += numRead;
1239 				}
1240 				is.close();
1241 				assertArrayEquals("unexpected content for path " + path
1242 						+ " in workDir. ", buffer, i.get(path).getBytes());
1243 				nrFiles++;
1244 			}
1245 		}
1246 		assertEquals("WorkDir has not the right size.", i.size(), nrFiles);
1247 	}
1248 }