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