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