1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 package org.eclipse.jgit.lib;
42
43 import static java.nio.charset.StandardCharsets.UTF_8;
44 import static org.junit.Assert.assertArrayEquals;
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertFalse;
47 import static org.junit.Assert.assertNotNull;
48 import static org.junit.Assert.assertTrue;
49 import static org.junit.Assert.fail;
50
51 import java.io.File;
52 import java.io.FileInputStream;
53 import java.io.IOException;
54 import java.util.Arrays;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58
59 import org.eclipse.jgit.api.CheckoutCommand;
60 import org.eclipse.jgit.api.CheckoutResult;
61 import org.eclipse.jgit.api.Git;
62 import org.eclipse.jgit.api.MergeResult.MergeStatus;
63 import org.eclipse.jgit.api.ResetCommand.ResetType;
64 import org.eclipse.jgit.api.Status;
65 import org.eclipse.jgit.api.errors.GitAPIException;
66 import org.eclipse.jgit.api.errors.NoFilepatternException;
67 import org.eclipse.jgit.dircache.DirCache;
68 import org.eclipse.jgit.dircache.DirCacheCheckout;
69 import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
70 import org.eclipse.jgit.dircache.DirCacheEditor;
71 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
72 import org.eclipse.jgit.dircache.DirCacheEntry;
73 import org.eclipse.jgit.errors.CheckoutConflictException;
74 import org.eclipse.jgit.errors.CorruptObjectException;
75 import org.eclipse.jgit.errors.NoWorkTreeException;
76 import org.eclipse.jgit.events.ChangeRecorder;
77 import org.eclipse.jgit.events.ListenerHandle;
78 import org.eclipse.jgit.junit.RepositoryTestCase;
79 import org.eclipse.jgit.junit.TestRepository;
80 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
81 import org.eclipse.jgit.revwalk.RevCommit;
82 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
83 import org.eclipse.jgit.treewalk.FileTreeIterator;
84 import org.eclipse.jgit.treewalk.TreeWalk;
85 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
86 import org.eclipse.jgit.util.FS;
87 import org.eclipse.jgit.util.FileUtils;
88 import org.junit.Assume;
89 import org.junit.Test;
90
91 public class DirCacheCheckoutTest extends RepositoryTestCase {
92 private DirCacheCheckout dco;
93 protected ObjectId theHead;
94 protected ObjectId theMerge;
95 private DirCache dirCache;
96
97 private void prescanTwoTrees(ObjectId head, ObjectId merge)
98 throws IllegalStateException, IOException {
99 DirCache dc = db.lockDirCache();
100 try {
101 dco = new DirCacheCheckout(db, head, dc, merge);
102 dco.preScanTwoTrees();
103 } finally {
104 dc.unlock();
105 }
106 }
107
108 private void checkout() throws IOException {
109 DirCache dc = db.lockDirCache();
110 try {
111 dco = new DirCacheCheckout(db, theHead, dc, theMerge);
112 dco.checkout();
113 } finally {
114 dc.unlock();
115 }
116 }
117
118 private List<String> getRemoved() {
119 return dco.getRemoved();
120 }
121
122 private Map<String, CheckoutMetadata> getUpdated() {
123 return dco.getUpdated();
124 }
125
126 private List<String> getConflicts() {
127 return dco.getConflicts();
128 }
129
130 private static HashMap<String, String> mk(String a) {
131 return mkmap(a, a);
132 }
133
134 private static HashMap<String, String> mkmap(String... args) {
135 if ((args.length % 2) > 0)
136 throw new IllegalArgumentException("needs to be pairs");
137
138 HashMap<String, String> map = new HashMap<>();
139 for (int i = 0; i < args.length; i += 2) {
140 map.put(args[i], args[i + 1]);
141 }
142
143 return map;
144 }
145
146 @Test
147 public void testResetHard() throws IOException, NoFilepatternException,
148 GitAPIException {
149 ChangeRecorder recorder = new ChangeRecorder();
150 ListenerHandle handle = null;
151 try (Git git = new Git(db)) {
152 handle = db.getListenerList()
153 .addWorkingTreeModifiedListener(recorder);
154 writeTrashFile("f", "f()");
155 writeTrashFile("D/g", "g()");
156 git.add().addFilepattern(".").call();
157 git.commit().setMessage("inital").call();
158 assertIndex(mkmap("f", "f()", "D/g", "g()"));
159 recorder.assertNoEvent();
160 git.branchCreate().setName("topic").call();
161 recorder.assertNoEvent();
162
163 writeTrashFile("f", "f()\nmaster");
164 writeTrashFile("D/g", "g()\ng2()");
165 writeTrashFile("E/h", "h()");
166 git.add().addFilepattern(".").call();
167 RevCommit master = git.commit().setMessage("master-1").call();
168 assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
169 recorder.assertNoEvent();
170
171 checkoutBranch("refs/heads/topic");
172 assertIndex(mkmap("f", "f()", "D/g", "g()"));
173 recorder.assertEvent(new String[] { "f", "D/g" },
174 new String[] { "E/h" });
175
176 writeTrashFile("f", "f()\nside");
177 assertTrue(new File(db.getWorkTree(), "D/g").delete());
178 writeTrashFile("G/i", "i()");
179 git.add().addFilepattern(".").call();
180 git.add().addFilepattern(".").setUpdate(true).call();
181 RevCommit topic = git.commit().setMessage("topic-1").call();
182 assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
183 recorder.assertNoEvent();
184
185 writeTrashFile("untracked", "untracked");
186
187 resetHard(master);
188 assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
189 recorder.assertEvent(new String[] { "f", "D/g", "E/h" },
190 new String[] { "G", "G/i" });
191
192 resetHard(topic);
193 assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
194 assertWorkDir(mkmap("f", "f()\nside", "G/i", "i()", "untracked",
195 "untracked"));
196 recorder.assertEvent(new String[] { "f", "G/i" },
197 new String[] { "D", "D/g", "E", "E/h" });
198
199 assertEquals(MergeStatus.CONFLICTING, git.merge().include(master)
200 .call().getMergeStatus());
201 assertEquals(
202 "[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]",
203 indexState(0));
204 recorder.assertEvent(new String[] { "f", "D/g", "E/h" },
205 ChangeRecorder.EMPTY);
206
207 resetHard(master);
208 assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
209 assertWorkDir(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h",
210 "h()", "untracked", "untracked"));
211 recorder.assertEvent(new String[] { "f", "D/g" },
212 new String[] { "G", "G/i" });
213
214 } finally {
215 if (handle != null) {
216 handle.remove();
217 }
218 }
219 }
220
221
222
223
224
225
226
227
228
229
230 @Test
231 public void testResetHardFromIndexEntryWithoutFileToTreeWithoutFile()
232 throws Exception {
233 ChangeRecorder recorder = new ChangeRecorder();
234 ListenerHandle handle = null;
235 try (Git git = new Git(db)) {
236 handle = db.getListenerList()
237 .addWorkingTreeModifiedListener(recorder);
238 writeTrashFile("x", "x");
239 git.add().addFilepattern("x").call();
240 RevCommit id1 = git.commit().setMessage("c1").call();
241
242 writeTrashFile("f/g", "f/g");
243 git.rm().addFilepattern("x").call();
244 recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "x" });
245 git.add().addFilepattern("f/g").call();
246 git.commit().setMessage("c2").call();
247 deleteTrashFile("f/g");
248 deleteTrashFile("f");
249
250
251 git.reset().setMode(ResetType.HARD).setRef(id1.getName()).call();
252 assertIndex(mkmap("x", "x"));
253 recorder.assertEvent(new String[] { "x" }, ChangeRecorder.EMPTY);
254 } finally {
255 if (handle != null) {
256 handle.remove();
257 }
258 }
259 }
260
261
262
263
264
265
266 @Test
267 public void testInitialCheckout() throws Exception {
268 ChangeRecorder recorder = new ChangeRecorder();
269 ListenerHandle handle = null;
270 try (Git git = new Git(db);
271 TestRepository<Repository> db_t = new TestRepository<>(db)) {
272 handle = db.getListenerList()
273 .addWorkingTreeModifiedListener(recorder);
274 BranchBuilder master = db_t.branch("master");
275 master.commit().add("f", "1").message("m0").create();
276 assertFalse(new File(db.getWorkTree(), "f").exists());
277 git.checkout().setName("master").call();
278 assertTrue(new File(db.getWorkTree(), "f").exists());
279 recorder.assertEvent(new String[] { "f" }, ChangeRecorder.EMPTY);
280 } finally {
281 if (handle != null) {
282 handle.remove();
283 }
284 }
285 }
286
287 private DirCacheCheckout resetHard(RevCommit commit)
288 throws NoWorkTreeException,
289 CorruptObjectException, IOException {
290 DirCacheCheckout dc;
291 dc = new DirCacheCheckout(db, null, db.lockDirCache(),
292 commit.getTree());
293 dc.setFailOnConflict(true);
294 assertTrue(dc.checkout());
295 return dc;
296 }
297
298 private void assertIndex(HashMap<String, String> i)
299 throws CorruptObjectException, IOException {
300 String expectedValue;
301 String path;
302 DirCache read = DirCache.read(db.getIndexFile(), db.getFS());
303
304 assertEquals("Index has not the right size.", i.size(),
305 read.getEntryCount());
306 for (int j = 0; j < read.getEntryCount(); j++) {
307 path = read.getEntry(j).getPathString();
308 expectedValue = i.get(path);
309 assertNotNull("found unexpected entry for path " + path
310 + " in index", expectedValue);
311 assertTrue("unexpected content for path " + path
312 + " in index. Expected: <" + expectedValue + ">",
313 Arrays.equals(db.open(read.getEntry(j).getObjectId())
314 .getCachedBytes(), i.get(path).getBytes(UTF_8)));
315 }
316 }
317
318 @Test
319 public void testRules1thru3_NoIndexEntry() throws IOException {
320 ObjectId head = buildTree(mk("foo"));
321 ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE,
322 new byte[0]);
323
324 prescanTwoTrees(head, merge);
325
326 assertTrue(getRemoved().contains("foo"));
327
328 prescanTwoTrees(merge, head);
329
330 assertTrue(getUpdated().containsKey("foo"));
331
332 merge = buildTree(mkmap("foo", "a"));
333
334 prescanTwoTrees(head, merge);
335
336 assertConflict("foo");
337 }
338
339 void setupCase(HashMap<String, String> headEntries, HashMap<String, String> mergeEntries, HashMap<String, String> indexEntries) throws IOException {
340 theHead = buildTree(headEntries);
341 theMerge = buildTree(mergeEntries);
342 buildIndex(indexEntries);
343 }
344
345 private void buildIndex(HashMap<String, String> indexEntries) throws IOException {
346 dirCache = new DirCache(db.getIndexFile(), db.getFS());
347 if (indexEntries != null) {
348 assertTrue(dirCache.lock());
349 DirCacheEditor editor = dirCache.editor();
350 for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) {
351 writeTrashFile(e.getKey(), e.getValue());
352 ObjectInserter inserter = db.newObjectInserter();
353 final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
354 Constants.encode(e.getValue()));
355 editor.add(new DirCacheEditor.DeletePath(e.getKey()));
356 editor.add(new DirCacheEditor.PathEdit(e.getKey()) {
357 @Override
358 public void apply(DirCacheEntry ent) {
359 ent.setFileMode(FileMode.REGULAR_FILE);
360 ent.setObjectId(id);
361 ent.setUpdateNeeded(false);
362 }
363 });
364 }
365 assertTrue(editor.commit());
366 }
367
368 }
369
370 static final class AddEdit extends PathEdit {
371
372 private final ObjectId data;
373
374 private final long length;
375
376 public AddEdit(String entryPath, ObjectId data, long length) {
377 super(entryPath);
378 this.data = data;
379 this.length = length;
380 }
381
382 @Override
383 public void apply(DirCacheEntry ent) {
384 ent.setFileMode(FileMode.REGULAR_FILE);
385 ent.setLength(length);
386 ent.setObjectId(data);
387 }
388
389 }
390
391 private ObjectId buildTree(HashMap<String, String> headEntries)
392 throws IOException {
393 DirCache lockDirCache = DirCache.newInCore();
394
395 DirCacheEditor editor = lockDirCache.editor();
396 if (headEntries != null) {
397 for (java.util.Map.Entry<String, String> e : headEntries.entrySet()) {
398 AddEdit addEdit = new AddEdit(e.getKey(),
399 genSha1(e.getValue()), e.getValue().length());
400 editor.add(addEdit);
401 }
402 }
403 editor.finish();
404 return lockDirCache.writeTree(db.newObjectInserter());
405 }
406
407 ObjectId genSha1(String data) {
408 try (ObjectInserter w = db.newObjectInserter()) {
409 ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes(UTF_8));
410 w.flush();
411 return id;
412 } catch (IOException e) {
413 fail(e.toString());
414 }
415 return null;
416 }
417
418 protected void go() throws IllegalStateException, IOException {
419 prescanTwoTrees(theHead, theMerge);
420 }
421
422 @Test
423 public void testRules4thru13_IndexEntryNotInHead() throws IOException {
424
425 HashMap<String, String> idxMap;
426
427 idxMap = new HashMap<>();
428 idxMap.put("foo", "foo");
429 setupCase(null, null, idxMap);
430 go();
431
432 assertTrue(getUpdated().isEmpty());
433 assertTrue(getRemoved().isEmpty());
434 assertTrue(getConflicts().isEmpty());
435
436
437 idxMap = new HashMap<>();
438 idxMap.put("foo", "foo");
439 setupCase(null, idxMap, idxMap);
440 go();
441
442 assertAllEmpty();
443
444
445 HashMap<String, String> mergeMap;
446 mergeMap = new HashMap<>();
447
448 mergeMap.put("foo", "merge");
449 setupCase(null, mergeMap, idxMap);
450 go();
451
452 assertTrue(getUpdated().isEmpty());
453 assertTrue(getRemoved().isEmpty());
454 assertTrue(getConflicts().contains("foo"));
455
456
457
458 HashMap<String, String> headMap = new HashMap<>();
459 headMap.put("foo", "foo");
460 setupCase(headMap, null, idxMap);
461 go();
462
463 assertTrue(getRemoved().contains("foo"));
464 assertTrue(getUpdated().isEmpty());
465 assertTrue(getConflicts().isEmpty());
466
467
468 setupCase(headMap, null, idxMap);
469 assertTrue(new File(trash, "foo").delete());
470 writeTrashFile("foo", "bar");
471 db.readDirCache().getEntry(0).setUpdateNeeded(true);
472 go();
473
474 assertTrue(getRemoved().isEmpty());
475 assertTrue(getUpdated().isEmpty());
476 assertTrue(getConflicts().contains("foo"));
477
478
479 headMap.put("foo", "head");
480 setupCase(headMap, null, idxMap);
481 go();
482
483 assertTrue(getRemoved().isEmpty());
484 assertTrue(getUpdated().isEmpty());
485 assertTrue(getConflicts().contains("foo"));
486
487
488 setupCase(headMap, headMap, idxMap);
489 go();
490
491 assertAllEmpty();
492
493
494 setupCase(headMap, mergeMap, idxMap); go();
495 assertTrue(getConflicts().contains("foo"));
496
497
498 setupCase(headMap, idxMap, idxMap); go();
499 assertAllEmpty();
500
501
502 setupCase(idxMap, mergeMap, idxMap); go();
503 assertTrue(getUpdated().containsKey("foo"));
504
505
506 setupCase(idxMap, mergeMap, idxMap);
507 assertTrue(new File(trash, "foo").delete());
508 writeTrashFile("foo", "bar");
509 db.readDirCache().getEntry(0).setUpdateNeeded(true);
510 go();
511 assertTrue(getConflicts().contains("foo"));
512 }
513
514 private void assertAllEmpty() {
515 assertTrue(getRemoved().isEmpty());
516 assertTrue(getUpdated().isEmpty());
517 assertTrue(getConflicts().isEmpty());
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552 @Test
553 public void testDirectoryFileSimple() throws IOException {
554 ObjectId treeDF = buildTree(mkmap("DF", "DF"));
555 ObjectId treeDFDF = buildTree(mkmap("DF/DF", "DF/DF"));
556 buildIndex(mkmap("DF", "DF"));
557
558 prescanTwoTrees(treeDF, treeDFDF);
559
560 assertTrue(getRemoved().contains("DF"));
561 assertTrue(getUpdated().containsKey("DF/DF"));
562
563 recursiveDelete(new File(trash, "DF"));
564 buildIndex(mkmap("DF/DF", "DF/DF"));
565
566 prescanTwoTrees(treeDFDF, treeDF);
567 assertTrue(getRemoved().contains("DF/DF"));
568 assertTrue(getUpdated().containsKey("DF"));
569 }
570
571 @Test
572 public void testDirectoryFileConflicts_1() throws Exception {
573
574 doit(mk("DF/DF"), mk("DF"), mk("DF/DF"));
575 assertNoConflicts();
576 assertUpdated("DF");
577 assertRemoved("DF/DF");
578 }
579
580 @Test
581 public void testDirectoryFileConflicts_2() throws Exception {
582
583 setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF"));
584 writeTrashFile("DF/DF", "different");
585 go();
586 assertConflict("DF/DF");
587
588 }
589
590 @Test
591 public void testDirectoryFileConflicts_3() throws Exception {
592
593 doit(mk("DF/DF"), mk("DF/DF"), mk("DF"));
594 assertNoConflicts();
595 }
596
597 @Test
598 public void testDirectoryFileConflicts_4() throws Exception {
599
600 doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF"));
601 assertConflict("DF/DF");
602
603 }
604
605 @Test
606 public void testDirectoryFileConflicts_5() throws Exception {
607
608 doit(mk("DF/DF"), mk("DF"), mk("DF"));
609 assertRemoved("DF/DF");
610 assertEquals(0, dco.getConflicts().size());
611 assertEquals(0, dco.getUpdated().size());
612 }
613
614 @Test
615 public void testDirectoryFileConflicts_5b() throws Exception {
616
617 doit(mk("DF/DF"), mkmap("DF", "different"), mk("DF"));
618 assertRemoved("DF/DF");
619 assertConflict("DF");
620 assertEquals(0, dco.getUpdated().size());
621 }
622
623 @Test
624 public void testDirectoryFileConflicts_6() throws Exception {
625
626 setupCase(mk("DF/DF"), mk("DF"), mk("DF"));
627 writeTrashFile("DF", "different");
628 go();
629 assertRemoved("DF/DF");
630 assertEquals(0, dco.getConflicts().size());
631 assertEquals(0, dco.getUpdated().size());
632 }
633
634 @Test
635 public void testDirectoryFileConflicts_6b() throws Exception {
636
637 setupCase(mk("DF/DF"), mk("DF"), mkmap("DF", "different"));
638 writeTrashFile("DF", "again different");
639 go();
640 assertRemoved("DF/DF");
641 assertConflict("DF");
642 assertEquals(0, dco.getUpdated().size());
643 }
644
645 @Test
646 public void testDirectoryFileConflicts_7() throws Exception {
647
648 doit(mk("DF"), mk("DF"), mk("DF/DF"));
649 assertUpdated("DF");
650 assertRemoved("DF/DF");
651
652 cleanUpDF();
653 setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
654 go();
655 assertRemoved("DF/DF/DF/DF/DF");
656 assertUpdated("DF/DF");
657
658 cleanUpDF();
659 setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
660 writeTrashFile("DF/DF/DF/DF/DF", "diff");
661 go();
662 assertConflict("DF/DF/DF/DF/DF");
663
664
665
666
667
668
669
670
671
672 }
673
674 @Test
675 public void testDirectoryFileConflicts_8() throws Exception {
676
677 setupCase(mk("DF"), mk("DF"), mk("DF/DF"));
678 recursiveDelete(new File(db.getWorkTree(), "DF"));
679 writeTrashFile("DF", "xy");
680 go();
681 assertConflict("DF/DF");
682 }
683
684 @Test
685 public void testDirectoryFileConflicts_9() throws Exception {
686
687 doit(mkmap("DF", "QP"), mkmap("DF", "QP"), mkmap("DF/DF", "DF/DF"));
688 assertRemoved("DF/DF");
689 assertUpdated("DF");
690 }
691
692 @Test
693 public void testDirectoryFileConflicts_10() throws Exception {
694
695 cleanUpDF();
696 doit(mk("DF"), mk("DF/DF"), mk("DF/DF"));
697 assertNoConflicts();
698 }
699
700 @Test
701 public void testDirectoryFileConflicts_11() throws Exception {
702
703 doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf"));
704 assertConflict("DF/DF");
705 }
706
707 @Test
708 public void testDirectoryFileConflicts_12() throws Exception {
709
710 cleanUpDF();
711 doit(mk("DF"), mk("DF/DF"), mk("DF"));
712 assertRemoved("DF");
713 assertUpdated("DF/DF");
714 }
715
716 @Test
717 public void testDirectoryFileConflicts_13() throws Exception {
718
719 cleanUpDF();
720 setupCase(mk("DF"), mk("DF/DF"), mk("DF"));
721 writeTrashFile("DF", "asdfsdf");
722 go();
723 assertConflict("DF");
724 assertUpdated("DF/DF");
725 }
726
727 @Test
728 public void testDirectoryFileConflicts_14() throws Exception {
729
730 cleanUpDF();
731 doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo"));
732 assertConflict("DF");
733 assertUpdated("DF/DF");
734 }
735
736 @Test
737 public void testDirectoryFileConflicts_15() throws Exception {
738
739 doit(mkmap(), mk("DF/DF"), mk("DF"));
740
741
742
743
744
745 assertUpdated("DF/DF");
746 }
747
748 @Test
749 public void testDirectoryFileConflicts_15b() throws Exception {
750
751 doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF"));
752
753
754
755
756
757
758 assertUpdated("DF/DF/DF/DF");
759 }
760
761 @Test
762 public void testDirectoryFileConflicts_16() throws Exception {
763
764 cleanUpDF();
765 doit(mkmap(), mk("DF"), mk("DF/DF/DF"));
766 assertRemoved("DF/DF/DF");
767 assertUpdated("DF");
768 }
769
770 @Test
771 public void testDirectoryFileConflicts_17() throws Exception {
772
773 cleanUpDF();
774 setupCase(mkmap(), mk("DF"), mk("DF/DF/DF"));
775 writeTrashFile("DF/DF/DF", "asdf");
776 go();
777 assertConflict("DF/DF/DF");
778
779
780
781
782
783
784 }
785
786 @Test
787 public void testDirectoryFileConflicts_18() throws Exception {
788
789 cleanUpDF();
790 doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null);
791 assertRemoved("DF/DF");
792 assertUpdated("DF/DF/DF/DF");
793 }
794
795 @Test
796 public void testDirectoryFileConflicts_19() throws Exception {
797
798 cleanUpDF();
799 doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null);
800 assertRemoved("DF/DF/DF/DF");
801 assertUpdated("DF/DF/DF");
802 }
803
804 protected void cleanUpDF() throws Exception {
805 tearDown();
806 setUp();
807 recursiveDelete(new File(trash, "DF"));
808 }
809
810 protected void assertConflict(String s) {
811 assertTrue(getConflicts().contains(s));
812 }
813
814 protected void assertUpdated(String s) {
815 assertTrue(getUpdated().containsKey(s));
816 }
817
818 protected void assertRemoved(String s) {
819 assertTrue(getRemoved().contains(s));
820 }
821
822 protected void assertNoConflicts() {
823 assertTrue(getConflicts().isEmpty());
824 }
825
826 protected void doit(HashMap<String, String> h, HashMap<String, String> m, HashMap<String, String> i)
827 throws IOException {
828 setupCase(h, m, i);
829 go();
830 }
831
832 @Test
833 public void testUntrackedConflicts() throws IOException {
834 setupCase(null, mk("foo"), null);
835 writeTrashFile("foo", "foo");
836 go();
837
838
839 recursiveDelete(new File(trash, "foo"));
840 setupCase(mk("other"), mkmap("other", "other", "foo", "foo"),
841 mk("other"));
842 writeTrashFile("foo", "bar");
843 try {
844 checkout();
845 fail("didn't get the expected exception");
846 } catch (CheckoutConflictException e) {
847 assertConflict("foo");
848 assertEquals("foo", e.getConflictingFiles()[0]);
849 assertWorkDir(mkmap("foo", "bar", "other", "other"));
850 assertIndex(mk("other"));
851 }
852
853
854 recursiveDelete(new File(trash, "other"));
855 recursiveDelete(new File(trash, "foo"));
856 setupCase(null, mk("foo"), null);
857 writeTrashFile("foo", "bar");
858 try {
859 checkout();
860 fail("didn't get the expected exception");
861 } catch (CheckoutConflictException e) {
862 assertConflict("foo");
863 assertWorkDir(mkmap("foo", "bar"));
864 assertIndex(mkmap("other", "other"));
865 }
866
867
868
869
870
871
872
873
874
875
876 recursiveDelete(new File(trash, "foo"));
877 recursiveDelete(new File(trash, "other"));
878 setupCase(null, mk("foo"), null);
879 writeTrashFile("foo/bar/baz", "");
880 writeTrashFile("foo/blahblah", "");
881 go();
882
883 assertConflict("foo");
884 assertConflict("foo/bar/baz");
885 assertConflict("foo/blahblah");
886
887 recursiveDelete(new File(trash, "foo"));
888
889 setupCase(mkmap("foo/bar", "", "foo/baz", ""),
890 mk("foo"), mkmap("foo/bar", "", "foo/baz", ""));
891 assertTrue(new File(trash, "foo/bar").exists());
892 go();
893
894 assertNoConflicts();
895 }
896
897 @Test
898 public void testCloseNameConflictsX0() throws IOException {
899 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") );
900 checkout();
901 assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
902 assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
903 go();
904 assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
905 assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs"));
906 assertNoConflicts();
907 }
908
909 @Test
910 public void testCloseNameConflicts1() throws IOException {
911 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") );
912 checkout();
913 assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
914 assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
915 go();
916 assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
917 assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a"));
918 assertNoConflicts();
919 }
920
921 @Test
922 public void testCheckoutHierarchy() throws IOException {
923 setupCase(
924 mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
925 "e/g"),
926 mkmap("a", "a2", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
927 "e/g2"),
928 mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g",
929 "e/g3"));
930 try {
931 checkout();
932 fail("did not throw CheckoutConflictException");
933 } catch (CheckoutConflictException e) {
934 assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f",
935 "e/f", "e/g", "e/g3"));
936 assertConflict("e/g");
937 assertEquals("e/g", e.getConflictingFiles()[0]);
938 }
939 }
940
941 @Test
942 public void testCheckoutOutChanges() throws IOException {
943 setupCase(mk("foo"), mk("foo/bar"), mk("foo"));
944 checkout();
945 assertIndex(mk("foo/bar"));
946 assertWorkDir(mk("foo/bar"));
947
948 assertFalse(new File(trash, "foo").isFile());
949 assertTrue(new File(trash, "foo/bar").isFile());
950 recursiveDelete(new File(trash, "foo"));
951
952 assertWorkDir(mkmap());
953
954 setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar"));
955 checkout();
956
957 assertIndex(mk("foo"));
958 assertWorkDir(mk("foo"));
959
960 assertFalse(new File(trash, "foo/bar").isFile());
961 assertTrue(new File(trash, "foo").isFile());
962
963 setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar"));
964
965 assertIndex(mkmap("foo", "bar"));
966 assertWorkDir(mkmap("foo", "bar"));
967
968 try {
969 checkout();
970 fail("did not throw exception");
971 } catch (CheckoutConflictException e) {
972 assertIndex(mkmap("foo", "bar"));
973 assertWorkDir(mkmap("foo", "bar"));
974 }
975 }
976
977 @Test
978 public void testCheckoutChangeLinkToEmptyDir() throws Exception {
979 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
980 String fname = "was_file";
981 ChangeRecorder recorder = new ChangeRecorder();
982 ListenerHandle handle = null;
983 try (Git git = new Git(db)) {
984 handle = db.getListenerList()
985 .addWorkingTreeModifiedListener(recorder);
986
987 writeTrashFile(fname, "a");
988 git.add().addFilepattern(fname).call();
989
990
991 String linkName = "link";
992 File link = writeLink(linkName, fname).toFile();
993 git.add().addFilepattern(linkName).call();
994 git.commit().setMessage("Added file and link").call();
995
996 assertWorkDir(mkmap(linkName, "a", fname, "a"));
997
998
999 FileUtils.delete(link);
1000 FileUtils.mkdir(link);
1001 assertTrue("Link must be a directory now", link.isDirectory());
1002
1003
1004 writeTrashFile(fname, "b");
1005 assertWorkDir(mkmap(fname, "b", linkName, "/"));
1006 recorder.assertNoEvent();
1007
1008
1009 git.checkout().setStartPoint(Constants.HEAD).addPath(fname)
1010 .addPath(linkName).call();
1011
1012 assertWorkDir(mkmap(fname, "a", linkName, "a"));
1013 recorder.assertEvent(new String[] { fname, linkName },
1014 ChangeRecorder.EMPTY);
1015
1016 Status st = git.status().call();
1017 assertTrue(st.isClean());
1018 } finally {
1019 if (handle != null) {
1020 handle.remove();
1021 }
1022 }
1023 }
1024
1025 @Test
1026 public void testCheckoutChangeLinkToEmptyDirs() throws Exception {
1027 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1028 String fname = "was_file";
1029 ChangeRecorder recorder = new ChangeRecorder();
1030 ListenerHandle handle = null;
1031 try (Git git = new Git(db)) {
1032 handle = db.getListenerList()
1033 .addWorkingTreeModifiedListener(recorder);
1034
1035 writeTrashFile(fname, "a");
1036 git.add().addFilepattern(fname).call();
1037
1038
1039 String linkName = "link";
1040 File link = writeLink(linkName, fname).toFile();
1041 git.add().addFilepattern(linkName).call();
1042 git.commit().setMessage("Added file and link").call();
1043
1044 assertWorkDir(mkmap(linkName, "a", fname, "a"));
1045
1046
1047 FileUtils.delete(link);
1048 FileUtils.mkdirs(new File(link, "dummyDir"));
1049 assertTrue("Link must be a directory now", link.isDirectory());
1050
1051 assertFalse("Must not delete non empty directory", link.delete());
1052
1053
1054 writeTrashFile(fname, "b");
1055 assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/"));
1056 recorder.assertNoEvent();
1057
1058
1059 git.checkout().setStartPoint(Constants.HEAD).addPath(fname)
1060 .addPath(linkName).call();
1061
1062 assertWorkDir(mkmap(fname, "a", linkName, "a"));
1063 recorder.assertEvent(new String[] { fname, linkName },
1064 ChangeRecorder.EMPTY);
1065
1066 Status st = git.status().call();
1067 assertTrue(st.isClean());
1068 } finally {
1069 if (handle != null) {
1070 handle.remove();
1071 }
1072 }
1073 }
1074
1075 @Test
1076 public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception {
1077 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1078 String fname = "file";
1079 ChangeRecorder recorder = new ChangeRecorder();
1080 ListenerHandle handle = null;
1081 try (Git git = new Git(db)) {
1082 handle = db.getListenerList()
1083 .addWorkingTreeModifiedListener(recorder);
1084
1085 writeTrashFile(fname, "a");
1086 git.add().addFilepattern(fname).call();
1087
1088
1089 String linkName = "link";
1090 File link = writeLink(linkName, fname).toFile();
1091 git.add().addFilepattern(linkName).call();
1092 git.commit().setMessage("Added file and link").call();
1093
1094 assertWorkDir(mkmap(linkName, "a", fname, "a"));
1095
1096
1097 FileUtils.delete(link);
1098
1099
1100 writeTrashFile(linkName + "/dir1", "file1", "c");
1101
1102
1103 writeTrashFile(linkName + "/dir2", "file2", "d");
1104
1105 assertTrue("File must be a directory now", link.isDirectory());
1106 assertFalse("Must not delete non empty directory", link.delete());
1107
1108
1109 assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1110 linkName + "/dir2/file2", "d"));
1111 recorder.assertNoEvent();
1112
1113
1114 git.checkout().setStartPoint(Constants.HEAD).addPath(linkName)
1115 .call();
1116
1117
1118 assertWorkDir(mkmap(linkName, "a", fname, "a"));
1119 recorder.assertEvent(new String[] { linkName },
1120 ChangeRecorder.EMPTY);
1121
1122 Status st = git.status().call();
1123 assertTrue(st.isClean());
1124 } finally {
1125 if (handle != null) {
1126 handle.remove();
1127 }
1128 }
1129 }
1130
1131 @Test
1132 public void testCheckoutChangeLinkToNonEmptyDirsAndNewIndexEntry()
1133 throws Exception {
1134 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1135 String fname = "file";
1136 ChangeRecorder recorder = new ChangeRecorder();
1137 ListenerHandle handle = null;
1138 try (Git git = new Git(db)) {
1139 handle = db.getListenerList()
1140 .addWorkingTreeModifiedListener(recorder);
1141
1142 writeTrashFile(fname, "a");
1143 git.add().addFilepattern(fname).call();
1144
1145
1146 String linkName = "link";
1147 File link = writeLink(linkName, fname).toFile();
1148 git.add().addFilepattern(linkName).call();
1149 git.commit().setMessage("Added file and link").call();
1150
1151 assertWorkDir(mkmap(linkName, "a", fname, "a"));
1152
1153
1154 FileUtils.delete(link);
1155
1156
1157 writeTrashFile(linkName + "/dir1", "file1", "c");
1158 git.add().addFilepattern(linkName + "/dir1/file1").call();
1159
1160
1161 writeTrashFile(linkName + "/dir2", "file2", "d");
1162
1163 assertTrue("File must be a directory now", link.isDirectory());
1164 assertFalse("Must not delete non empty directory", link.delete());
1165
1166
1167 assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1168 linkName + "/dir2/file2", "d"));
1169 recorder.assertNoEvent();
1170
1171
1172 git.checkout().setStartPoint(Constants.HEAD).addPath(linkName)
1173 .call();
1174
1175
1176 assertWorkDir(mkmap(linkName, "a", fname, "a"));
1177 recorder.assertEvent(new String[] { linkName },
1178 ChangeRecorder.EMPTY);
1179
1180 Status st = git.status().call();
1181 assertTrue(st.isClean());
1182 } finally {
1183 if (handle != null) {
1184 handle.remove();
1185 }
1186 }
1187 }
1188
1189 @Test
1190 public void testCheckoutChangeFileToEmptyDir() throws Exception {
1191 String fname = "was_file";
1192 ChangeRecorder recorder = new ChangeRecorder();
1193 ListenerHandle handle = null;
1194 try (Git git = new Git(db)) {
1195 handle = db.getListenerList()
1196 .addWorkingTreeModifiedListener(recorder);
1197
1198 File file = writeTrashFile(fname, "a");
1199 git.add().addFilepattern(fname).call();
1200 git.commit().setMessage("Added file").call();
1201
1202
1203 FileUtils.delete(file);
1204 FileUtils.mkdir(file);
1205 assertTrue("File must be a directory now", file.isDirectory());
1206 assertWorkDir(mkmap(fname, "/"));
1207 recorder.assertNoEvent();
1208
1209
1210 git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1211 assertWorkDir(mkmap(fname, "a"));
1212 recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
1213
1214 Status st = git.status().call();
1215 assertTrue(st.isClean());
1216 } finally {
1217 if (handle != null) {
1218 handle.remove();
1219 }
1220 }
1221 }
1222
1223 @Test
1224 public void testCheckoutChangeFileToEmptyDirs() throws Exception {
1225 String fname = "was_file";
1226 ChangeRecorder recorder = new ChangeRecorder();
1227 ListenerHandle handle = null;
1228 try (Git git = new Git(db)) {
1229 handle = db.getListenerList()
1230 .addWorkingTreeModifiedListener(recorder);
1231
1232 File file = writeTrashFile(fname, "a");
1233 git.add().addFilepattern(fname).call();
1234 git.commit().setMessage("Added file").call();
1235
1236
1237 FileUtils.delete(file);
1238 FileUtils.mkdirs(new File(file, "dummyDir"));
1239 assertTrue("File must be a directory now", file.isDirectory());
1240 assertFalse("Must not delete non empty directory", file.delete());
1241
1242 assertWorkDir(mkmap(fname + "/dummyDir", "/"));
1243 recorder.assertNoEvent();
1244
1245
1246 git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1247 assertWorkDir(mkmap(fname, "a"));
1248 recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
1249
1250 Status st = git.status().call();
1251 assertTrue(st.isClean());
1252 } finally {
1253 if (handle != null) {
1254 handle.remove();
1255 }
1256 }
1257 }
1258
1259 @Test
1260 public void testCheckoutChangeFileToNonEmptyDirs() throws Exception {
1261 String fname = "was_file";
1262 ChangeRecorder recorder = new ChangeRecorder();
1263 ListenerHandle handle = null;
1264 try (Git git = new Git(db)) {
1265 handle = db.getListenerList()
1266 .addWorkingTreeModifiedListener(recorder);
1267
1268 File file = writeTrashFile(fname, "a");
1269 git.add().addFilepattern(fname).call();
1270 git.commit().setMessage("Added file").call();
1271
1272 assertWorkDir(mkmap(fname, "a"));
1273
1274
1275 FileUtils.delete(file);
1276
1277
1278 writeTrashFile(fname + "/dir1", "file1", "c");
1279
1280
1281 writeTrashFile(fname + "/dir2", "file2", "d");
1282
1283 assertTrue("File must be a directory now", file.isDirectory());
1284 assertFalse("Must not delete non empty directory", file.delete());
1285
1286
1287 assertWorkDir(mkmap(fname + "/dir1/file1", "c",
1288 fname + "/dir2/file2", "d"));
1289 recorder.assertNoEvent();
1290
1291
1292 git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1293
1294
1295 assertWorkDir(mkmap(fname, "a"));
1296 recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
1297
1298 Status st = git.status().call();
1299 assertTrue(st.isClean());
1300 } finally {
1301 if (handle != null) {
1302 handle.remove();
1303 }
1304 }
1305 }
1306
1307 @Test
1308 public void testCheckoutChangeFileToNonEmptyDirsAndNewIndexEntry()
1309 throws Exception {
1310 String fname = "was_file";
1311 ChangeRecorder recorder = new ChangeRecorder();
1312 ListenerHandle handle = null;
1313 try (Git git = new Git(db)) {
1314 handle = db.getListenerList()
1315 .addWorkingTreeModifiedListener(recorder);
1316
1317 File file = writeTrashFile(fname, "a");
1318 git.add().addFilepattern(fname).call();
1319 git.commit().setMessage("Added file").call();
1320
1321 assertWorkDir(mkmap(fname, "a"));
1322
1323
1324 FileUtils.delete(file);
1325
1326
1327 writeTrashFile(fname + "/dir", "file1", "c");
1328 git.add().addFilepattern(fname + "/dir/file1").call();
1329
1330
1331 writeTrashFile(fname + "/dir", "file2", "d");
1332
1333 assertTrue("File must be a directory now", file.isDirectory());
1334 assertFalse("Must not delete non empty directory", file.delete());
1335
1336
1337 assertWorkDir(mkmap(fname + "/dir/file1", "c", fname + "/dir/file2",
1338 "d"));
1339 recorder.assertNoEvent();
1340
1341
1342 git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
1343 assertWorkDir(mkmap(fname, "a"));
1344 recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
1345 Status st = git.status().call();
1346 assertTrue(st.isClean());
1347 } finally {
1348 if (handle != null) {
1349 handle.remove();
1350 }
1351 }
1352 }
1353
1354 @Test
1355 public void testCheckoutOutChangesAutoCRLFfalse() throws IOException {
1356 setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
1357 checkout();
1358 assertIndex(mkmap("foo/bar", "foo\nbar"));
1359 assertWorkDir(mkmap("foo/bar", "foo\nbar"));
1360 }
1361
1362 @Test
1363 public void testCheckoutOutChangesAutoCRLFInput() throws IOException {
1364 setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
1365 db.getConfig().setString("core", null, "autocrlf", "input");
1366 checkout();
1367 assertIndex(mkmap("foo/bar", "foo\nbar"));
1368 assertWorkDir(mkmap("foo/bar", "foo\nbar"));
1369 }
1370
1371 @Test
1372 public void testCheckoutOutChangesAutoCRLFtrue() throws IOException {
1373 setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
1374 db.getConfig().setString("core", null, "autocrlf", "true");
1375 checkout();
1376 assertIndex(mkmap("foo/bar", "foo\nbar"));
1377 assertWorkDir(mkmap("foo/bar", "foo\r\nbar"));
1378 }
1379
1380 @Test
1381 public void testCheckoutOutChangesAutoCRLFtrueBinary() throws IOException {
1382 setupCase(mk("foo"), mkmap("foo/bar", "foo\nb\u0000ar"), mk("foo"));
1383 db.getConfig().setString("core", null, "autocrlf", "true");
1384 checkout();
1385 assertIndex(mkmap("foo/bar", "foo\nb\u0000ar"));
1386 assertWorkDir(mkmap("foo/bar", "foo\nb\u0000ar"));
1387 }
1388
1389 @Test
1390 public void testCheckoutUncachedChanges() throws IOException {
1391 setupCase(mk("foo"), mk("foo"), mk("foo"));
1392 writeTrashFile("foo", "otherData");
1393 checkout();
1394 assertIndex(mk("foo"));
1395 assertWorkDir(mkmap("foo", "otherData"));
1396 assertTrue(new File(trash, "foo").isFile());
1397 }
1398
1399 @Test
1400 public void testDontOverwriteDirtyFile() throws IOException {
1401 setupCase(mk("foo"), mk("other"), mk("foo"));
1402 writeTrashFile("foo", "different");
1403 try {
1404 checkout();
1405 fail("Didn't got the expected conflict");
1406 } catch (CheckoutConflictException e) {
1407 assertIndex(mk("foo"));
1408 assertWorkDir(mkmap("foo", "different"));
1409 assertEquals(Arrays.asList("foo"), getConflicts());
1410 assertTrue(new File(trash, "foo").isFile());
1411 }
1412 }
1413
1414 @Test
1415 public void testDontOverwriteEmptyFolder() throws IOException {
1416 setupCase(mk("foo"), mk("foo"), mk("foo"));
1417 FileUtils.mkdir(new File(db.getWorkTree(), "d"));
1418 checkout();
1419 assertWorkDir(mkmap("foo", "foo", "d", "/"));
1420 }
1421
1422 @Test
1423 public void testOverwriteUntrackedIgnoredFile() throws IOException,
1424 GitAPIException {
1425 String fname="file.txt";
1426 ChangeRecorder recorder = new ChangeRecorder();
1427 ListenerHandle handle = null;
1428 try (Git git = new Git(db)) {
1429 handle = db.getListenerList()
1430 .addWorkingTreeModifiedListener(recorder);
1431
1432 writeTrashFile(fname, "a");
1433 git.add().addFilepattern(fname).call();
1434 git.commit().setMessage("create file").call();
1435
1436
1437 git.branchCreate().setName("side").call();
1438
1439
1440 writeTrashFile(fname, "b");
1441 git.add().addFilepattern(fname).call();
1442 git.commit().setMessage("modify file").call();
1443 recorder.assertNoEvent();
1444
1445
1446 git.checkout().setName("side").call();
1447 recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
1448 git.rm().addFilepattern(fname).call();
1449 recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { fname });
1450 writeTrashFile(".gitignore", fname);
1451 git.add().addFilepattern(".gitignore").call();
1452 git.commit().setMessage("delete and ignore file").call();
1453
1454 writeTrashFile(fname, "Something different");
1455 recorder.assertNoEvent();
1456 git.checkout().setName("master").call();
1457 assertWorkDir(mkmap(fname, "b"));
1458 recorder.assertEvent(new String[] { fname },
1459 new String[] { ".gitignore" });
1460 assertTrue(git.status().call().isClean());
1461 } finally {
1462 if (handle != null) {
1463 handle.remove();
1464 }
1465 }
1466 }
1467
1468 @Test
1469 public void testOverwriteUntrackedFileModeChange()
1470 throws IOException, GitAPIException {
1471 String fname = "file.txt";
1472 ChangeRecorder recorder = new ChangeRecorder();
1473 ListenerHandle handle = null;
1474 try (Git git = new Git(db)) {
1475 handle = db.getListenerList()
1476 .addWorkingTreeModifiedListener(recorder);
1477
1478 File file = writeTrashFile(fname, "a");
1479 git.add().addFilepattern(fname).call();
1480 git.commit().setMessage("create file").call();
1481 assertWorkDir(mkmap(fname, "a"));
1482
1483
1484 git.branchCreate().setName("side").call();
1485
1486
1487 git.checkout().setName("side").call();
1488 recorder.assertNoEvent();
1489
1490
1491 FileUtils.delete(file);
1492
1493
1494 writeTrashFile(fname + "/dir1", "file1", "c");
1495 git.add().addFilepattern(fname + "/dir1/file1").call();
1496
1497
1498 writeTrashFile(fname + "/dir2", "file2", "d");
1499
1500 assertTrue("File must be a directory now", file.isDirectory());
1501 assertFalse("Must not delete non empty directory", file.delete());
1502
1503
1504 assertWorkDir(mkmap(fname + "/dir1/file1", "c",
1505 fname + "/dir2/file2", "d"));
1506
1507 try {
1508 git.checkout().setName("master").call();
1509 fail("did not throw exception");
1510 } catch (Exception e) {
1511
1512 assertWorkDir(mkmap(fname + "/dir1/file1", "c",
1513 fname + "/dir2/file2", "d"));
1514 }
1515 recorder.assertNoEvent();
1516 } finally {
1517 if (handle != null) {
1518 handle.remove();
1519 }
1520 }
1521 }
1522
1523 @Test
1524 public void testOverwriteUntrackedLinkModeChange()
1525 throws Exception {
1526 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
1527 String fname = "file.txt";
1528 ChangeRecorder recorder = new ChangeRecorder();
1529 ListenerHandle handle = null;
1530 try (Git git = new Git(db)) {
1531 handle = db.getListenerList()
1532 .addWorkingTreeModifiedListener(recorder);
1533
1534 writeTrashFile(fname, "a");
1535 git.add().addFilepattern(fname).call();
1536
1537
1538 String linkName = "link";
1539 File link = writeLink(linkName, fname).toFile();
1540 git.add().addFilepattern(linkName).call();
1541 git.commit().setMessage("Added file and link").call();
1542
1543 assertWorkDir(mkmap(linkName, "a", fname, "a"));
1544
1545
1546 git.branchCreate().setName("side").call();
1547
1548
1549 git.checkout().setName("side").call();
1550 recorder.assertNoEvent();
1551
1552
1553 FileUtils.delete(link);
1554
1555
1556 writeTrashFile(linkName + "/dir1", "file1", "c");
1557 git.add().addFilepattern(linkName + "/dir1/file1").call();
1558
1559
1560 writeTrashFile(linkName + "/dir2", "file2", "d");
1561
1562 assertTrue("Link must be a directory now", link.isDirectory());
1563 assertFalse("Must not delete non empty directory", link.delete());
1564
1565
1566 assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1567 linkName + "/dir2/file2", "d"));
1568
1569 try {
1570 git.checkout().setName("master").call();
1571 fail("did not throw exception");
1572 } catch (Exception e) {
1573
1574 assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
1575 linkName + "/dir2/file2", "d"));
1576 }
1577 recorder.assertNoEvent();
1578 } finally {
1579 if (handle != null) {
1580 handle.remove();
1581 }
1582 }
1583 }
1584
1585 @Test
1586 public void testFileModeChangeWithNoContentChangeUpdate() throws Exception {
1587 if (!FS.DETECTED.supportsExecute())
1588 return;
1589
1590 ChangeRecorder recorder = new ChangeRecorder();
1591 ListenerHandle handle = null;
1592 try (Git git = new Git(db)) {
1593 handle = db.getListenerList()
1594 .addWorkingTreeModifiedListener(recorder);
1595
1596 File file = writeTrashFile("file.txt", "a");
1597 git.add().addFilepattern("file.txt").call();
1598 git.commit().setMessage("commit1").call();
1599 assertFalse(db.getFS().canExecute(file));
1600
1601
1602 git.branchCreate().setName("b1").call();
1603
1604
1605 db.getFS().setExecute(file, true);
1606 git.add().addFilepattern("file.txt").call();
1607 git.commit().setMessage("commit2").call();
1608 recorder.assertNoEvent();
1609
1610
1611 Status status = git.status().call();
1612 assertTrue(status.getModified().isEmpty());
1613 assertTrue(status.getChanged().isEmpty());
1614 assertTrue(db.getFS().canExecute(file));
1615
1616
1617 git.checkout().setName("b1").call();
1618
1619
1620 status = git.status().call();
1621 assertTrue(status.getModified().isEmpty());
1622 assertTrue(status.getChanged().isEmpty());
1623 assertFalse(db.getFS().canExecute(file));
1624 recorder.assertEvent(new String[] { "file.txt" },
1625 ChangeRecorder.EMPTY);
1626 } finally {
1627 if (handle != null) {
1628 handle.remove();
1629 }
1630 }
1631 }
1632
1633 @Test
1634 public void testFileModeChangeAndContentChangeConflict() throws Exception {
1635 if (!FS.DETECTED.supportsExecute())
1636 return;
1637
1638 ChangeRecorder recorder = new ChangeRecorder();
1639 ListenerHandle handle = null;
1640 try (Git git = new Git(db)) {
1641 handle = db.getListenerList()
1642 .addWorkingTreeModifiedListener(recorder);
1643
1644 File file = writeTrashFile("file.txt", "a");
1645 git.add().addFilepattern("file.txt").call();
1646 git.commit().setMessage("commit1").call();
1647 assertFalse(db.getFS().canExecute(file));
1648
1649
1650 git.branchCreate().setName("b1").call();
1651
1652
1653 db.getFS().setExecute(file, true);
1654 git.add().addFilepattern("file.txt").call();
1655 git.commit().setMessage("commit2").call();
1656
1657
1658 Status status = git.status().call();
1659 assertTrue(status.getModified().isEmpty());
1660 assertTrue(status.getChanged().isEmpty());
1661 assertTrue(db.getFS().canExecute(file));
1662
1663 writeTrashFile("file.txt", "b");
1664
1665
1666 CheckoutCommand checkout = git.checkout().setName("b1");
1667 try {
1668 checkout.call();
1669 fail("Checkout exception not thrown");
1670 } catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
1671 CheckoutResult result = checkout.getResult();
1672 assertNotNull(result);
1673 assertNotNull(result.getConflictList());
1674 assertEquals(1, result.getConflictList().size());
1675 assertTrue(result.getConflictList().contains("file.txt"));
1676 }
1677 recorder.assertNoEvent();
1678 } finally {
1679 if (handle != null) {
1680 handle.remove();
1681 }
1682 }
1683 }
1684
1685 @Test
1686 public void testDirtyFileModeEqualHeadMerge()
1687 throws Exception {
1688 if (!FS.DETECTED.supportsExecute())
1689 return;
1690
1691 ChangeRecorder recorder = new ChangeRecorder();
1692 ListenerHandle handle = null;
1693 try (Git git = new Git(db)) {
1694 handle = db.getListenerList()
1695 .addWorkingTreeModifiedListener(recorder);
1696
1697 File file = writeTrashFile("file.txt", "a");
1698 git.add().addFilepattern("file.txt").call();
1699 git.commit().setMessage("commit1").call();
1700 assertFalse(db.getFS().canExecute(file));
1701
1702
1703 git.branchCreate().setName("b1").call();
1704
1705
1706 writeTrashFile("file2.txt", "");
1707 git.add().addFilepattern("file2.txt").call();
1708 git.commit().setMessage("commit2").call();
1709
1710
1711 writeTrashFile("file.txt", "a");
1712 db.getFS().setExecute(file, true);
1713 git.add().addFilepattern("file.txt").call();
1714
1715
1716 writeTrashFile("file.txt", "b");
1717
1718 assertEquals(
1719 "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
1720 indexState(CONTENT));
1721 assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
1722 recorder.assertNoEvent();
1723
1724
1725
1726 git.checkout().setName("b1").call();
1727 assertEquals("[file.txt, mode:100755, content:a]",
1728 indexState(CONTENT));
1729 assertWorkDir(mkmap("file.txt", "b"));
1730 recorder.assertEvent(ChangeRecorder.EMPTY,
1731 new String[] { "file2.txt" });
1732 } finally {
1733 if (handle != null) {
1734 handle.remove();
1735 }
1736 }
1737 }
1738
1739 @Test
1740 public void testDirtyFileModeEqualIndexMerge()
1741 throws Exception {
1742 if (!FS.DETECTED.supportsExecute())
1743 return;
1744
1745 ChangeRecorder recorder = new ChangeRecorder();
1746 ListenerHandle handle = null;
1747 try (Git git = new Git(db)) {
1748 handle = db.getListenerList()
1749 .addWorkingTreeModifiedListener(recorder);
1750
1751 File file = writeTrashFile("file.txt", "a");
1752 git.add().addFilepattern("file.txt").call();
1753 git.commit().setMessage("commit1").call();
1754 assertFalse(db.getFS().canExecute(file));
1755
1756
1757 git.branchCreate().setName("b1").call();
1758
1759
1760 file = writeTrashFile("file.txt", "b");
1761 db.getFS().setExecute(file, true);
1762 git.add().addFilepattern("file.txt").call();
1763 git.commit().setMessage("commit2").call();
1764
1765
1766 writeTrashFile("file.txt", "a");
1767 db.getFS().setExecute(file, false);
1768 git.add().addFilepattern("file.txt").call();
1769
1770
1771 writeTrashFile("file.txt", "c");
1772 db.getFS().setExecute(file, true);
1773
1774 assertEquals("[file.txt, mode:100644, content:a]",
1775 indexState(CONTENT));
1776 assertWorkDir(mkmap("file.txt", "c"));
1777 recorder.assertNoEvent();
1778
1779
1780
1781
1782 git.checkout().setName("b1").call();
1783 assertEquals("[file.txt, mode:100644, content:a]",
1784 indexState(CONTENT));
1785 assertWorkDir(mkmap("file.txt", "c"));
1786 recorder.assertNoEvent();
1787 } finally {
1788 if (handle != null) {
1789 handle.remove();
1790 }
1791 }
1792 }
1793
1794 @Test
1795 public void testFileModeChangeAndContentChangeNoConflict() throws Exception {
1796 if (!FS.DETECTED.supportsExecute())
1797 return;
1798
1799 ChangeRecorder recorder = new ChangeRecorder();
1800 ListenerHandle handle = null;
1801 try (Git git = new Git(db)) {
1802 handle = db.getListenerList()
1803 .addWorkingTreeModifiedListener(recorder);
1804
1805 File file1 = writeTrashFile("file1.txt", "a");
1806 git.add().addFilepattern("file1.txt").call();
1807 git.commit().setMessage("commit1").call();
1808 assertFalse(db.getFS().canExecute(file1));
1809
1810
1811 File file2 = writeTrashFile("file2.txt", "b");
1812 git.add().addFilepattern("file2.txt").call();
1813 git.commit().setMessage("commit2").call();
1814 assertFalse(db.getFS().canExecute(file2));
1815 recorder.assertNoEvent();
1816
1817
1818 assertNotNull(git.checkout().setCreateBranch(true).setName("b1")
1819 .setStartPoint(Constants.HEAD + "~1").call());
1820 recorder.assertEvent(ChangeRecorder.EMPTY,
1821 new String[] { "file2.txt" });
1822
1823
1824 file1 = writeTrashFile("file1.txt", "c");
1825 db.getFS().setExecute(file1, true);
1826 git.add().addFilepattern("file1.txt").call();
1827
1828
1829 assertNotNull(git.checkout().setName(Constants.MASTER).call());
1830 recorder.assertEvent(new String[] { "file2.txt" },
1831 ChangeRecorder.EMPTY);
1832 } finally {
1833 if (handle != null) {
1834 handle.remove();
1835 }
1836 }
1837 }
1838
1839 @Test(expected = CheckoutConflictException.class)
1840 public void testFolderFileConflict() throws Exception {
1841 RevCommit headCommit = commitFile("f/a", "initial content", "master");
1842 RevCommit checkoutCommit = commitFile("f/a", "side content", "side");
1843 FileUtils.delete(new File(db.getWorkTree(), "f"), FileUtils.RECURSIVE);
1844 writeTrashFile("f", "file instead of folder");
1845 new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(),
1846 checkoutCommit.getTree()).checkout();
1847 }
1848
1849 @Test
1850 public void testMultipleContentConflicts() throws Exception {
1851 commitFile("a", "initial content", "master");
1852 RevCommit headCommit = commitFile("b", "initial content", "master");
1853 commitFile("a", "side content", "side");
1854 RevCommit checkoutCommit = commitFile("b", "side content", "side");
1855 writeTrashFile("a", "changed content");
1856 writeTrashFile("b", "changed content");
1857
1858 try {
1859 new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(),
1860 checkoutCommit.getTree()).checkout();
1861 fail();
1862 } catch (CheckoutConflictException expected) {
1863 assertEquals(2, expected.getConflictingFiles().length);
1864 assertTrue(Arrays.asList(expected.getConflictingFiles())
1865 .contains("a"));
1866 assertTrue(Arrays.asList(expected.getConflictingFiles())
1867 .contains("b"));
1868 assertEquals("changed content", read("a"));
1869 assertEquals("changed content", read("b"));
1870 }
1871 }
1872
1873 @Test
1874 public void testFolderFileAndContentConflicts() throws Exception {
1875 RevCommit headCommit = commitFile("f/a", "initial content", "master");
1876 commitFile("b", "side content", "side");
1877 RevCommit checkoutCommit = commitFile("f/a", "side content", "side");
1878 FileUtils.delete(new File(db.getWorkTree(), "f"), FileUtils.RECURSIVE);
1879 writeTrashFile("f", "file instead of a folder");
1880 writeTrashFile("b", "changed content");
1881
1882 try {
1883 new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(),
1884 checkoutCommit.getTree()).checkout();
1885 fail();
1886 } catch (CheckoutConflictException expected) {
1887 assertEquals(2, expected.getConflictingFiles().length);
1888 assertTrue(Arrays.asList(expected.getConflictingFiles())
1889 .contains("b"));
1890 assertTrue(Arrays.asList(expected.getConflictingFiles())
1891 .contains("f"));
1892 assertEquals("file instead of a folder", read("f"));
1893 assertEquals("changed content", read("b"));
1894 }
1895 }
1896
1897 @Test
1898 public void testLongFilename() throws Exception {
1899 char[] bytes = new char[253];
1900 Arrays.fill(bytes, 'f');
1901 String longFileName = new String(bytes);
1902
1903 doit(mkmap(longFileName, "a"), mkmap(longFileName, "b"),
1904 mkmap(longFileName, "a"));
1905 writeTrashFile(longFileName, "a");
1906 checkout();
1907 assertNoConflicts();
1908 assertUpdated(longFileName);
1909 }
1910
1911 @Test
1912 public void testIgnoredDirectory() throws Exception {
1913 writeTrashFile(".gitignore", "src/ignored");
1914 writeTrashFile("src/ignored/sub/foo.txt", "1");
1915 try (Git git = new Git(db)) {
1916 git.add().addFilepattern(".").call();
1917 RevCommit commit = git.commit().setMessage("adding .gitignore")
1918 .call();
1919 writeTrashFile("foo.txt", "2");
1920 writeTrashFile("zzz.txt", "3");
1921 git.add().addFilepattern("foo.txt").call();
1922 git.commit().setMessage("add file").call();
1923 assertEquals("Should not have entered ignored directory", 1,
1924 resetHardAndCount(commit));
1925 }
1926 }
1927
1928 @Test
1929 public void testIgnoredDirectoryWithTrackedContent() throws Exception {
1930 writeTrashFile("src/ignored/sub/foo.txt", "1");
1931 try (Git git = new Git(db)) {
1932 git.add().addFilepattern(".").call();
1933 git.commit().setMessage("adding foo.txt").call();
1934 writeTrashFile(".gitignore", "src/ignored");
1935 writeTrashFile("src/ignored/sub/foo.txt", "2");
1936 writeTrashFile("src/ignored/other/bar.txt", "3");
1937 git.add().addFilepattern(".").call();
1938 RevCommit commit = git.commit().setMessage("adding .gitignore")
1939 .call();
1940 writeTrashFile("foo.txt", "2");
1941 writeTrashFile("zzz.txt", "3");
1942 git.add().addFilepattern("foo.txt").call();
1943 git.commit().setMessage("add file").call();
1944 File file = writeTrashFile("src/ignored/sub/foo.txt", "3");
1945 assertEquals("Should have entered ignored directory", 3,
1946 resetHardAndCount(commit));
1947 checkFile(file, "2");
1948 }
1949 }
1950
1951 @Test
1952 public void testResetWithChangeInGitignore() throws Exception {
1953 writeTrashFile(".gitignore", "src/ignored");
1954 writeTrashFile("src/ignored/sub/foo.txt", "1");
1955 try (Git git = new Git(db)) {
1956 git.add().addFilepattern(".").call();
1957 RevCommit initial = git.commit().setMessage("initial").call();
1958 writeTrashFile("src/newignored/foo.txt", "2");
1959 writeTrashFile("src/.gitignore", "newignored");
1960 git.add().addFilepattern(".").call();
1961 RevCommit commit = git.commit().setMessage("newignored").call();
1962 assertEquals("Should not have entered src/newignored directory", 1,
1963 resetHardAndCount(initial));
1964 assertEquals("Should have entered src/newignored directory", 2,
1965 resetHardAndCount(commit));
1966 deleteTrashFile("src/.gitignore");
1967 git.rm().addFilepattern("src/.gitignore").call();
1968 RevCommit top = git.commit().setMessage("Unignore newignore")
1969 .call();
1970 assertEquals("Should have entered src/newignored directory", 2,
1971 resetHardAndCount(initial));
1972 assertEquals("Should have entered src/newignored directory", 2,
1973 resetHardAndCount(commit));
1974 assertEquals("Should not have entered src/newignored directory", 1,
1975 resetHardAndCount(top));
1976
1977 }
1978 }
1979
1980 private static class TestFileTreeIterator extends FileTreeIterator {
1981
1982
1983 private final int[] count;
1984
1985 public TestFileTreeIterator(Repository repo, int[] count) {
1986 super(repo);
1987 this.count = count;
1988 }
1989
1990 protected TestFileTreeIterator(final WorkingTreeIterator p,
1991 final File root, FS fs, FileModeStrategy fileModeStrategy,
1992 int[] count) {
1993 super(p, root, fs, fileModeStrategy);
1994 this.count = count;
1995 }
1996
1997 @Override
1998 protected AbstractTreeIterator enterSubtree() {
1999 count[0] += 1;
2000 return new TestFileTreeIterator(this,
2001 ((FileEntry) current()).getFile(), fs, fileModeStrategy,
2002 count);
2003 }
2004 }
2005
2006 private int resetHardAndCount(RevCommit commit) throws Exception {
2007 int[] callCount = { 0 };
2008 DirCache cache = db.lockDirCache();
2009 FileTreeIterator workingTreeIterator = new TestFileTreeIterator(db,
2010 callCount);
2011 try {
2012 DirCacheCheckout checkout = new DirCacheCheckout(db, null, cache,
2013 commit.getTree().getId(), workingTreeIterator);
2014 checkout.setFailOnConflict(false);
2015 checkout.checkout();
2016 } finally {
2017 cache.unlock();
2018 }
2019 return callCount[0];
2020 }
2021
2022 public void assertWorkDir(Map<String, String> i)
2023 throws CorruptObjectException,
2024 IOException {
2025 try (TreeWalk walk = new TreeWalk(db)) {
2026 walk.setRecursive(false);
2027 walk.addTree(new FileTreeIterator(db));
2028 String expectedValue;
2029 String path;
2030 int nrFiles = 0;
2031 FileTreeIterator ft;
2032 while (walk.next()) {
2033 ft = walk.getTree(0, FileTreeIterator.class);
2034 path = ft.getEntryPathString();
2035 expectedValue = i.get(path);
2036 File file = new File(db.getWorkTree(), path);
2037 assertTrue(file.exists());
2038 if (file.isFile()) {
2039 assertNotNull("found unexpected file for path " + path
2040 + " in workdir", expectedValue);
2041 try (FileInputStream is = new FileInputStream(file)) {
2042 byte[] buffer = new byte[(int) file.length()];
2043 int offset = 0;
2044 int numRead = 0;
2045 while (offset < buffer.length
2046 && (numRead = is.read(buffer, offset,
2047 buffer.length - offset)) >= 0) {
2048 offset += numRead;
2049 }
2050 assertArrayEquals(
2051 "unexpected content for path " + path
2052 + " in workDir. ",
2053 buffer, i.get(path).getBytes(UTF_8));
2054 }
2055 nrFiles++;
2056 } else if (file.isDirectory()) {
2057 String[] files = file.list();
2058 if (files != null && files.length == 0) {
2059 assertEquals("found unexpected empty folder for path "
2060 + path + " in workDir. ", "/", i.get(path));
2061 nrFiles++;
2062 }
2063 }
2064 if (walk.isSubtree()) {
2065 walk.enterSubtree();
2066 }
2067 }
2068 assertEquals("WorkDir has not the right size.", i.size(), nrFiles);
2069 }
2070 }
2071 }