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