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