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
42
43 package org.eclipse.jgit.dircache;
44
45 import static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP;
46
47 import java.io.File;
48 import java.io.FileOutputStream;
49 import java.io.IOException;
50 import java.io.OutputStream;
51 import java.nio.file.StandardCopyOption;
52 import java.text.MessageFormat;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Map;
58
59 import org.eclipse.jgit.api.errors.CanceledException;
60 import org.eclipse.jgit.api.errors.FilterFailedException;
61 import org.eclipse.jgit.attributes.FilterCommand;
62 import org.eclipse.jgit.attributes.FilterCommandRegistry;
63 import org.eclipse.jgit.errors.CheckoutConflictException;
64 import org.eclipse.jgit.errors.CorruptObjectException;
65 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
66 import org.eclipse.jgit.errors.IndexWriteException;
67 import org.eclipse.jgit.errors.MissingObjectException;
68 import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
69 import org.eclipse.jgit.internal.JGitText;
70 import org.eclipse.jgit.lib.ConfigConstants;
71 import org.eclipse.jgit.lib.Constants;
72 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
73 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
74 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
75 import org.eclipse.jgit.lib.FileMode;
76 import org.eclipse.jgit.lib.NullProgressMonitor;
77 import org.eclipse.jgit.lib.ObjectChecker;
78 import org.eclipse.jgit.lib.ObjectId;
79 import org.eclipse.jgit.lib.ObjectLoader;
80 import org.eclipse.jgit.lib.ObjectReader;
81 import org.eclipse.jgit.lib.ProgressMonitor;
82 import org.eclipse.jgit.lib.Repository;
83 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
84 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
85 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
86 import org.eclipse.jgit.treewalk.FileTreeIterator;
87 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
88 import org.eclipse.jgit.treewalk.TreeWalk;
89 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
90 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
91 import org.eclipse.jgit.treewalk.filter.PathFilter;
92 import org.eclipse.jgit.util.FS;
93 import org.eclipse.jgit.util.FS.ExecutionResult;
94 import org.eclipse.jgit.util.FileUtils;
95 import org.eclipse.jgit.util.IntList;
96 import org.eclipse.jgit.util.RawParseUtils;
97 import org.eclipse.jgit.util.SystemReader;
98 import org.eclipse.jgit.util.io.EolStreamTypeUtil;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102
103
104
105 public class DirCacheCheckout {
106 private static Logger LOG = LoggerFactory.getLogger(DirCacheCheckout.class);
107
108 private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
109
110
111
112
113
114
115 public static class CheckoutMetadata {
116
117 public final EolStreamType eolStreamType;
118
119
120 public final String smudgeFilterCommand;
121
122
123
124
125
126 public CheckoutMetadata(EolStreamType eolStreamType,
127 String smudgeFilterCommand) {
128 this.eolStreamType = eolStreamType;
129 this.smudgeFilterCommand = smudgeFilterCommand;
130 }
131
132 static CheckoutMetadata EMPTY = new CheckoutMetadata(
133 EolStreamType.DIRECT, null);
134 }
135
136 private Repository repo;
137
138 private HashMap<String, CheckoutMetadata> updated = new HashMap<>();
139
140 private ArrayList<String> conflicts = new ArrayList<>();
141
142 private ArrayList<String> removed = new ArrayList<>();
143
144 private ObjectId mergeCommitTree;
145
146 private DirCache dc;
147
148 private DirCacheBuilder builder;
149
150 private NameConflictTreeWalk walk;
151
152 private ObjectId headCommitTree;
153
154 private WorkingTreeIterator workingTree;
155
156 private boolean failOnConflict = true;
157
158 private boolean force = false;
159
160 private ArrayList<String> toBeDeleted = new ArrayList<>();
161
162 private boolean initialCheckout;
163
164 private boolean performingCheckout;
165
166 private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
167
168
169
170
171
172
173 public Map<String, CheckoutMetadata> getUpdated() {
174 return updated;
175 }
176
177
178
179
180
181
182 public List<String> getConflicts() {
183 return conflicts;
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198 public List<String> getToBeDeleted() {
199 return toBeDeleted;
200 }
201
202
203
204
205
206
207 public List<String> getRemoved() {
208 return removed;
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
228 ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
229 throws IOException {
230 this.repo = repo;
231 this.dc = dc;
232 this.headCommitTree = headCommitTree;
233 this.mergeCommitTree = mergeCommitTree;
234 this.workingTree = workingTree;
235 this.initialCheckout = !repo.isBare() && !repo.getIndexFile().exists();
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 public DirCacheCheckout(Repository repo, ObjectId headCommitTree,
255 DirCache dc, ObjectId mergeCommitTree) throws IOException {
256 this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(repo));
257 }
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 public DirCacheCheckout(Repository repo, DirCache dc,
274 ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
275 throws IOException {
276 this(repo, null, dc, mergeCommitTree, workingTree);
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292 public DirCacheCheckout(Repository repo, DirCache dc,
293 ObjectId mergeCommitTree) throws IOException {
294 this(repo, null, dc, mergeCommitTree, new FileTreeIterator(repo));
295 }
296
297
298
299
300
301
302
303
304
305 public void setProgressMonitor(ProgressMonitor monitor) {
306 this.monitor = monitor != null ? monitor : NullProgressMonitor.INSTANCE;
307 }
308
309
310
311
312
313
314
315
316 public void preScanTwoTrees() throws CorruptObjectException, IOException {
317 removed.clear();
318 updated.clear();
319 conflicts.clear();
320 walk = new NameConflictTreeWalk(repo);
321 builder = dc.builder();
322
323 addTree(walk, headCommitTree);
324 addTree(walk, mergeCommitTree);
325 int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
326 walk.addTree(workingTree);
327 workingTree.setDirCacheIterator(walk, dciPos);
328
329 while (walk.next()) {
330 processEntry(walk.getTree(0, CanonicalTreeParser.class),
331 walk.getTree(1, CanonicalTreeParser.class),
332 walk.getTree(2, DirCacheBuildIterator.class),
333 walk.getTree(3, WorkingTreeIterator.class));
334 if (walk.isSubtree())
335 walk.enterSubtree();
336 }
337 }
338
339 private void addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException {
340 if (id == null)
341 tw.addTree(new EmptyTreeIterator());
342 else
343 tw.addTree(id);
344 }
345
346
347
348
349
350
351
352
353
354
355 public void prescanOneTree()
356 throws MissingObjectException, IncorrectObjectTypeException,
357 CorruptObjectException, IOException {
358 removed.clear();
359 updated.clear();
360 conflicts.clear();
361
362 builder = dc.builder();
363
364 walk = new NameConflictTreeWalk(repo);
365 addTree(walk, mergeCommitTree);
366 int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
367 walk.addTree(workingTree);
368 workingTree.setDirCacheIterator(walk, dciPos);
369
370 while (walk.next()) {
371 processEntry(walk.getTree(0, CanonicalTreeParser.class),
372 walk.getTree(1, DirCacheBuildIterator.class),
373 walk.getTree(2, WorkingTreeIterator.class));
374 if (walk.isSubtree())
375 walk.enterSubtree();
376 }
377 conflicts.removeAll(removed);
378 }
379
380
381
382
383
384
385
386
387
388
389 void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
390 WorkingTreeIterator f) throws IOException {
391 if (m != null) {
392 checkValidPath(m);
393
394
395 if (i == null) {
396
397 if (f != null && !FileMode.TREE.equals(f.getEntryFileMode())
398 && !f.isEntryIgnored()) {
399 if (failOnConflict) {
400
401 conflicts.add(walk.getPathString());
402 } else {
403
404
405
406 update(m.getEntryPathString(), m.getEntryObjectId(),
407 m.getEntryFileMode());
408 }
409 } else
410 update(m.getEntryPathString(), m.getEntryObjectId(),
411 m.getEntryFileMode());
412 } else if (f == null || !m.idEqual(i)) {
413
414
415 update(m.getEntryPathString(), m.getEntryObjectId(),
416 m.getEntryFileMode());
417 } else if (i.getDirCacheEntry() != null) {
418
419 if (f.isModified(i.getDirCacheEntry(), true,
420 this.walk.getObjectReader())
421 || i.getDirCacheEntry().getStage() != 0)
422
423
424 update(m.getEntryPathString(), m.getEntryObjectId(),
425 m.getEntryFileMode());
426 else {
427
428
429 DirCacheEntry entry = i.getDirCacheEntry();
430 if (entry.getLastModified() == 0)
431 entry.setLastModified(f.getEntryLastModified());
432 keep(entry, f);
433 }
434 } else
435
436 keep(i.getDirCacheEntry(), f);
437 } else {
438
439
440 if (f != null) {
441
442 if (walk.isDirectoryFileConflict()) {
443
444
445
446 conflicts.add(walk.getPathString());
447 } else {
448
449
450 if (i != null) {
451
452
453
454 remove(i.getEntryPathString());
455 conflicts.remove(i.getEntryPathString());
456 } else {
457
458
459 }
460 }
461 } else {
462
463
464
465
466 }
467 }
468 }
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484 public boolean checkout() throws IOException {
485 try {
486 return doCheckout();
487 } catch (CanceledException ce) {
488
489
490 throw new IOException(ce);
491 } finally {
492 try {
493 dc.unlock();
494 } finally {
495 if (performingCheckout) {
496 WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
497 getUpdated().keySet(), getRemoved());
498 if (!event.isEmpty()) {
499 repo.fireEvent(event);
500 }
501 }
502 }
503 }
504 }
505
506 private boolean doCheckout() throws CorruptObjectException, IOException,
507 MissingObjectException, IncorrectObjectTypeException,
508 CheckoutConflictException, IndexWriteException, CanceledException {
509 toBeDeleted.clear();
510 try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
511 if (headCommitTree != null)
512 preScanTwoTrees();
513 else
514 prescanOneTree();
515
516 if (!conflicts.isEmpty()) {
517 if (failOnConflict)
518 throw new CheckoutConflictException(conflicts.toArray(new String[0]));
519 else
520 cleanUpConflicts();
521 }
522
523
524 builder.finish();
525
526
527 int numTotal = removed.size() + updated.size() + conflicts.size();
528 monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);
529
530 performingCheckout = true;
531 File file = null;
532 String last = null;
533
534
535
536 IntList nonDeleted = new IntList();
537 for (int i = removed.size() - 1; i >= 0; i--) {
538 String r = removed.get(i);
539 file = new File(repo.getWorkTree(), r);
540 if (!file.delete() && repo.getFS().exists(file)) {
541
542
543
544
545
546 if (!repo.getFS().isDirectory(file)) {
547 nonDeleted.add(i);
548 toBeDeleted.add(r);
549 }
550 } else {
551 if (last != null && !isSamePrefix(r, last))
552 removeEmptyParents(new File(repo.getWorkTree(), last));
553 last = r;
554 }
555 monitor.update(1);
556 if (monitor.isCancelled()) {
557 throw new CanceledException(MessageFormat.format(
558 JGitText.get().operationCanceled,
559 JGitText.get().checkingOutFiles));
560 }
561 }
562 if (file != null) {
563 removeEmptyParents(file);
564 }
565 removed = filterOut(removed, nonDeleted);
566 nonDeleted = null;
567 Iterator<Map.Entry<String, CheckoutMetadata>> toUpdate = updated
568 .entrySet().iterator();
569 Map.Entry<String, CheckoutMetadata> e = null;
570 try {
571 while (toUpdate.hasNext()) {
572 e = toUpdate.next();
573 String path = e.getKey();
574 CheckoutMetadata meta = e.getValue();
575 DirCacheEntry entry = dc.getEntry(path);
576 if (FileMode.GITLINK.equals(entry.getRawMode())) {
577 checkoutGitlink(path, entry);
578 } else {
579 checkoutEntry(repo, entry, objectReader, false, meta);
580 }
581 e = null;
582
583 monitor.update(1);
584 if (monitor.isCancelled()) {
585 throw new CanceledException(MessageFormat.format(
586 JGitText.get().operationCanceled,
587 JGitText.get().checkingOutFiles));
588 }
589 }
590 } catch (Exception ex) {
591
592
593 if (e != null) {
594 toUpdate.remove();
595 }
596 while (toUpdate.hasNext()) {
597 e = toUpdate.next();
598 toUpdate.remove();
599 }
600 throw ex;
601 }
602 for (String conflict : conflicts) {
603
604
605
606 int entryIdx = dc.findEntry(conflict);
607 if (entryIdx >= 0) {
608 while (entryIdx < dc.getEntryCount()) {
609 DirCacheEntry entry = dc.getEntry(entryIdx);
610 if (!entry.getPathString().equals(conflict)) {
611 break;
612 }
613 if (entry.getStage() == DirCacheEntry.STAGE_3) {
614 checkoutEntry(repo, entry, objectReader, false,
615 null);
616 break;
617 }
618 ++entryIdx;
619 }
620 }
621
622 monitor.update(1);
623 if (monitor.isCancelled()) {
624 throw new CanceledException(MessageFormat.format(
625 JGitText.get().operationCanceled,
626 JGitText.get().checkingOutFiles));
627 }
628 }
629 monitor.endTask();
630
631
632 if (!builder.commit())
633 throw new IndexWriteException();
634 }
635 return toBeDeleted.isEmpty();
636 }
637
638 private void checkoutGitlink(String path, DirCacheEntry entry)
639 throws IOException {
640 File gitlinkDir = new File(repo.getWorkTree(), path);
641 FileUtils.mkdirs(gitlinkDir, true);
642 FS fs = repo.getFS();
643 entry.setLastModified(fs.lastModified(gitlinkDir));
644 }
645
646 private static ArrayList<String> filterOut(ArrayList<String> strings,
647 IntList indicesToRemove) {
648 int n = indicesToRemove.size();
649 if (n == strings.size()) {
650 return new ArrayList<>(0);
651 }
652 switch (n) {
653 case 0:
654 return strings;
655 case 1:
656 strings.remove(indicesToRemove.get(0));
657 return strings;
658 default:
659 int length = strings.size();
660 ArrayList<String> result = new ArrayList<>(length - n);
661
662
663 int j = n - 1;
664 int idx = indicesToRemove.get(j);
665 for (int i = 0; i < length; i++) {
666 if (i == idx) {
667 idx = (--j >= 0) ? indicesToRemove.get(j) : -1;
668 } else {
669 result.add(strings.get(i));
670 }
671 }
672 return result;
673 }
674 }
675
676 private static boolean isSamePrefix(String a, String b) {
677 int as = a.lastIndexOf('/');
678 int bs = b.lastIndexOf('/');
679 return a.substring(0, as + 1).equals(b.substring(0, bs + 1));
680 }
681
682 private void removeEmptyParents(File f) {
683 File parentFile = f.getParentFile();
684
685 while (parentFile != null && !parentFile.equals(repo.getWorkTree())) {
686 if (!parentFile.delete())
687 break;
688 parentFile = parentFile.getParentFile();
689 }
690 }
691
692
693
694
695
696
697
698
699
700
701
702 private boolean equalIdAndMode(ObjectIdrg/eclipse/jgit/lib/ObjectId.html#ObjectId">ObjectId id1, FileMode mode1, ObjectId id2,
703 FileMode mode2) {
704 if (!mode1.equals(mode2))
705 return false;
706 return id1 != null ? id1.equals(id2) : id2 == null;
707 }
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726 void processEntry(CanonicalTreeParser../../org/eclipse/jgit/treewalk/CanonicalTreeParser.html#CanonicalTreeParser">CanonicalTreeParser h, CanonicalTreeParser m,
727 DirCacheBuildIterator i, WorkingTreeIterator f) throws IOException {
728 DirCacheEntry dce = i != null ? i.getDirCacheEntry() : null;
729
730 String name = walk.getPathString();
731
732 if (m != null)
733 checkValidPath(m);
734
735 if (i == null && m == null && h == null) {
736
737 if (walk.isDirectoryFileConflict())
738
739 conflict(name, null, null, null);
740
741
742 return;
743 }
744
745 ObjectId iId = (i == null ? null : i.getEntryObjectId());
746 ObjectId mId = (m == null ? null : m.getEntryObjectId());
747 ObjectId hId = (h == null ? null : h.getEntryObjectId());
748 FileMode iMode = (i == null ? null : i.getEntryFileMode());
749 FileMode mMode = (m == null ? null : m.getEntryFileMode());
750 FileMode hMode = (h == null ? null : h.getEntryFileMode());
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799 int ffMask = 0;
800 if (h != null)
801 ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
802 if (i != null)
803 ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
804 if (m != null)
805 ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;
806
807
808
809 if (((ffMask & 0x222) != 0x000)
810 && (((ffMask & 0x00F) == 0x00D) || ((ffMask & 0x0F0) == 0x0D0) || ((ffMask & 0xF00) == 0xD00))) {
811
812
813
814
815
816 switch (ffMask) {
817 case 0xDDF:
818 if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
819 conflict(name, dce, h, m);
820 } else {
821 update(name, mId, mMode);
822 }
823
824 break;
825 case 0xDFD:
826 keep(dce, f);
827 break;
828 case 0xF0D:
829 remove(name);
830 break;
831 case 0xDFF:
832 if (equalIdAndMode(iId, iMode, mId, mMode))
833 keep(dce, f);
834 else
835 conflict(name, dce, h, m);
836 break;
837 case 0xFDD:
838
839
840
841
842
843
844
845 break;
846 case 0xD0F:
847 update(name, mId, mMode);
848 break;
849 case 0xDF0:
850 case 0x0FD:
851 conflict(name, dce, h, m);
852 break;
853 case 0xFDF:
854 if (equalIdAndMode(hId, hMode, mId, mMode)) {
855 if (isModifiedSubtree_IndexWorkingtree(name))
856 conflict(name, dce, h, m);
857 else
858 update(name, mId, mMode);
859 } else
860 conflict(name, dce, h, m);
861 break;
862 case 0xFD0:
863 keep(dce, f);
864 break;
865 case 0xFFD:
866 if (equalIdAndMode(hId, hMode, iId, iMode))
867 if (f != null
868 && f.isModified(dce, true,
869 this.walk.getObjectReader()))
870 conflict(name, dce, h, m);
871 else
872 remove(name);
873 else
874 conflict(name, dce, h, m);
875 break;
876 case 0x0DF:
877 if (!isModifiedSubtree_IndexWorkingtree(name))
878 update(name, mId, mMode);
879 else
880 conflict(name, dce, h, m);
881 break;
882 default:
883 keep(dce, f);
884 }
885 return;
886 }
887
888 if ((ffMask & 0x222) == 0) {
889
890
891 if (f == null || FileMode.TREE.equals(f.getEntryFileMode())) {
892
893
894 return;
895 } else {
896
897 if (!idEqual(h, m)) {
898
899
900 conflict(name, null, null, null);
901 }
902 return;
903 }
904 }
905
906 if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
907
908 conflict(name, null, h, m);
909 return;
910 }
911
912 if (i == null) {
913
914
915
916 if (f != null && !f.isEntryIgnored()) {
917
918 if (!FileMode.GITLINK.equals(mMode)) {
919
920
921 if (mId == null
922 || !equalIdAndMode(mId, mMode,
923 f.getEntryObjectId(), f.getEntryFileMode())) {
924 conflict(name, null, h, m);
925 return;
926 }
927 }
928 }
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943 if (h == null)
944
945
946
947
948
949 update(name, mId, mMode);
950 else if (m == null)
951
952
953
954
955
956 remove(name);
957 else {
958
959
960
961
962
963
964
965 if (equalIdAndMode(hId, hMode, mId, mMode)) {
966 if (initialCheckout)
967 update(name, mId, mMode);
968 else
969 keep(dce, f);
970 } else
971 conflict(name, dce, h, m);
972 }
973 } else {
974
975 if (h == null) {
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992 if (m == null
993 || !isModified_IndexTree(name, iId, iMode, mId, mMode,
994 mergeCommitTree)) {
995
996
997
998 if (m==null && walk.isDirectoryFileConflict()) {
999
1000
1001
1002
1003 if (dce != null
1004 && (f == null || f.isModified(dce, true,
1005 this.walk.getObjectReader())))
1006
1007
1008
1009
1010
1011
1012
1013
1014 conflict(name, dce, h, m);
1015 else
1016
1017
1018
1019
1020
1021
1022
1023
1024 remove(name);
1025 } else
1026
1027
1028
1029
1030
1031
1032 keep(dce, f);
1033 } else
1034
1035
1036
1037
1038
1039 conflict(name, dce, h, m);
1040 } else if (m == null) {
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056 if (iMode == FileMode.GITLINK) {
1057
1058
1059
1060
1061
1062 remove(name);
1063 } else {
1064
1065
1066
1067 if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
1068 headCommitTree)) {
1069
1070
1071
1072
1073 if (f != null
1074 && f.isModified(dce, true,
1075 this.walk.getObjectReader())) {
1076
1077
1078
1079
1080
1081
1082 if (!FileMode.TREE.equals(f.getEntryFileMode())
1083 && FileMode.TREE.equals(iMode))
1084
1085
1086 return;
1087 else
1088
1089
1090 conflict(name, dce, h, m);
1091 } else
1092
1093
1094
1095
1096
1097
1098 remove(name);
1099 } else
1100
1101
1102
1103
1104
1105
1106
1107 conflict(name, dce, h, m);
1108 }
1109 } else {
1110
1111
1112
1113 if (!equalIdAndMode(hId, hMode, mId, mMode)
1114 && isModified_IndexTree(name, iId, iMode, hId, hMode,
1115 headCommitTree)
1116 && isModified_IndexTree(name, iId, iMode, mId, mMode,
1117 mergeCommitTree))
1118
1119
1120
1121 conflict(name, dce, h, m);
1122 else
1123
1124
1125
1126
1127
1128
1129 if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
1130 headCommitTree)
1131 && isModified_IndexTree(name, iId, iMode, mId, mMode,
1132 mergeCommitTree)) {
1133
1134
1135
1136
1137 if (dce != null
1138 && FileMode.GITLINK.equals(dce.getFileMode())) {
1139
1140
1141
1142
1143
1144
1145
1146
1147 update(name, mId, mMode);
1148 } else if (dce != null
1149 && (f != null && f.isModified(dce, true,
1150 this.walk.getObjectReader()))) {
1151
1152
1153
1154
1155
1156
1157 conflict(name, dce, h, m);
1158 } else {
1159
1160
1161
1162
1163
1164
1165
1166 update(name, mId, mMode);
1167 }
1168 } else {
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181 keep(dce, f);
1182 }
1183 }
1184 }
1185 }
1186
1187 private static boolean idEqual(AbstractTreeIterator a,
1188 AbstractTreeIterator b) {
1189 if (a == b) {
1190 return true;
1191 }
1192 if (a == null || b == null) {
1193 return false;
1194 }
1195 return a.getEntryObjectId().equals(b.getEntryObjectId());
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205 private void conflict(String path, DirCacheEntry e, AbstractTreeIterator./../org/eclipse/jgit/treewalk/AbstractTreeIterator.html#AbstractTreeIterator">AbstractTreeIterator h, AbstractTreeIterator m) {
1206 conflicts.add(path);
1207
1208 DirCacheEntry entry;
1209 if (e != null) {
1210 entry = new DirCacheEntry(e.getPathString(), DirCacheEntry.STAGE_1);
1211 entry.copyMetaData(e, true);
1212 builder.add(entry);
1213 }
1214
1215 if (h != null && !FileMode.TREE.equals(h.getEntryFileMode())) {
1216 entry = new DirCacheEntry(h.getEntryPathString(), DirCacheEntry.STAGE_2);
1217 entry.setFileMode(h.getEntryFileMode());
1218 entry.setObjectId(h.getEntryObjectId());
1219 builder.add(entry);
1220 }
1221
1222 if (m != null && !FileMode.TREE.equals(m.getEntryFileMode())) {
1223 entry = new DirCacheEntry(m.getEntryPathString(), DirCacheEntry.STAGE_3);
1224 entry.setFileMode(m.getEntryFileMode());
1225 entry.setObjectId(m.getEntryObjectId());
1226 builder.add(entry);
1227 }
1228 }
1229
1230 private void keep(DirCacheEntry e, WorkingTreeIterator f)
1231 throws IOException {
1232 if (e != null && !FileMode.TREE.equals(e.getFileMode()))
1233 builder.add(e);
1234 if (force) {
1235 if (f.isModified(e, true, this.walk.getObjectReader())) {
1236 checkoutEntry(repo, e, this.walk.getObjectReader());
1237 }
1238 }
1239 }
1240
1241 private void remove(String path) {
1242 removed.add(path);
1243 }
1244
1245 private void update(String path, ObjectId mId, FileMode mode)
1246 throws IOException {
1247 if (!FileMode.TREE.equals(mode)) {
1248 updated.put(path, new CheckoutMetadata(
1249 walk.getEolStreamType(CHECKOUT_OP),
1250 walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)));
1251
1252 DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
1253 entry.setObjectId(mId);
1254 entry.setFileMode(mode);
1255 builder.add(entry);
1256 }
1257 }
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268 public void setFailOnConflict(boolean failOnConflict) {
1269 this.failOnConflict = failOnConflict;
1270 }
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282 public void setForce(boolean force) {
1283 this.force = force;
1284 }
1285
1286
1287
1288
1289
1290
1291
1292 private void cleanUpConflicts() throws CheckoutConflictException {
1293
1294 for (String c : conflicts) {
1295 File conflict = new File(repo.getWorkTree(), c);
1296 if (!conflict.delete())
1297 throw new CheckoutConflictException(MessageFormat.format(
1298 JGitText.get().cannotDeleteFile, c));
1299 removeEmptyParents(conflict);
1300 }
1301 for (String r : removed) {
1302 File file = new File(repo.getWorkTree(), r);
1303 if (!file.delete())
1304 throw new CheckoutConflictException(
1305 MessageFormat.format(JGitText.get().cannotDeleteFile,
1306 file.getAbsolutePath()));
1307 removeEmptyParents(file);
1308 }
1309 }
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320 private boolean isModifiedSubtree_IndexWorkingtree(String path)
1321 throws CorruptObjectException, IOException {
1322 try (NameConflictTreeWalkTreeWalk.html#NameConflictTreeWalk">NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1323 int dciPos = tw.addTree(new DirCacheIterator(dc));
1324 FileTreeIterator fti = new FileTreeIterator(repo);
1325 tw.addTree(fti);
1326 fti.setDirCacheIterator(tw, dciPos);
1327 tw.setRecursive(true);
1328 tw.setFilter(PathFilter.create(path));
1329 DirCacheIterator dcIt;
1330 WorkingTreeIterator wtIt;
1331 while (tw.next()) {
1332 dcIt = tw.getTree(0, DirCacheIterator.class);
1333 wtIt = tw.getTree(1, WorkingTreeIterator.class);
1334 if (dcIt == null || wtIt == null)
1335 return true;
1336 if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
1337 this.walk.getObjectReader())) {
1338 return true;
1339 }
1340 }
1341 return false;
1342 }
1343 }
1344
1345 private boolean isModified_IndexTree(String path, ObjectId iId,
1346 FileMode iMode, ObjectIdrg/eclipse/jgit/lib/ObjectId.html#ObjectId">ObjectId tId, FileMode tMode, ObjectId rootTree)
1347 throws CorruptObjectException, IOException {
1348 if (iMode != tMode)
1349 return true;
1350 if (FileMode.TREE.equals(iMode)
1351 && (iId == null || ObjectId.zeroId().equals(iId)))
1352 return isModifiedSubtree_IndexTree(path, rootTree);
1353 else
1354 return !equalIdAndMode(iId, iMode, tId, tMode);
1355 }
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
1369 throws CorruptObjectException, IOException {
1370 try (NameConflictTreeWalkTreeWalk.html#NameConflictTreeWalk">NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1371 tw.addTree(new DirCacheIterator(dc));
1372 tw.addTree(tree);
1373 tw.setRecursive(true);
1374 tw.setFilter(PathFilter.create(path));
1375 while (tw.next()) {
1376 AbstractTreeIterator dcIt = tw.getTree(0,
1377 DirCacheIterator.class);
1378 AbstractTreeIterator treeIt = tw.getTree(1,
1379 AbstractTreeIterator.class);
1380 if (dcIt == null || treeIt == null)
1381 return true;
1382 if (dcIt.getEntryRawMode() != treeIt.getEntryRawMode())
1383 return true;
1384 if (!dcIt.getEntryObjectId().equals(treeIt.getEntryObjectId()))
1385 return true;
1386 }
1387 return false;
1388 }
1389 }
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422 @Deprecated
1423 public static void checkoutEntry(Repository repo, DirCacheEntry entry,
1424 ObjectReader or) throws IOException {
1425 checkoutEntry(repo, entry, or, false, null);
1426 }
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465 public static void checkoutEntry(Repository repo, DirCacheEntry entry,
1466 ObjectReader or, boolean deleteRecursive,
1467 CheckoutMetadata checkoutMetadata) throws IOException {
1468 if (checkoutMetadata == null)
1469 checkoutMetadata = CheckoutMetadata.EMPTY;
1470 ObjectLoader ol = or.open(entry.getObjectId());
1471 File f = new File(repo.getWorkTree(), entry.getPathString());
1472 File parentDir = f.getParentFile();
1473 FileUtils.mkdirs(parentDir, true);
1474 FS fs = repo.getFS();
1475 WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
1476 if (entry.getFileMode() == FileMode.SYMLINK
1477 && opt.getSymLinks() == SymLinks.TRUE) {
1478 byte[] bytes = ol.getBytes();
1479 String target = RawParseUtils.decode(bytes);
1480 if (deleteRecursive && f.isDirectory()) {
1481 FileUtils.delete(f, FileUtils.RECURSIVE);
1482 }
1483 fs.createSymLink(f, target);
1484 entry.setLength(bytes.length);
1485 entry.setLastModified(fs.lastModified(f));
1486 return;
1487 }
1488
1489 String name = f.getName();
1490 if (name.length() > 200) {
1491 name = name.substring(0, 200);
1492 }
1493 File tmpFile = File.createTempFile(
1494 "._" + name, null, parentDir);
1495
1496 EolStreamType nonNullEolStreamType;
1497 if (checkoutMetadata.eolStreamType != null) {
1498 nonNullEolStreamType = checkoutMetadata.eolStreamType;
1499 } else if (opt.getAutoCRLF() == AutoCRLF.TRUE) {
1500 nonNullEolStreamType = EolStreamType.AUTO_CRLF;
1501 } else {
1502 nonNullEolStreamType = EolStreamType.DIRECT;
1503 }
1504 try (OutputStream channel = EolStreamTypeUtil.wrapOutputStream(
1505 new FileOutputStream(tmpFile), nonNullEolStreamType)) {
1506 if (checkoutMetadata.smudgeFilterCommand != null) {
1507 if (FilterCommandRegistry
1508 .isRegistered(checkoutMetadata.smudgeFilterCommand)) {
1509 runBuiltinFilterCommand(repo, checkoutMetadata, ol,
1510 channel);
1511 } else {
1512 runExternalFilterCommand(repo, entry, checkoutMetadata, ol,
1513 fs, channel);
1514 }
1515 } else {
1516 ol.copyTo(channel);
1517 }
1518 }
1519
1520
1521
1522
1523 if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT
1524 && checkoutMetadata.smudgeFilterCommand == null) {
1525 entry.setLength(ol.getSize());
1526 } else {
1527 entry.setLength(tmpFile.length());
1528 }
1529
1530 if (opt.isFileMode() && fs.supportsExecute()) {
1531 if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
1532 if (!fs.canExecute(tmpFile))
1533 fs.setExecute(tmpFile, true);
1534 } else {
1535 if (fs.canExecute(tmpFile))
1536 fs.setExecute(tmpFile, false);
1537 }
1538 }
1539 try {
1540 if (deleteRecursive && f.isDirectory()) {
1541 FileUtils.delete(f, FileUtils.RECURSIVE);
1542 }
1543 FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
1544 } catch (IOException e) {
1545 throw new IOException(
1546 MessageFormat.format(JGitText.get().renameFileFailed,
1547 tmpFile.getPath(), f.getPath()),
1548 e);
1549 } finally {
1550 if (tmpFile.exists()) {
1551 FileUtils.delete(tmpFile);
1552 }
1553 }
1554 entry.setLastModified(fs.lastModified(f));
1555 }
1556
1557
1558 private static void runExternalFilterCommand(Repository repo,
1559 DirCacheEntry entry,
1560 CheckoutMetadata checkoutMetadata, ObjectLoader ol, FS fs,
1561 OutputStream channel) throws IOException {
1562 ProcessBuilder filterProcessBuilder = fs.runInShell(
1563 checkoutMetadata.smudgeFilterCommand, new String[0]);
1564 filterProcessBuilder.directory(repo.getWorkTree());
1565 filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
1566 repo.getDirectory().getAbsolutePath());
1567 ExecutionResult result;
1568 int rc;
1569 try {
1570
1571 result = fs.execute(filterProcessBuilder, ol.openStream());
1572 rc = result.getRc();
1573 if (rc == 0) {
1574 result.getStdout().writeTo(channel,
1575 NullProgressMonitor.INSTANCE);
1576 }
1577 } catch (IOException | InterruptedException e) {
1578 throw new IOException(new FilterFailedException(e,
1579 checkoutMetadata.smudgeFilterCommand,
1580 entry.getPathString()));
1581 }
1582 if (rc != 0) {
1583 throw new IOException(new FilterFailedException(rc,
1584 checkoutMetadata.smudgeFilterCommand,
1585 entry.getPathString(),
1586 result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
1587 RawParseUtils.decode(result.getStderr()
1588 .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
1589 }
1590 }
1591
1592
1593 private static void runBuiltinFilterCommand(Repository repo,
1594 CheckoutMetadata checkoutMetadata, ObjectLoader ol,
1595 OutputStream channel) throws MissingObjectException, IOException {
1596 boolean isMandatory = repo.getConfig().getBoolean(
1597 ConfigConstants.CONFIG_FILTER_SECTION,
1598 ConfigConstants.CONFIG_SECTION_LFS,
1599 ConfigConstants.CONFIG_KEY_REQUIRED, false);
1600 FilterCommand command = null;
1601 try {
1602 command = FilterCommandRegistry.createFilterCommand(
1603 checkoutMetadata.smudgeFilterCommand, repo, ol.openStream(),
1604 channel);
1605 } catch (IOException e) {
1606 LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
1607 if (!isMandatory) {
1608
1609
1610
1611 ol.copyTo(channel);
1612 } else {
1613 throw e;
1614 }
1615 }
1616 if (command != null) {
1617 while (command.run() != -1) {
1618
1619 }
1620 }
1621 }
1622
1623 @SuppressWarnings("deprecation")
1624 private static void checkValidPath(CanonicalTreeParser t)
1625 throws InvalidPathException {
1626 ObjectChecker chk = new ObjectChecker()
1627 .setSafeForWindows(SystemReader.getInstance().isWindows())
1628 .setSafeForMacOS(SystemReader.getInstance().isMacOS());
1629 for (CanonicalTreeParser i = t; i != null; i = i.getParent())
1630 checkValidPathSegment(chk, i);
1631 }
1632
1633 private static void checkValidPathSegment(ObjectChecker chk,
1634 CanonicalTreeParser t) throws InvalidPathException {
1635 try {
1636 int ptr = t.getNameOffset();
1637 int end = ptr + t.getNameLength();
1638 chk.checkPathSegment(t.getEntryPathBuffer(), ptr, end);
1639 } catch (CorruptObjectException err) {
1640 String path = t.getEntryPathString();
1641 InvalidPathException i = new InvalidPathException(path);
1642 i.initCause(err);
1643 throw i;
1644 }
1645 }
1646 }