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