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.DirCacheCheckout.CheckoutMetadata;
69  import org.eclipse.jgit.dircache.DirCacheEditor;
70  import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
71  import org.eclipse.jgit.dircache.DirCacheEntry;
72  import org.eclipse.jgit.errors.CheckoutConflictException;
73  import org.eclipse.jgit.errors.CorruptObjectException;
74  import org.eclipse.jgit.errors.NoWorkTreeException;
75  import org.eclipse.jgit.junit.RepositoryTestCase;
76  import org.eclipse.jgit.junit.TestRepository;
77  import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
78  import org.eclipse.jgit.revwalk.RevCommit;
79  import org.eclipse.jgit.treewalk.FileTreeIterator;
80  import org.eclipse.jgit.treewalk.TreeWalk;
81  import org.eclipse.jgit.util.FS;
82  import org.eclipse.jgit.util.FileUtils;
83  import org.junit.Assume;
84  import org.junit.Test;
85  
86  public class DirCacheCheckoutTest extends RepositoryTestCase {
87  	private DirCacheCheckout dco;
88  	protected ObjectId theHead;
89  	protected ObjectId theMerge;
90  	private DirCache dirCache;
91  
92  	private void prescanTwoTrees(ObjectId head, ObjectId merge)
93  			throws IllegalStateException, IOException {
94  		DirCache dc = db.lockDirCache();
95  		try {
96  			dco = new DirCacheCheckout(db, head, dc, merge);
97  			dco.preScanTwoTrees();
98  		} finally {
99  			dc.unlock();
100 		}
101 	}
102 
103 	private void checkout() throws IOException {
104 		DirCache dc = db.lockDirCache();
105 		try {
106 			dco = new DirCacheCheckout(db, theHead, dc, theMerge);
107 			dco.checkout();
108 		} finally {
109 			dc.unlock();
110 		}
111 	}
112 
113 	private List<String> getRemoved() {
114 		return dco.getRemoved();
115 	}
116 
117 	private Map<String, CheckoutMetadata> getUpdated() {
118 		return dco.getUpdated();
119 	}
120 
121 	private List<String> getConflicts() {
122 		return dco.getConflicts();
123 	}
124 
125 	private static HashMap<String, String> mk(String a) {
126 		return mkmap(a, a);
127 	}
128 
129 	private static HashMap<String, String> mkmap(String... args) {
130 		if ((args.length % 2) > 0)
131 			throw new IllegalArgumentException("needs to be pairs");
132 
133 		HashMap<String, String> map = new HashMap<>();
134 		for (int i = 0; i < args.length; i += 2) {
135 			map.put(args[i], args[i + 1]);
136 		}
137 
138 		return map;
139 	}
140 
141 	@Test
142 	public void testResetHard() throws IOException, NoFilepatternException,
143 			GitAPIException {
144 		try (Git git = new Git(db)) {
145 			writeTrashFile("f", "f()");
146 			writeTrashFile("D/g", "g()");
147 			git.add().addFilepattern(".").call();
148 			git.commit().setMessage("inital").call();
149 			assertIndex(mkmap("f", "f()", "D/g", "g()"));
150 
151 			git.branchCreate().setName("topic").call();
152 
153 			writeTrashFile("f", "f()\nmaster");
154 			writeTrashFile("D/g", "g()\ng2()");
155 			writeTrashFile("E/h", "h()");
156 			git.add().addFilepattern(".").call();
157 			RevCommit master = git.commit().setMessage("master-1").call();
158 			assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
159 
160 			checkoutBranch("refs/heads/topic");
161 			assertIndex(mkmap("f", "f()", "D/g", "g()"));
162 
163 			writeTrashFile("f", "f()\nside");
164 			assertTrue(new File(db.getWorkTree(), "D/g").delete());
165 			writeTrashFile("G/i", "i()");
166 			git.add().addFilepattern(".").call();
167 			git.add().addFilepattern(".").setUpdate(true).call();
168 			RevCommit topic = git.commit().setMessage("topic-1").call();
169 			assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
170 
171 			writeTrashFile("untracked", "untracked");
172 
173 			resetHard(master);
174 			assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
175 			resetHard(topic);
176 			assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
177 			assertWorkDir(mkmap("f", "f()\nside", "G/i", "i()", "untracked",
178 					"untracked"));
179 
180 			assertEquals(MergeStatus.CONFLICTING, git.merge().include(master)
181 					.call().getMergeStatus());
182 			assertEquals(
183 					"[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]",
184 					indexState(0));
185 
186 			resetHard(master);
187 			assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
188 			assertWorkDir(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h",
189 					"h()", "untracked", "untracked"));
190 		}
191 	}
192 
193 	/**
194 	 * Reset hard from unclean condition.
195 	 * <p>
196 	 * WorkDir: Empty <br/>
197 	 * Index: f/g <br/>
198 	 * Merge: x
199 	 *
200 	 * @throws Exception
201 	 */
202 	@Test
203 	public void testResetHardFromIndexEntryWithoutFileToTreeWithoutFile()
204 			throws Exception {
205 		try (Git git = new Git(db)) {
206 			writeTrashFile("x", "x");
207 			git.add().addFilepattern("x").call();
208 			RevCommit id1 = git.commit().setMessage("c1").call();
209 
210 			writeTrashFile("f/g", "f/g");
211 			git.rm().addFilepattern("x").call();
212 			git.add().addFilepattern("f/g").call();
213 			git.commit().setMessage("c2").call();
214 			deleteTrashFile("f/g");
215 			deleteTrashFile("f");
216 
217 			// The actual test
218 			git.reset().setMode(ResetType.HARD).setRef(id1.getName()).call();
219 			assertIndex(mkmap("x", "x"));
220 		}
221 	}
222 
223 	/**
224 	 * Test first checkout in a repo
225 	 *
226 	 * @throws Exception
227 	 */
228 	@Test
229 	public void testInitialCheckout() throws Exception {
230 		try (Git git = new Git(db)) {
231 			TestRepository<Repository> db_t = new TestRepository<>(db);
232 			BranchBuilder master = db_t.branch("master");
233 			master.commit().add("f", "1").message("m0").create();
234 			assertFalse(new File(db.getWorkTree(), "f").exists());
235 			git.checkout().setName("master").call();
236 			assertTrue(new File(db.getWorkTree(), "f").exists());
237 		}
238 	}
239 
240 	private DirCacheCheckout resetHard(RevCommit commit)
241 			throws NoWorkTreeException,
242 			CorruptObjectException, IOException {
243 		DirCacheCheckout dc;
244 		dc = new DirCacheCheckout(db, null, db.lockDirCache(),
245 				commit.getTree());
246 		dc.setFailOnConflict(true);
247 		assertTrue(dc.checkout());
248 		return dc;
249 	}
250 
251 	private void assertIndex(HashMap<String, String> i)
252 			throws CorruptObjectException, IOException {
253 		String expectedValue;
254 		String path;
255 		DirCache read = DirCache.read(db.getIndexFile(), db.getFS());
256 
257 		assertEquals("Index has not the right size.", i.size(),
258 				read.getEntryCount());
259 		for (int j = 0; j < read.getEntryCount(); j++) {
260 			path = read.getEntry(j).getPathString();
261 			expectedValue = i.get(path);
262 			assertNotNull("found unexpected entry for path " + path
263 					+ " in index", expectedValue);
264 			assertTrue("unexpected content for path " + path
265 					+ " in index. Expected: <" + expectedValue + ">",
266 					Arrays.equals(db.open(read.getEntry(j).getObjectId())
267 							.getCachedBytes(), i.get(path).getBytes()));
268 		}
269 	}
270 
271 	@Test
272 	public void testRules1thru3_NoIndexEntry() throws IOException {
273 		ObjectId head = buildTree(mk("foo"));
274 		ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE,
275 				new byte[0]);
276 
277 		prescanTwoTrees(head, merge);
278 
279 		assertTrue(getRemoved().contains("foo"));
280 
281 		prescanTwoTrees(merge, head);
282 
283 		assertTrue(getUpdated().containsKey("foo"));
284 
285 		merge = buildTree(mkmap("foo", "a"));
286 
287 		prescanTwoTrees(head, merge);
288 
289 		assertConflict("foo");
290 	}
291 
292 	void setupCase(HashMap<String, String> headEntries, HashMap<String, String> mergeEntries, HashMap<String, String> indexEntries) throws IOException {
293 		theHead = buildTree(headEntries);
294 		theMerge = buildTree(mergeEntries);
295 		buildIndex(indexEntries);
296 	}
297 
298 	private void buildIndex(HashMap<String, String> indexEntries) throws IOException {
299 		dirCache = new DirCache(db.getIndexFile(), db.getFS());
300 		if (indexEntries != null) {
301 			assertTrue(dirCache.lock());
302 			DirCacheEditor editor = dirCache.editor();
303 			for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) {
304 				writeTrashFile(e.getKey(), e.getValue());
305 				ObjectInserter inserter = db.newObjectInserter();
306 				final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
307 						Constants.encode(e.getValue()));
308 				editor.add(new DirCacheEditor.DeletePath(e.getKey()));
309 				editor.add(new DirCacheEditor.PathEdit(e.getKey()) {
310 					@Override
311 					public void apply(DirCacheEntry ent) {
312 						ent.setFileMode(FileMode.REGULAR_FILE);
313 						ent.setObjectId(id);
314 						ent.setUpdateNeeded(false);
315 					}
316 				});
317 			}
318 			assertTrue(editor.commit());
319 		}
320 
321 	}
322 
323 	static final class AddEdit extends PathEdit {
324 
325 		private final ObjectId data;
326 
327 		private final long length;
328 
329 		public AddEdit(String entryPath, ObjectId data, long length) {
330 			super(entryPath);
331 			this.data = data;
332 			this.length = length;
333 		}
334 
335 		@Override
336 		public void apply(DirCacheEntry ent) {
337 			ent.setFileMode(FileMode.REGULAR_FILE);
338 			ent.setLength(length);
339 			ent.setObjectId(data);
340 		}
341 
342 	}
343 
344 	private ObjectId buildTree(HashMap<String, String> headEntries)
345 			throws IOException {
346 		DirCache lockDirCache = DirCache.newInCore();
347 		// assertTrue(lockDirCache.lock());
348 		DirCacheEditor editor = lockDirCache.editor();
349 		if (headEntries != null) {
350 			for (java.util.Map.Entry<String, String> e : headEntries.entrySet()) {
351 				AddEdit addEdit = new AddEdit(e.getKey(),
352 						genSha1(e.getValue()), e.getValue().length());
353 				editor.add(addEdit);
354 			}
355 		}
356 		editor.finish();
357 		return lockDirCache.writeTree(db.newObjectInserter());
358 	}
359 
360 	ObjectId genSha1(String data) {
361 		try (ObjectInserter w = db.newObjectInserter()) {
362 			ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
363 			w.flush();
364 			return id;
365 		} catch (IOException e) {
366 			fail(e.toString());
367 		}
368 		return null;
369 	}
370 
371 	protected void go() throws IllegalStateException, IOException {
372 		prescanTwoTrees(theHead, theMerge);
373 	}
374 
375 	@Test
376 	public void testRules4thru13_IndexEntryNotInHead() throws IOException {
377 		// rules 4 and 5
378 		HashMap<String, String> idxMap;
379 
380 		idxMap = new HashMap<>();
381 		idxMap.put("foo", "foo");
382 		setupCase(null, null, idxMap);
383 		go();
384 
385 		assertTrue(getUpdated().isEmpty());
386 		assertTrue(getRemoved().isEmpty());
387 		assertTrue(getConflicts().isEmpty());
388 
389 		// rules 6 and 7
390 		idxMap = new HashMap<>();
391 		idxMap.put("foo", "foo");
392 		setupCase(null, idxMap, idxMap);
393 		go();
394 
395 		assertAllEmpty();
396 
397 		// rules 8 and 9
398 		HashMap<String, String> mergeMap;
399 		mergeMap = new HashMap<>();
400 
401 		mergeMap.put("foo", "merge");
402 		setupCase(null, mergeMap, idxMap);
403 		go();
404 
405 		assertTrue(getUpdated().isEmpty());
406 		assertTrue(getRemoved().isEmpty());
407 		assertTrue(getConflicts().contains("foo"));
408 
409 		// rule 10
410 
411 		HashMap<String, String> headMap = new HashMap<>();
412 		headMap.put("foo", "foo");
413 		setupCase(headMap, null, idxMap);
414 		go();
415 
416 		assertTrue(getRemoved().contains("foo"));
417 		assertTrue(getUpdated().isEmpty());
418 		assertTrue(getConflicts().isEmpty());
419 
420 		// rule 11
421 		setupCase(headMap, null, idxMap);
422 		assertTrue(new File(trash, "foo").delete());
423 		writeTrashFile("foo", "bar");
424 		db.readDirCache().getEntry(0).setUpdateNeeded(true);
425 		go();
426 
427 		assertTrue(getRemoved().isEmpty());
428 		assertTrue(getUpdated().isEmpty());
429 		assertTrue(getConflicts().contains("foo"));
430 
431 		// rule 12 & 13
432 		headMap.put("foo", "head");
433 		setupCase(headMap, null, idxMap);
434 		go();
435 
436 		assertTrue(getRemoved().isEmpty());
437 		assertTrue(getUpdated().isEmpty());
438 		assertTrue(getConflicts().contains("foo"));
439 
440 		// rules 14 & 15
441 		setupCase(headMap, headMap, idxMap);
442 		go();
443 
444 		assertAllEmpty();
445 
446 		// rules 16 & 17
447 		setupCase(headMap, mergeMap, idxMap); go();
448 		assertTrue(getConflicts().contains("foo"));
449 
450 		// rules 18 & 19
451 		setupCase(headMap, idxMap, idxMap); go();
452 		assertAllEmpty();
453 
454 		// rule 20
455 		setupCase(idxMap, mergeMap, idxMap); go();
456 		assertTrue(getUpdated().containsKey("foo"));
457 
458 		// rules 21
459 		setupCase(idxMap, mergeMap, idxMap);
460 		assertTrue(new File(trash, "foo").delete());
461 		writeTrashFile("foo", "bar");
462 		db.readDirCache().getEntry(0).setUpdateNeeded(true);
463 		go();
464 		assertTrue(getConflicts().contains("foo"));
465 	}
466 
467 	private void assertAllEmpty() {
468 		assertTrue(getRemoved().isEmpty());
469 		assertTrue(getUpdated().isEmpty());
470 		assertTrue(getConflicts().isEmpty());
471 	}
472 
473 	/*-
474 	 * Directory/File Conflict cases:
475 	 * It's entirely possible that in practice a number of these may be equivalent
476 	 * to the cases described in git-read-tree.txt. As long as it does the right thing,
477 	 * that's all I care about. These are basically reverse-engineered from
478 	 * what git currently does. If there are tests for these in git, it's kind of
479 	 * hard to track them all down...
480 	 *
481 	 *     H        I       M     Clean     H==M     H==I    I==M         Result
482 	 *     ------------------------------------------------------------------
483 	 *1    D        D       F       Y         N       Y       N           Update
484 	 *2    D        D       F       N         N       Y       N           Conflict
485 	 *3    D        F       D                 Y       N       N           Keep
486 	 *4    D        F       D                 N       N       N           Conflict
487 	 *5    D        F       F       Y         N       N       Y           Keep
488 	 *5b   D        F       F       Y         N       N       N           Conflict
489 	 *6    D        F       F       N         N       N       Y           Keep
490 	 *6b   D        F       F       N         N       N       N           Conflict
491 	 *7    F        D       F       Y         Y       N       N           Update
492 	 *8    F        D       F       N         Y       N       N           Conflict
493 	 *9    F        D       F       Y         N       N       N           Update
494 	 *10   F        D       D                 N       N       Y           Keep
495 	 *11   F        D       D                 N       N       N           Conflict
496 	 *12   F        F       D       Y         N       Y       N           Update
497 	 *13   F        F       D       N         N       Y       N           Conflict
498 	 *14   F        F       D                 N       N       N           Conflict
499 	 *15   0        F       D                 N       N       N           Conflict
500 	 *16   0        D       F       Y         N       N       N           Update
501 	 *17   0        D       F                 N       N       N           Conflict
502 	 *18   F        0       D                                             Update
503 	 *19   D        0       F                                             Update
504 	 */
505 	@Test
506 	public void testDirectoryFileSimple() throws IOException {
507 		ObjectId treeDF = buildTree(mkmap("DF", "DF"));
508 		ObjectId treeDFDF = buildTree(mkmap("DF/DF", "DF/DF"));
509 		buildIndex(mkmap("DF", "DF"));
510 
511 		prescanTwoTrees(treeDF, treeDFDF);
512 
513 		assertTrue(getRemoved().contains("DF"));
514 		assertTrue(getUpdated().containsKey("DF/DF"));
515 
516 		recursiveDelete(new File(trash, "DF"));
517 		buildIndex(mkmap("DF/DF", "DF/DF"));
518 
519 		prescanTwoTrees(treeDFDF, treeDF);
520 		assertTrue(getRemoved().contains("DF/DF"));
521 		assertTrue(getUpdated().containsKey("DF"));
522 	}
523 
524 	@Test
525 	public void testDirectoryFileConflicts_1() throws Exception {
526 		// 1
527 		doit(mk("DF/DF"), mk("DF"), mk("DF/DF"));
528 		assertNoConflicts();
529 		assertUpdated("DF");
530 		assertRemoved("DF/DF");
531 	}
532 
533 	@Test
534 	public void testDirectoryFileConflicts_2() throws Exception {
535 		// 2
536 		setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF"));
537 		writeTrashFile("DF/DF", "different");
538 		go();
539 		assertConflict("DF/DF");
540 
541 	}
542 
543 	@Test
544 	public void testDirectoryFileConflicts_3() throws Exception {
545 		// 3
546 		doit(mk("DF/DF"), mk("DF/DF"), mk("DF"));
547 		assertNoConflicts();
548 	}
549 
550 	@Test
551 	public void testDirectoryFileConflicts_4() throws Exception {
552 		// 4 (basically same as 3, just with H and M different)
553 		doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF"));
554 		assertConflict("DF/DF");
555 
556 	}
557 
558 	@Test
559 	public void testDirectoryFileConflicts_5() throws Exception {
560 		// 5
561 		doit(mk("DF/DF"), mk("DF"), mk("DF"));
562 		assertRemoved("DF/DF");
563 		assertEquals(0, dco.getConflicts().size());
564 		assertEquals(0, dco.getUpdated().size());
565 	}
566 
567 	@Test
568 	public void testDirectoryFileConflicts_5b() throws Exception {
569 		// 5
570 		doit(mk("DF/DF"), mkmap("DF", "different"), mk("DF"));
571 		assertRemoved("DF/DF");
572 		assertConflict("DF");
573 		assertEquals(0, dco.getUpdated().size());
574 	}
575 
576 	@Test
577 	public void testDirectoryFileConflicts_6() throws Exception {
578 		// 6
579 		setupCase(mk("DF/DF"), mk("DF"), mk("DF"));
580 		writeTrashFile("DF", "different");
581 		go();
582 		assertRemoved("DF/DF");
583 		assertEquals(0, dco.getConflicts().size());
584 		assertEquals(0, dco.getUpdated().size());
585 	}
586 
587 	@Test
588 	public void testDirectoryFileConflicts_6b() throws Exception {
589 		// 6
590 		setupCase(mk("DF/DF"), mk("DF"), mkmap("DF", "different"));
591 		writeTrashFile("DF", "again different");
592 		go();
593 		assertRemoved("DF/DF");
594 		assertConflict("DF");
595 		assertEquals(0, dco.getUpdated().size());
596 	}
597 
598 	@Test
599 	public void testDirectoryFileConflicts_7() throws Exception {
600 		// 7
601 		doit(mk("DF"), mk("DF"), mk("DF/DF"));
602 		assertUpdated("DF");
603 		assertRemoved("DF/DF");
604 
605 		cleanUpDF();
606 		setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
607 		go();
608 		assertRemoved("DF/DF/DF/DF/DF");
609 		assertUpdated("DF/DF");
610 
611 		cleanUpDF();
612 		setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
613 		writeTrashFile("DF/DF/DF/DF/DF", "diff");
614 		go();
615 		assertConflict("DF/DF/DF/DF/DF");
616 
617 		// assertUpdated("DF/DF");
618 		// Why do we expect an update on DF/DF. H==M,
619 		// H&M are files and index contains a dir, index
620 		// is dirty: that case is not in the table but
621 		// we cannot update DF/DF to a file, this would
622 		// require that we delete DF/DF/DF/DF/DF in workdir
623 		// throwing away unsaved contents.
624 		// This test would fail in DirCacheCheckoutTests.
625 	}
626 
627 	@Test
628 	public void testDirectoryFileConflicts_8() throws Exception {
629 		// 8
630 		setupCase(mk("DF"), mk("DF"), mk("DF/DF"));
631 		recursiveDelete(new File(db.getWorkTree(), "DF"));
632 		writeTrashFile("DF", "xy");
633 		go();
634 		assertConflict("DF/DF");
635 	}
636 
637 	@Test
638 	public void testDirectoryFileConflicts_9() throws Exception {
639 		// 9
640 		doit(mkmap("DF", "QP"), mkmap("DF", "QP"), mkmap("DF/DF", "DF/DF"));
641 		assertRemoved("DF/DF");
642 		assertUpdated("DF");
643 	}
644 
645 	@Test
646 	public void testDirectoryFileConflicts_10() throws Exception {
647 		// 10
648 		cleanUpDF();
649 		doit(mk("DF"), mk("DF/DF"), mk("DF/DF"));
650 		assertNoConflicts();
651 	}
652 
653 	@Test
654 	public void testDirectoryFileConflicts_11() throws Exception {
655 		// 11
656 		doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf"));
657 		assertConflict("DF/DF");
658 	}
659 
660 	@Test
661 	public void testDirectoryFileConflicts_12() throws Exception {
662 		// 12
663 		cleanUpDF();
664 		doit(mk("DF"), mk("DF/DF"), mk("DF"));
665 		assertRemoved("DF");
666 		assertUpdated("DF/DF");
667 	}
668 
669 	@Test
670 	public void testDirectoryFileConflicts_13() throws Exception {
671 		// 13
672 		cleanUpDF();
673 		setupCase(mk("DF"), mk("DF/DF"), mk("DF"));
674 		writeTrashFile("DF", "asdfsdf");
675 		go();
676 		assertConflict("DF");
677 		assertUpdated("DF/DF");
678 	}
679 
680 	@Test
681 	public void testDirectoryFileConflicts_14() throws Exception {
682 		// 14
683 		cleanUpDF();
684 		doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo"));
685 		assertConflict("DF");
686 		assertUpdated("DF/DF");
687 	}
688 
689 	@Test
690 	public void testDirectoryFileConflicts_15() throws Exception {
691 		// 15
692 		doit(mkmap(), mk("DF/DF"), mk("DF"));
693 
694 		// This test would fail in DirCacheCheckoutTests. I think this test is wrong,
695 		// it should check for conflicts according to rule 15
696 		// assertRemoved("DF");
697 
698 		assertUpdated("DF/DF");
699 	}
700 
701 	@Test
702 	public void testDirectoryFileConflicts_15b() throws Exception {
703 		// 15, take 2, just to check multi-leveled
704 		doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF"));
705 
706 		// I think this test is wrong, it should
707 		// check for conflicts according to rule 15
708 		// This test would fail in DirCacheCheckouts
709 		// assertRemoved("DF");
710 
711 		assertUpdated("DF/DF/DF/DF");
712 	}
713 
714 	@Test
715 	public void testDirectoryFileConflicts_16() throws Exception {
716 		// 16
717 		cleanUpDF();
718 		doit(mkmap(), mk("DF"), mk("DF/DF/DF"));
719 		assertRemoved("DF/DF/DF");
720 		assertUpdated("DF");
721 	}
722 
723 	@Test
724 	public void testDirectoryFileConflicts_17() throws Exception {
725 		// 17
726 		cleanUpDF();
727 		setupCase(mkmap(), mk("DF"), mk("DF/DF/DF"));
728 		writeTrashFile("DF/DF/DF", "asdf");
729 		go();
730 		assertConflict("DF/DF/DF");
731 
732 		// Why do we expect an update on DF. If we really update
733 		// DF and update also the working tree we would have to
734 		// overwrite a dirty file in the work-tree DF/DF/DF
735 		// This test would fail in DirCacheCheckout
736 		// assertUpdated("DF");
737 	}
738 
739 	@Test
740 	public void testDirectoryFileConflicts_18() throws Exception {
741 		// 18
742 		cleanUpDF();
743 		doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null);
744 		assertRemoved("DF/DF");
745 		assertUpdated("DF/DF/DF/DF");
746 	}
747 
748 	@Test
749 	public void testDirectoryFileConflicts_19() throws Exception {
750 		// 19
751 		cleanUpDF();
752 		doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null);
753 		assertRemoved("DF/DF/DF/DF");
754 		assertUpdated("DF/DF/DF");
755 	}
756 
757 	protected void cleanUpDF() throws Exception {
758 		tearDown();
759 		setUp();
760 		recursiveDelete(new File(trash, "DF"));
761 	}
762 
763 	protected void assertConflict(String s) {
764 		assertTrue(getConflicts().contains(s));
765 	}
766 
767 	protected void assertUpdated(String s) {
768 		assertTrue(getUpdated().containsKey(s));
769 	}
770 
771 	protected void assertRemoved(String s) {
772 		assertTrue(getRemoved().contains(s));
773 	}
774 
775 	protected void assertNoConflicts() {
776 		assertTrue(getConflicts().isEmpty());
777 	}
778 
779 	protected void doit(HashMap<String, String> h, HashMap<String, String> m, HashMap<String, String> i)
780 			throws IOException {
781 				setupCase(h, m, i);
782 				go();
783 			}
784 
785 	@Test
786 	public void testUntrackedConflicts() throws IOException {
787 		setupCase(null, mk("foo"), null);
788 		writeTrashFile("foo", "foo");
789 		go();
790 
791 		// test that we don't overwrite untracked files when there is a HEAD
792 		recursiveDelete(new File(trash, "foo"));
793 		setupCase(mk("other"), mkmap("other", "other", "foo", "foo"),
794 				mk("other"));
795 		writeTrashFile("foo", "bar");
796 		try {
797 			checkout();
798 			fail("didn't get the expected exception");
799 		} catch (CheckoutConflictException e) {
800 			assertConflict("foo");
801 			assertEquals("foo", e.getConflictingFiles()[0]);
802 			assertWorkDir(mkmap("foo", "bar", "other", "other"));
803 			assertIndex(mk("other"));
804 		}
805 
806 		// test that we don't overwrite untracked files when there is no HEAD
807 		recursiveDelete(new File(trash, "other"));
808 		recursiveDelete(new File(trash, "foo"));
809 		setupCase(null, mk("foo"), null);
810 		writeTrashFile("foo", "bar");
811 		try {
812 			checkout();
813 			fail("didn't get the expected exception");
814 		} catch (CheckoutConflictException e) {
815 			assertConflict("foo");
816 			assertWorkDir(mkmap("foo", "bar"));
817 			assertIndex(mkmap("other", "other"));
818 		}
819 
820 		// TODO: Why should we expect conflicts here?
821 		// H and M are empty and according to rule #5 of
822 		// the carry-over rules a dirty index is no reason
823 		// for a conflict. (I also feel it should be a
824 		// conflict because we are going to overwrite
825 		// unsaved content in the working tree
826 		// This test would fail in DirCacheCheckoutTest
827 		// assertConflict("foo");
828 
829 		recursiveDelete(new File(trash, "foo"));
830 		recursiveDelete(new File(trash, "other"));
831 		setupCase(null, mk("foo"), null);
832 		writeTrashFile("foo/bar/baz", "");
833 		writeTrashFile("foo/blahblah", "");
834 		go();
835 
836 		assertConflict("foo");
837 		assertConflict("foo/bar/baz");
838 		assertConflict("foo/blahblah");
839 
840 		recursiveDelete(new File(trash, "foo"));
841 
842 		setupCase(mkmap("foo/bar", "", "foo/baz", ""),
843 				mk("foo"), mkmap("foo/bar", "", "foo/baz", ""));
844 		assertTrue(new File(trash, "foo/bar").exists());
845 		go();
846 
847 		assertNoConflicts();
848 	}
849 
850 	@Test
851 	public void testCloseNameConflictsX0() throws IOException {
852 		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") );
853 		checkout();
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 		go();
857 		assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
858 		assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
859 		assertNoConflicts();
860 	}
861 
862 	@Test
863 	public void testCloseNameConflicts1() throws IOException {
864 		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") );
865 		checkout();
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 		go();
869 		assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
870 		assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
871 		assertNoConflicts();
872 	}
873 
874 	@Test
875 	public void testCheckoutHierarchy() throws IOException {
876 		setupCase(
877 				mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
878 						"e/g"),
879 				mkmap("a", "a2", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
880 						"e/g2"),
881 				mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
882 						"e/g3"));
883 		try {
884 			checkout();
885 		} catch (CheckoutConflictException e) {
886 			assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f",
887 					"e/f", "e/g", "e/g3"));
888 			assertConflict("e/g");
889 			assertEquals("e/g", e.getConflictingFiles()[0]);
890 		}
891 	}
892 
893 	@Test
894 	public void testCheckoutOutChanges() throws IOException {
895 		setupCase(mk("foo"), mk("foo/bar"), mk("foo"));
896 		checkout();
897 		assertIndex(mk("foo/bar"));
898 		assertWorkDir(mk("foo/bar"));
899 
900 		assertFalse(new File(trash, "foo").isFile());
901 		assertTrue(new File(trash, "foo/bar").isFile());
902 		recursiveDelete(new File(trash, "foo"));
903 
904 		assertWorkDir(mkmap());
905 
906 		setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar"));
907 		checkout();
908 
909 		assertIndex(mk("foo"));
910 		assertWorkDir(mk("foo"));
911 
912 		assertFalse(new File(trash, "foo/bar").isFile());
913 		assertTrue(new File(trash, "foo").isFile());
914 
915 		setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar"));
916 
917 		assertIndex(mkmap("foo", "bar"));
918 		assertWorkDir(mkmap("foo", "bar"));
919 
920 		try {
921 			checkout();
922 			fail("did not throw exception");
923 		} catch (CheckoutConflictException e) {
924 			assertIndex(mkmap("foo", "bar"));
925 			assertWorkDir(mkmap("foo", "bar"));
926 		}
927 	}
928 
929 	@Test
930 	public void testCheckoutChangeLinkToEmptyDir() throws Exception {
931 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
932 		String fname = "was_file";
933 		Git git = Git.wrap(db);
934 
935 		// Add a file
936 		writeTrashFile(fname, "a");
937 		git.add().addFilepattern(fname).call();
938 
939 		// Add a link to file
940 		String linkName = "link";
941 		File link = writeLink(linkName, fname).toFile();
942 		git.add().addFilepattern(linkName).call();
943 		git.commit().setMessage("Added file and link").call();
944 
945 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
946 
947 		// replace link with empty directory
948 		FileUtils.delete(link);
949 		FileUtils.mkdir(link);
950 		assertTrue("Link must be a directory now", link.isDirectory());
951 
952 		// modify file
953 		writeTrashFile(fname, "b");
954 		assertWorkDir(mkmap(fname, "b", linkName, "/"));
955 
956 		// revert both paths to HEAD state
957 		git.checkout().setStartPoint(Constants.HEAD)
958 				.addPath(fname).addPath(linkName).call();
959 
960 		assertWorkDir(mkmap(fname, "a", linkName, "a"));
961 
962 		Status st = git.status().call();
963 		assertTrue(st.isClean());
964 	}
965 
966 	@Test
967 	public void testCheckoutChangeLinkToEmptyDirs() throws Exception {
968 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
969 		String fname = "was_file";
970 		Git git = Git.wrap(db);
971 
972 		// Add a file
973 		writeTrashFile(fname, "a");
974 		git.add().addFilepattern(fname).call();
975 
976 		// Add a link to file
977 		String linkName = "link";
978 		File link = writeLink(linkName, fname).toFile();
979 		git.add().addFilepattern(linkName).call();
980 		git.commit().setMessage("Added file and link").call();
981 
982 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
983 
984 		// replace link with directory containing only directories, no files
985 		FileUtils.delete(link);
986 		FileUtils.mkdirs(new File(link, "dummyDir"));
987 		assertTrue("Link must be a directory now", link.isDirectory());
988 
989 		assertFalse("Must not delete non empty directory", link.delete());
990 
991 		// modify file
992 		writeTrashFile(fname, "b");
993 		assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/"));
994 
995 		// revert both paths to HEAD state
996 		git.checkout().setStartPoint(Constants.HEAD)
997 				.addPath(fname).addPath(linkName).call();
998 
999 		assertWorkDir(mkmap(fname, "a", linkName, "a"));
1000 
1001 		Status st = git.status().call();
1002 		assertTrue(st.isClean());
1003 	}
1004 
1005 	@Test
1006 	public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception {
1007 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1008 		String fname = "file";
1009 		Git git = Git.wrap(db);
1010 
1011 		// Add a file
1012 		writeTrashFile(fname, "a");
1013 		git.add().addFilepattern(fname).call();
1014 
1015 		// Add a link to file
1016 		String linkName = "link";
1017 		File link = writeLink(linkName, fname).toFile();
1018 		git.add().addFilepattern(linkName).call();
1019 		git.commit().setMessage("Added file and link").call();
1020 
1021 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
1022 
1023 		// replace link with directory containing only directories, no files
1024 		FileUtils.delete(link);
1025 
1026 		// create but do not add a file in the new directory to the index
1027 		writeTrashFile(linkName + "/dir1", "file1", "c");
1028 
1029 		// create but do not add a file in the new directory to the index
1030 		writeTrashFile(linkName + "/dir2", "file2", "d");
1031 
1032 		assertTrue("File must be a directory now", link.isDirectory());
1033 		assertFalse("Must not delete non empty directory", link.delete());
1034 
1035 		// 2 extra files are created
1036 		assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1037 				linkName + "/dir2/file2", "d"));
1038 
1039 		// revert path to HEAD state
1040 		git.checkout().setStartPoint(Constants.HEAD).addPath(linkName).call();
1041 
1042 		// expect only the one added to the index
1043 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
1044 
1045 		Status st = git.status().call();
1046 		assertTrue(st.isClean());
1047 	}
1048 
1049 	@Test
1050 	public void testCheckoutChangeLinkToNonEmptyDirsAndNewIndexEntry()
1051 			throws Exception {
1052 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1053 		String fname = "file";
1054 		Git git = Git.wrap(db);
1055 
1056 		// Add a file
1057 		writeTrashFile(fname, "a");
1058 		git.add().addFilepattern(fname).call();
1059 
1060 		// Add a link to file
1061 		String linkName = "link";
1062 		File link = writeLink(linkName, fname).toFile();
1063 		git.add().addFilepattern(linkName).call();
1064 		git.commit().setMessage("Added file and link").call();
1065 
1066 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
1067 
1068 		// replace link with directory containing only directories, no files
1069 		FileUtils.delete(link);
1070 
1071 		// create and add a file in the new directory to the index
1072 		writeTrashFile(linkName + "/dir1", "file1", "c");
1073 		git.add().addFilepattern(linkName + "/dir1/file1").call();
1074 
1075 		// create but do not add a file in the new directory to the index
1076 		writeTrashFile(linkName + "/dir2", "file2", "d");
1077 
1078 		assertTrue("File must be a directory now", link.isDirectory());
1079 		assertFalse("Must not delete non empty directory", link.delete());
1080 
1081 		// 2 extra files are created
1082 		assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1083 				linkName + "/dir2/file2", "d"));
1084 
1085 		// revert path to HEAD state
1086 		git.checkout().setStartPoint(Constants.HEAD).addPath(linkName).call();
1087 
1088 		// original file and link
1089 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
1090 
1091 		Status st = git.status().call();
1092 		assertTrue(st.isClean());
1093 	}
1094 
1095 	@Test
1096 	public void testCheckoutChangeFileToEmptyDir() throws Exception {
1097 		String fname = "was_file";
1098 		Git git = Git.wrap(db);
1099 
1100 		// Add a file
1101 		File file = writeTrashFile(fname, "a");
1102 		git.add().addFilepattern(fname).call();
1103 		git.commit().setMessage("Added file").call();
1104 
1105 		// replace file with empty directory
1106 		FileUtils.delete(file);
1107 		FileUtils.mkdir(file);
1108 		assertTrue("File must be a directory now", file.isDirectory());
1109 
1110 		assertWorkDir(mkmap(fname, "/"));
1111 
1112 		// revert path to HEAD state
1113 		git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1114 
1115 		assertWorkDir(mkmap(fname, "a"));
1116 
1117 		Status st = git.status().call();
1118 		assertTrue(st.isClean());
1119 	}
1120 
1121 	@Test
1122 	public void testCheckoutChangeFileToEmptyDirs() throws Exception {
1123 		String fname = "was_file";
1124 		Git git = Git.wrap(db);
1125 
1126 		// Add a file
1127 		File file = writeTrashFile(fname, "a");
1128 		git.add().addFilepattern(fname).call();
1129 		git.commit().setMessage("Added file").call();
1130 
1131 		// replace file with directory containing only directories, no files
1132 		FileUtils.delete(file);
1133 		FileUtils.mkdirs(new File(file, "dummyDir"));
1134 		assertTrue("File must be a directory now", file.isDirectory());
1135 		assertFalse("Must not delete non empty directory", file.delete());
1136 
1137 		assertWorkDir(mkmap(fname + "/dummyDir", "/"));
1138 
1139 		// revert path to HEAD state
1140 		git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1141 
1142 		assertWorkDir(mkmap(fname, "a"));
1143 
1144 		Status st = git.status().call();
1145 		assertTrue(st.isClean());
1146 	}
1147 
1148 	@Test
1149 	public void testCheckoutChangeFileToNonEmptyDirs() throws Exception {
1150 		String fname = "was_file";
1151 		Git git = Git.wrap(db);
1152 
1153 		// Add a file
1154 		File file = writeTrashFile(fname, "a");
1155 		git.add().addFilepattern(fname).call();
1156 		git.commit().setMessage("Added file").call();
1157 
1158 		assertWorkDir(mkmap(fname, "a"));
1159 
1160 		// replace file with directory containing only directories, no files
1161 		FileUtils.delete(file);
1162 
1163 		// create but do not add a file in the new directory to the index
1164 		writeTrashFile(fname + "/dir1", "file1", "c");
1165 
1166 		// create but do not add a file in the new directory to the index
1167 		writeTrashFile(fname + "/dir2", "file2", "d");
1168 
1169 		assertTrue("File must be a directory now", file.isDirectory());
1170 		assertFalse("Must not delete non empty directory", file.delete());
1171 
1172 		// 2 extra files are created
1173 		assertWorkDir(
1174 				mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
1175 
1176 		// revert path to HEAD state
1177 		git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1178 
1179 		// expect only the one added to the index
1180 		assertWorkDir(mkmap(fname, "a"));
1181 
1182 		Status st = git.status().call();
1183 		assertTrue(st.isClean());
1184 	}
1185 
1186 	@Test
1187 	public void testCheckoutChangeFileToNonEmptyDirsAndNewIndexEntry()
1188 			throws Exception {
1189 		String fname = "was_file";
1190 		Git git = Git.wrap(db);
1191 
1192 		// Add a file
1193 		File file = writeTrashFile(fname, "a");
1194 		git.add().addFilepattern(fname).call();
1195 		git.commit().setMessage("Added file").call();
1196 
1197 		assertWorkDir(mkmap(fname, "a"));
1198 
1199 		// replace file with directory containing only directories, no files
1200 		FileUtils.delete(file);
1201 
1202 		// create and add a file in the new directory to the index
1203 		writeTrashFile(fname + "/dir", "file1", "c");
1204 		git.add().addFilepattern(fname + "/dir/file1").call();
1205 
1206 		// create but do not add a file in the new directory to the index
1207 		writeTrashFile(fname + "/dir", "file2", "d");
1208 
1209 		assertTrue("File must be a directory now", file.isDirectory());
1210 		assertFalse("Must not delete non empty directory", file.delete());
1211 
1212 		// 2 extra files are created
1213 		assertWorkDir(
1214 				mkmap(fname + "/dir/file1", "c", fname + "/dir/file2", "d"));
1215 
1216 		// revert path to HEAD state
1217 		git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1218 		assertWorkDir(mkmap(fname, "a"));
1219 
1220 		Status st = git.status().call();
1221 		assertTrue(st.isClean());
1222 	}
1223 
1224 	@Test
1225 	public void testCheckoutOutChangesAutoCRLFfalse() throws IOException {
1226 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
1227 		checkout();
1228 		assertIndex(mkmap("foo/bar", "foo\nbar"));
1229 		assertWorkDir(mkmap("foo/bar", "foo\nbar"));
1230 	}
1231 
1232 	@Test
1233 	public void testCheckoutOutChangesAutoCRLFInput() throws IOException {
1234 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
1235 		db.getConfig().setString("core", null, "autocrlf", "input");
1236 		checkout();
1237 		assertIndex(mkmap("foo/bar", "foo\nbar"));
1238 		assertWorkDir(mkmap("foo/bar", "foo\nbar"));
1239 	}
1240 
1241 	@Test
1242 	public void testCheckoutOutChangesAutoCRLFtrue() throws IOException {
1243 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
1244 		db.getConfig().setString("core", null, "autocrlf", "true");
1245 		checkout();
1246 		assertIndex(mkmap("foo/bar", "foo\nbar"));
1247 		assertWorkDir(mkmap("foo/bar", "foo\r\nbar"));
1248 	}
1249 
1250 	@Test
1251 	public void testCheckoutOutChangesAutoCRLFtrueBinary() throws IOException {
1252 		setupCase(mk("foo"), mkmap("foo/bar", "foo\nb\u0000ar"), mk("foo"));
1253 		db.getConfig().setString("core", null, "autocrlf", "true");
1254 		checkout();
1255 		assertIndex(mkmap("foo/bar", "foo\nb\u0000ar"));
1256 		assertWorkDir(mkmap("foo/bar", "foo\nb\u0000ar"));
1257 	}
1258 
1259 	@Test
1260 	public void testCheckoutUncachedChanges() throws IOException {
1261 		setupCase(mk("foo"), mk("foo"), mk("foo"));
1262 		writeTrashFile("foo", "otherData");
1263 		checkout();
1264 		assertIndex(mk("foo"));
1265 		assertWorkDir(mkmap("foo", "otherData"));
1266 		assertTrue(new File(trash, "foo").isFile());
1267 	}
1268 
1269 	@Test
1270 	public void testDontOverwriteDirtyFile() throws IOException {
1271 		setupCase(mk("foo"), mk("other"), mk("foo"));
1272 		writeTrashFile("foo", "different");
1273 		try {
1274 			checkout();
1275 			fail("Didn't got the expected conflict");
1276 		} catch (CheckoutConflictException e) {
1277 			assertIndex(mk("foo"));
1278 			assertWorkDir(mkmap("foo", "different"));
1279 			assertEquals(Arrays.asList("foo"), getConflicts());
1280 			assertTrue(new File(trash, "foo").isFile());
1281 		}
1282 	}
1283 
1284 	@Test
1285 	public void testDontOverwriteEmptyFolder() throws IOException {
1286 		setupCase(mk("foo"), mk("foo"), mk("foo"));
1287 		FileUtils.mkdir(new File(db.getWorkTree(), "d"));
1288 		checkout();
1289 		assertWorkDir(mkmap("foo", "foo", "d", "/"));
1290 	}
1291 
1292 	@Test
1293 	public void testOverwriteUntrackedIgnoredFile() throws IOException,
1294 			GitAPIException {
1295 		String fname="file.txt";
1296 		Git git = Git.wrap(db);
1297 
1298 		// Add a file
1299 		writeTrashFile(fname, "a");
1300 		git.add().addFilepattern(fname).call();
1301 		git.commit().setMessage("create file").call();
1302 
1303 		// Create branch
1304 		git.branchCreate().setName("side").call();
1305 
1306 		// Modify file
1307 		writeTrashFile(fname, "b");
1308 		git.add().addFilepattern(fname).call();
1309 		git.commit().setMessage("modify file").call();
1310 
1311 		// Switch branches
1312 		git.checkout().setName("side").call();
1313 		git.rm().addFilepattern(fname).call();
1314 		writeTrashFile(".gitignore", fname);
1315 		git.add().addFilepattern(".gitignore").call();
1316 		git.commit().setMessage("delete and ignore file").call();
1317 
1318 		writeTrashFile(fname, "Something different");
1319 		git.checkout().setName("master").call();
1320 		assertWorkDir(mkmap(fname, "b"));
1321 		assertTrue(git.status().call().isClean());
1322 	}
1323 
1324 	@Test
1325 	public void testOverwriteUntrackedFileModeChange()
1326 			throws IOException, GitAPIException {
1327 		String fname = "file.txt";
1328 		Git git = Git.wrap(db);
1329 
1330 		// Add a file
1331 		File file = writeTrashFile(fname, "a");
1332 		git.add().addFilepattern(fname).call();
1333 		git.commit().setMessage("create file").call();
1334 		assertWorkDir(mkmap(fname, "a"));
1335 
1336 		// Create branch
1337 		git.branchCreate().setName("side").call();
1338 
1339 		// Switch branches
1340 		git.checkout().setName("side").call();
1341 
1342 		// replace file with directory containing files
1343 		FileUtils.delete(file);
1344 
1345 		// create and add a file in the new directory to the index
1346 		writeTrashFile(fname + "/dir1", "file1", "c");
1347 		git.add().addFilepattern(fname + "/dir1/file1").call();
1348 
1349 		// create but do not add a file in the new directory to the index
1350 		writeTrashFile(fname + "/dir2", "file2", "d");
1351 
1352 		assertTrue("File must be a directory now", file.isDirectory());
1353 		assertFalse("Must not delete non empty directory", file.delete());
1354 
1355 		// 2 extra files are created
1356 		assertWorkDir(
1357 				mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
1358 
1359 		try {
1360 			git.checkout().setName("master").call();
1361 			fail("did not throw exception");
1362 		} catch (Exception e) {
1363 			// 2 extra files are still there
1364 			assertWorkDir(mkmap(fname + "/dir1/file1", "c",
1365 					fname + "/dir2/file2", "d"));
1366 		}
1367 	}
1368 
1369 	@Test
1370 	public void testOverwriteUntrackedLinkModeChange()
1371 			throws Exception {
1372 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1373 		String fname = "file.txt";
1374 		Git git = Git.wrap(db);
1375 
1376 		// Add a file
1377 		writeTrashFile(fname, "a");
1378 		git.add().addFilepattern(fname).call();
1379 
1380 		// Add a link to file
1381 		String linkName = "link";
1382 		File link = writeLink(linkName, fname).toFile();
1383 		git.add().addFilepattern(linkName).call();
1384 		git.commit().setMessage("Added file and link").call();
1385 
1386 		assertWorkDir(mkmap(linkName, "a", fname, "a"));
1387 
1388 		// Create branch
1389 		git.branchCreate().setName("side").call();
1390 
1391 		// Switch branches
1392 		git.checkout().setName("side").call();
1393 
1394 		// replace link with directory containing files
1395 		FileUtils.delete(link);
1396 
1397 		// create and add a file in the new directory to the index
1398 		writeTrashFile(linkName + "/dir1", "file1", "c");
1399 		git.add().addFilepattern(linkName + "/dir1/file1").call();
1400 
1401 		// create but do not add a file in the new directory to the index
1402 		writeTrashFile(linkName + "/dir2", "file2", "d");
1403 
1404 		assertTrue("Link must be a directory now", link.isDirectory());
1405 		assertFalse("Must not delete non empty directory", link.delete());
1406 
1407 		// 2 extra files are created
1408 		assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1409 				linkName + "/dir2/file2", "d"));
1410 
1411 		try {
1412 			git.checkout().setName("master").call();
1413 			fail("did not throw exception");
1414 		} catch (Exception e) {
1415 			// 2 extra files are still there
1416 			assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1417 					linkName + "/dir2/file2", "d"));
1418 		}
1419 	}
1420 
1421 	@Test
1422 	public void testFileModeChangeWithNoContentChangeUpdate() throws Exception {
1423 		if (!FS.DETECTED.supportsExecute())
1424 			return;
1425 
1426 		Git git = Git.wrap(db);
1427 
1428 		// Add non-executable file
1429 		File file = writeTrashFile("file.txt", "a");
1430 		git.add().addFilepattern("file.txt").call();
1431 		git.commit().setMessage("commit1").call();
1432 		assertFalse(db.getFS().canExecute(file));
1433 
1434 		// Create branch
1435 		git.branchCreate().setName("b1").call();
1436 
1437 		// Make file executable
1438 		db.getFS().setExecute(file, true);
1439 		git.add().addFilepattern("file.txt").call();
1440 		git.commit().setMessage("commit2").call();
1441 
1442 		// Verify executable and working directory is clean
1443 		Status status = git.status().call();
1444 		assertTrue(status.getModified().isEmpty());
1445 		assertTrue(status.getChanged().isEmpty());
1446 		assertTrue(db.getFS().canExecute(file));
1447 
1448 		// Switch branches
1449 		git.checkout().setName("b1").call();
1450 
1451 		// Verify not executable and working directory is clean
1452 		status = git.status().call();
1453 		assertTrue(status.getModified().isEmpty());
1454 		assertTrue(status.getChanged().isEmpty());
1455 		assertFalse(db.getFS().canExecute(file));
1456 	}
1457 
1458 	@Test
1459 	public void testFileModeChangeAndContentChangeConflict() throws Exception {
1460 		if (!FS.DETECTED.supportsExecute())
1461 			return;
1462 
1463 		Git git = Git.wrap(db);
1464 
1465 		// Add non-executable file
1466 		File file = writeTrashFile("file.txt", "a");
1467 		git.add().addFilepattern("file.txt").call();
1468 		git.commit().setMessage("commit1").call();
1469 		assertFalse(db.getFS().canExecute(file));
1470 
1471 		// Create branch
1472 		git.branchCreate().setName("b1").call();
1473 
1474 		// Make file executable
1475 		db.getFS().setExecute(file, true);
1476 		git.add().addFilepattern("file.txt").call();
1477 		git.commit().setMessage("commit2").call();
1478 
1479 		// Verify executable and working directory is clean
1480 		Status status = git.status().call();
1481 		assertTrue(status.getModified().isEmpty());
1482 		assertTrue(status.getChanged().isEmpty());
1483 		assertTrue(db.getFS().canExecute(file));
1484 
1485 		writeTrashFile("file.txt", "b");
1486 
1487 		// Switch branches
1488 		CheckoutCommand checkout = git.checkout().setName("b1");
1489 		try {
1490 			checkout.call();
1491 			fail("Checkout exception not thrown");
1492 		} catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
1493 			CheckoutResult result = checkout.getResult();
1494 			assertNotNull(result);
1495 			assertNotNull(result.getConflictList());
1496 			assertEquals(1, result.getConflictList().size());
1497 			assertTrue(result.getConflictList().contains("file.txt"));
1498 		}
1499 	}
1500 
1501 	@Test
1502 	public void testDirtyFileModeEqualHeadMerge()
1503 			throws Exception {
1504 		if (!FS.DETECTED.supportsExecute())
1505 			return;
1506 
1507 		Git git = Git.wrap(db);
1508 
1509 		// Add non-executable file
1510 		File file = writeTrashFile("file.txt", "a");
1511 		git.add().addFilepattern("file.txt").call();
1512 		git.commit().setMessage("commit1").call();
1513 		assertFalse(db.getFS().canExecute(file));
1514 
1515 		// Create branch
1516 		git.branchCreate().setName("b1").call();
1517 
1518 		// Create second commit and don't touch file
1519 		writeTrashFile("file2.txt", "");
1520 		git.add().addFilepattern("file2.txt").call();
1521 		git.commit().setMessage("commit2").call();
1522 
1523 		// stage a mode change
1524 		writeTrashFile("file.txt", "a");
1525 		db.getFS().setExecute(file, true);
1526 		git.add().addFilepattern("file.txt").call();
1527 
1528 		// dirty the file
1529 		writeTrashFile("file.txt", "b");
1530 
1531 		assertEquals(
1532 				"[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
1533 				indexState(CONTENT));
1534 		assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
1535 
1536 		// Switch branches and check that the dirty file survived in worktree
1537 		// and index
1538 		git.checkout().setName("b1").call();
1539 		assertEquals("[file.txt, mode:100755, content:a]", indexState(CONTENT));
1540 		assertWorkDir(mkmap("file.txt", "b"));
1541 	}
1542 
1543 	@Test
1544 	public void testDirtyFileModeEqualIndexMerge()
1545 			throws Exception {
1546 		if (!FS.DETECTED.supportsExecute())
1547 			return;
1548 
1549 		Git git = Git.wrap(db);
1550 
1551 		// Add non-executable file
1552 		File file = writeTrashFile("file.txt", "a");
1553 		git.add().addFilepattern("file.txt").call();
1554 		git.commit().setMessage("commit1").call();
1555 		assertFalse(db.getFS().canExecute(file));
1556 
1557 		// Create branch
1558 		git.branchCreate().setName("b1").call();
1559 
1560 		// Create second commit with executable file
1561 		file = writeTrashFile("file.txt", "b");
1562 		db.getFS().setExecute(file, true);
1563 		git.add().addFilepattern("file.txt").call();
1564 		git.commit().setMessage("commit2").call();
1565 
1566 		// stage the same content as in the branch we want to switch to
1567 		writeTrashFile("file.txt", "a");
1568 		db.getFS().setExecute(file, false);
1569 		git.add().addFilepattern("file.txt").call();
1570 
1571 		// dirty the file
1572 		writeTrashFile("file.txt", "c");
1573 		db.getFS().setExecute(file, true);
1574 
1575 		assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
1576 		assertWorkDir(mkmap("file.txt", "c"));
1577 
1578 		// Switch branches and check that the dirty file survived in worktree
1579 		// and index
1580 		git.checkout().setName("b1").call();
1581 		assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
1582 		assertWorkDir(mkmap("file.txt", "c"));
1583 	}
1584 
1585 	@Test
1586 	public void testFileModeChangeAndContentChangeNoConflict() throws Exception {
1587 		if (!FS.DETECTED.supportsExecute())
1588 			return;
1589 
1590 		Git git = Git.wrap(db);
1591 
1592 		// Add first file
1593 		File file1 = writeTrashFile("file1.txt", "a");
1594 		git.add().addFilepattern("file1.txt").call();
1595 		git.commit().setMessage("commit1").call();
1596 		assertFalse(db.getFS().canExecute(file1));
1597 
1598 		// Add second file
1599 		File file2 = writeTrashFile("file2.txt", "b");
1600 		git.add().addFilepattern("file2.txt").call();
1601 		git.commit().setMessage("commit2").call();
1602 		assertFalse(db.getFS().canExecute(file2));
1603 
1604 		// Create branch from first commit
1605 		assertNotNull(git.checkout().setCreateBranch(true).setName("b1")
1606 				.setStartPoint(Constants.HEAD + "~1").call());
1607 
1608 		// Change content and file mode in working directory and index
1609 		file1 = writeTrashFile("file1.txt", "c");
1610 		db.getFS().setExecute(file1, true);
1611 		git.add().addFilepattern("file1.txt").call();
1612 
1613 		// Switch back to 'master'
1614 		assertNotNull(git.checkout().setName(Constants.MASTER).call());
1615 	}
1616 
1617 	@Test(expected = CheckoutConflictException.class)
1618 	public void testFolderFileConflict() throws Exception {
1619 		RevCommit headCommit = commitFile("f/a", "initial content", "master");
1620 		RevCommit checkoutCommit = commitFile("f/a", "side content", "side");
1621 		FileUtils.delete(new File(db.getWorkTree(), "f"), FileUtils.RECURSIVE);
1622 		writeTrashFile("f", "file instead of folder");
1623 		new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(),
1624 				checkoutCommit.getTree()).checkout();
1625 	}
1626 
1627 	@Test
1628 	public void testMultipleContentConflicts() throws Exception {
1629 		commitFile("a", "initial content", "master");
1630 		RevCommit headCommit = commitFile("b", "initial content", "master");
1631 		commitFile("a", "side content", "side");
1632 		RevCommit checkoutCommit = commitFile("b", "side content", "side");
1633 		writeTrashFile("a", "changed content");
1634 		writeTrashFile("b", "changed content");
1635 
1636 		try {
1637 			new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(),
1638 					checkoutCommit.getTree()).checkout();
1639 			fail();
1640 		} catch (CheckoutConflictException expected) {
1641 			assertEquals(2, expected.getConflictingFiles().length);
1642 			assertTrue(Arrays.asList(expected.getConflictingFiles())
1643 					.contains("a"));
1644 			assertTrue(Arrays.asList(expected.getConflictingFiles())
1645 					.contains("b"));
1646 			assertEquals("changed content", read("a"));
1647 			assertEquals("changed content", read("b"));
1648 		}
1649 	}
1650 
1651 	@Test
1652 	public void testFolderFileAndContentConflicts() throws Exception {
1653 		RevCommit headCommit = commitFile("f/a", "initial content", "master");
1654 		commitFile("b", "side content", "side");
1655 		RevCommit checkoutCommit = commitFile("f/a", "side content", "side");
1656 		FileUtils.delete(new File(db.getWorkTree(), "f"), FileUtils.RECURSIVE);
1657 		writeTrashFile("f", "file instead of a folder");
1658 		writeTrashFile("b", "changed content");
1659 
1660 		try {
1661 			new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(),
1662 					checkoutCommit.getTree()).checkout();
1663 			fail();
1664 		} catch (CheckoutConflictException expected) {
1665 			assertEquals(2, expected.getConflictingFiles().length);
1666 			assertTrue(Arrays.asList(expected.getConflictingFiles())
1667 					.contains("b"));
1668 			assertTrue(Arrays.asList(expected.getConflictingFiles())
1669 					.contains("f"));
1670 			assertEquals("file instead of a folder", read("f"));
1671 			assertEquals("changed content", read("b"));
1672 		}
1673 	}
1674 
1675 	public void assertWorkDir(Map<String, String> i)
1676 			throws CorruptObjectException,
1677 			IOException {
1678 		try (TreeWalk walk = new TreeWalk(db)) {
1679 			walk.setRecursive(false);
1680 			walk.addTree(new FileTreeIterator(db));
1681 			String expectedValue;
1682 			String path;
1683 			int nrFiles = 0;
1684 			FileTreeIterator ft;
1685 			while (walk.next()) {
1686 				ft = walk.getTree(0, FileTreeIterator.class);
1687 				path = ft.getEntryPathString();
1688 				expectedValue = i.get(path);
1689 				File file = new File(db.getWorkTree(), path);
1690 				assertTrue(file.exists());
1691 				if (file.isFile()) {
1692 					assertNotNull("found unexpected file for path " + path
1693 							+ " in workdir", expectedValue);
1694 					FileInputStream is = new FileInputStream(file);
1695 					byte[] buffer = new byte[(int) file.length()];
1696 					int offset = 0;
1697 					int numRead = 0;
1698 					while (offset < buffer.length
1699 							&& (numRead = is.read(buffer, offset, buffer.length
1700 									- offset)) >= 0) {
1701 						offset += numRead;
1702 					}
1703 					is.close();
1704 					assertArrayEquals("unexpected content for path " + path
1705 							+ " in workDir. ", buffer, i.get(path).getBytes());
1706 					nrFiles++;
1707 				} else if (file.isDirectory()) {
1708 					String[] files = file.list();
1709 					if (files != null && files.length == 0) {
1710 						assertEquals("found unexpected empty folder for path "
1711 								+ path + " in workDir. ", "/", i.get(path));
1712 						nrFiles++;
1713 					}
1714 				}
1715 				if (walk.isSubtree()) {
1716 					walk.enterSubtree();
1717 				}
1718 			}
1719 			assertEquals("WorkDir has not the right size.", i.size(), nrFiles);
1720 		}
1721 	}
1722 }