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
44
45
46
47 package org.eclipse.jgit.merge;
48
49 import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
50 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
51 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
52 import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
53 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
54
55 import java.io.BufferedOutputStream;
56 import java.io.File;
57 import java.io.FileNotFoundException;
58 import java.io.FileOutputStream;
59 import java.io.IOException;
60 import java.io.InputStream;
61 import java.io.OutputStream;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collections;
65 import java.util.HashMap;
66 import java.util.Iterator;
67 import java.util.LinkedList;
68 import java.util.List;
69 import java.util.Map;
70
71 import org.eclipse.jgit.attributes.Attributes;
72 import org.eclipse.jgit.diff.DiffAlgorithm;
73 import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
74 import org.eclipse.jgit.diff.RawText;
75 import org.eclipse.jgit.diff.RawTextComparator;
76 import org.eclipse.jgit.diff.Sequence;
77 import org.eclipse.jgit.dircache.DirCache;
78 import org.eclipse.jgit.dircache.DirCacheBuildIterator;
79 import org.eclipse.jgit.dircache.DirCacheBuilder;
80 import org.eclipse.jgit.dircache.DirCacheCheckout;
81 import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
82 import org.eclipse.jgit.dircache.DirCacheEntry;
83 import org.eclipse.jgit.errors.BinaryBlobException;
84 import org.eclipse.jgit.errors.CorruptObjectException;
85 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
86 import org.eclipse.jgit.errors.IndexWriteException;
87 import org.eclipse.jgit.errors.MissingObjectException;
88 import org.eclipse.jgit.errors.NoWorkTreeException;
89 import org.eclipse.jgit.lib.Config;
90 import org.eclipse.jgit.lib.ConfigConstants;
91 import org.eclipse.jgit.lib.Constants;
92 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
93 import org.eclipse.jgit.lib.FileMode;
94 import org.eclipse.jgit.lib.ObjectId;
95 import org.eclipse.jgit.lib.ObjectInserter;
96 import org.eclipse.jgit.lib.ObjectLoader;
97 import org.eclipse.jgit.lib.Repository;
98 import org.eclipse.jgit.revwalk.RevTree;
99 import org.eclipse.jgit.storage.pack.PackConfig;
100 import org.eclipse.jgit.submodule.SubmoduleConflict;
101 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
102 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
103 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
104 import org.eclipse.jgit.treewalk.TreeWalk;
105 import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
106 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
107 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
108 import org.eclipse.jgit.treewalk.filter.TreeFilter;
109 import org.eclipse.jgit.util.FS;
110 import org.eclipse.jgit.util.LfsFactory;
111 import org.eclipse.jgit.util.LfsFactory.LfsInputStream;
112 import org.eclipse.jgit.util.TemporaryBuffer;
113 import org.eclipse.jgit.util.io.EolStreamTypeUtil;
114
115
116
117
118 public class ResolveMerger extends ThreeWayMerger {
119
120
121
122
123 public enum MergeFailureReason {
124
125 DIRTY_INDEX,
126
127 DIRTY_WORKTREE,
128
129 COULD_NOT_DELETE
130 }
131
132
133
134
135
136
137 protected NameConflictTreeWalk tw;
138
139
140
141
142
143
144 protected String commitNames[];
145
146
147
148
149
150
151 protected static final int T_BASE = 0;
152
153
154
155
156
157
158 protected static final int T_OURS = 1;
159
160
161
162
163
164
165 protected static final int T_THEIRS = 2;
166
167
168
169
170
171
172 protected static final int T_INDEX = 3;
173
174
175
176
177
178
179 protected static final int T_FILE = 4;
180
181
182
183
184
185
186 protected DirCacheBuilder builder;
187
188
189
190
191
192
193 protected ObjectId resultTree;
194
195
196
197
198
199
200
201 protected List<String> unmergedPaths = new ArrayList<>();
202
203
204
205
206
207
208 protected List<String> modifiedFiles = new LinkedList<>();
209
210
211
212
213
214
215
216 protected Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();
217
218
219
220
221
222
223
224 protected List<String> toBeDeleted = new ArrayList<>();
225
226
227
228
229
230
231
232 protected Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<>();
233
234
235
236
237
238
239 protected Map<String, MergeFailureReason> failingPaths = new HashMap<>();
240
241
242
243
244
245
246
247 protected boolean enterSubtree;
248
249
250
251
252
253
254
255
256
257 protected boolean inCore;
258
259
260
261
262
263
264
265
266 protected boolean implicitDirCache;
267
268
269
270
271
272 protected DirCache dircache;
273
274
275
276
277
278
279 protected WorkingTreeIterator workingTreeIterator;
280
281
282
283
284
285 protected MergeAlgorithm mergeAlgorithm;
286
287
288
289
290
291
292
293 protected WorkingTreeOptions workingTreeOptions;
294
295
296
297
298
299 private int inCoreLimit;
300
301
302
303
304
305 private Map<String, CheckoutMetadata> checkoutMetadata;
306
307 private static MergeAlgorithm getMergeAlgorithm(Config config) {
308 SupportedAlgorithm diffAlg = config.getEnum(
309 CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
310 HISTOGRAM);
311 return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
312 }
313
314 private static int getInCoreLimit(Config config) {
315 return config.getInt(
316 ConfigConstants.CONFIG_MERGE_SECTION, ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
317 }
318
319 private static String[] defaultCommitNames() {
320 return new String[] { "BASE", "OURS", "THEIRS" };
321 }
322
323 private static final Attributes NO_ATTRIBUTES = new Attributes();
324
325
326
327
328
329
330
331
332
333 protected ResolveMerger(Repository local, boolean inCore) {
334 super(local);
335 Config config = local.getConfig();
336 mergeAlgorithm = getMergeAlgorithm(config);
337 inCoreLimit = getInCoreLimit(config);
338 commitNames = defaultCommitNames();
339 this.inCore = inCore;
340
341 if (inCore) {
342 implicitDirCache = false;
343 dircache = DirCache.newInCore();
344 } else {
345 implicitDirCache = true;
346 workingTreeOptions = local.getConfig().get(WorkingTreeOptions.KEY);
347 }
348 }
349
350
351
352
353
354
355
356 protected ResolveMerger(Repository local) {
357 this(local, false);
358 }
359
360
361
362
363
364
365
366
367
368
369 protected ResolveMerger(ObjectInserter inserter, Config config) {
370 super(inserter);
371 mergeAlgorithm = getMergeAlgorithm(config);
372 commitNames = defaultCommitNames();
373 inCore = true;
374 implicitDirCache = false;
375 dircache = DirCache.newInCore();
376 }
377
378
379 @Override
380 protected boolean mergeImpl() throws IOException {
381 if (implicitDirCache) {
382 dircache = nonNullRepo().lockDirCache();
383 }
384 if (!inCore) {
385 checkoutMetadata = new HashMap<>();
386 }
387 try {
388 return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
389 false);
390 } finally {
391 checkoutMetadata = null;
392 if (implicitDirCache) {
393 dircache.unlock();
394 }
395 }
396 }
397
398 private void checkout() throws NoWorkTreeException, IOException {
399
400
401
402 for (int i = toBeDeleted.size() - 1; i >= 0; i--) {
403 String fileName = toBeDeleted.get(i);
404 File f = new File(nonNullRepo().getWorkTree(), fileName);
405 if (!f.delete())
406 if (!f.isDirectory())
407 failingPaths.put(fileName,
408 MergeFailureReason.COULD_NOT_DELETE);
409 modifiedFiles.add(fileName);
410 }
411 for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
412 .entrySet()) {
413 DirCacheEntry cacheEntry = entry.getValue();
414 if (cacheEntry.getFileMode() == FileMode.GITLINK) {
415 new File(nonNullRepo().getWorkTree(), entry.getKey()).mkdirs();
416 } else {
417 DirCacheCheckout.checkoutEntry(db, cacheEntry, reader, false,
418 checkoutMetadata.get(entry.getKey()));
419 modifiedFiles.add(entry.getKey());
420 }
421 }
422 }
423
424
425
426
427
428
429
430
431
432
433
434
435 protected void cleanUp() throws NoWorkTreeException,
436 CorruptObjectException,
437 IOException {
438 if (inCore) {
439 modifiedFiles.clear();
440 return;
441 }
442
443 DirCache dc = nonNullRepo().readDirCache();
444 Iterator<String> mpathsIt=modifiedFiles.iterator();
445 while(mpathsIt.hasNext()) {
446 String mpath = mpathsIt.next();
447 DirCacheEntry entry = dc.getEntry(mpath);
448 if (entry != null) {
449 DirCacheCheckout.checkoutEntry(db, entry, reader, false,
450 checkoutMetadata.get(mpath));
451 }
452 mpathsIt.remove();
453 }
454 }
455
456
457
458
459
460
461
462
463
464
465
466 private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage,
467 long lastMod, long len) {
468 if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
469 DirCacheEntry e = new DirCacheEntry(path, stage);
470 e.setFileMode(p.getEntryFileMode());
471 e.setObjectId(p.getEntryObjectId());
472 e.setLastModified(lastMod);
473 e.setLength(len);
474 builder.add(e);
475 return e;
476 }
477 return null;
478 }
479
480
481
482
483
484
485
486
487
488
489 private DirCacheEntry keep(DirCacheEntry e) {
490 DirCacheEntry newEntry = new DirCacheEntry(e.getRawPath(),
491 e.getStage());
492 newEntry.setFileMode(e.getFileMode());
493 newEntry.setObjectId(e.getObjectId());
494 newEntry.setLastModified(e.getLastModified());
495 newEntry.setLength(e.getLength());
496 builder.add(newEntry);
497 return newEntry;
498 }
499
500
501
502
503
504
505
506
507
508
509
510
511
512 protected void addCheckoutMetadata(String path, Attributes attributes)
513 throws IOException {
514 if (checkoutMetadata != null) {
515 EolStreamType eol = EolStreamTypeUtil.detectStreamType(
516 OperationType.CHECKOUT_OP, workingTreeOptions, attributes);
517 CheckoutMetadata data = new CheckoutMetadata(eol,
518 tw.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE));
519 checkoutMetadata.put(path, data);
520 }
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537 protected void addToCheckout(String path, DirCacheEntry entry,
538 Attributes attributes) throws IOException {
539 toBeCheckedOut.put(path, entry);
540 addCheckoutMetadata(path, attributes);
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 protected void addDeletion(String path, boolean isFile,
558 Attributes attributes) throws IOException {
559 toBeDeleted.add(path);
560 if (isFile) {
561 addCheckoutMetadata(path, attributes);
562 }
563 }
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
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 protected boolean processEntry(CanonicalTreeParser base,
613 CanonicalTreeParser ours, CanonicalTreeParser theirs,
614 DirCacheBuildIterator index, WorkingTreeIterator work,
615 boolean ignoreConflicts, Attributes attributes)
616 throws MissingObjectException, IncorrectObjectTypeException,
617 CorruptObjectException, IOException {
618 enterSubtree = true;
619 final int modeO = tw.getRawMode(T_OURS);
620 final int modeT = tw.getRawMode(T_THEIRS);
621 final int modeB = tw.getRawMode(T_BASE);
622
623 if (modeO == 0 && modeT == 0 && modeB == 0)
624
625 return true;
626
627 if (isIndexDirty())
628 return false;
629
630 DirCacheEntry ourDce = null;
631
632 if (index == null || index.getDirCacheEntry() == null) {
633
634
635 if (nonTree(modeO)) {
636 ourDce = new DirCacheEntry(tw.getRawPath());
637 ourDce.setObjectId(tw.getObjectId(T_OURS));
638 ourDce.setFileMode(tw.getFileMode(T_OURS));
639 }
640 } else {
641 ourDce = index.getDirCacheEntry();
642 }
643
644 if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) {
645
646 if (modeO == modeT) {
647
648
649
650 keep(ourDce);
651
652 return true;
653 } else {
654
655
656
657 int newMode = mergeFileModes(modeB, modeO, modeT);
658 if (newMode != FileMode.MISSING.getBits()) {
659 if (newMode == modeO)
660
661 keep(ourDce);
662 else {
663
664
665 if (isWorktreeDirty(work, ourDce))
666 return false;
667
668
669 DirCacheEntry e = add(tw.getRawPath(), theirs,
670 DirCacheEntry.STAGE_0, 0, 0);
671 addToCheckout(tw.getPathString(), e, attributes);
672 }
673 return true;
674 } else {
675
676
677 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
678 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
679 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
680 unmergedPaths.add(tw.getPathString());
681 mergeResults.put(
682 tw.getPathString(),
683 new MergeResult<>(Collections
684 .<RawText> emptyList()));
685 }
686 return true;
687 }
688 }
689
690 if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
691
692
693 if (ourDce != null)
694 keep(ourDce);
695
696 return true;
697 }
698
699 if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
700
701
702
703
704 if (isWorktreeDirty(work, ourDce))
705 return false;
706 if (nonTree(modeT)) {
707
708
709
710 DirCacheEntry e = add(tw.getRawPath(), theirs,
711 DirCacheEntry.STAGE_0, 0, 0);
712 if (e != null) {
713 addToCheckout(tw.getPathString(), e, attributes);
714 }
715 return true;
716 } else {
717
718
719
720 if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) {
721
722 return true;
723 }
724 if (modeT != 0 && modeT == modeB) {
725
726 return true;
727 }
728 addDeletion(tw.getPathString(), nonTree(modeO), attributes);
729 return true;
730 }
731 }
732
733 if (tw.isSubtree()) {
734
735
736
737
738 if (nonTree(modeO) && !nonTree(modeT)) {
739 if (nonTree(modeB))
740 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
741 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
742 unmergedPaths.add(tw.getPathString());
743 enterSubtree = false;
744 return true;
745 }
746 if (nonTree(modeT) && !nonTree(modeO)) {
747 if (nonTree(modeB))
748 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
749 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
750 unmergedPaths.add(tw.getPathString());
751 enterSubtree = false;
752 return true;
753 }
754
755
756
757
758
759 if (!nonTree(modeO))
760 return true;
761
762
763
764 }
765
766 if (nonTree(modeO) && nonTree(modeT)) {
767
768 boolean worktreeDirty = isWorktreeDirty(work, ourDce);
769 if (!attributes.canBeContentMerged() && worktreeDirty) {
770 return false;
771 }
772
773 boolean gitlinkConflict = isGitLink(modeO) || isGitLink(modeT);
774
775 if (gitlinkConflict || !attributes.canBeContentMerged()) {
776 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
777 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
778 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
779
780 if (gitlinkConflict) {
781 MergeResult<SubmoduleConflict> result = new MergeResult<>(
782 Arrays.asList(
783 new SubmoduleConflict(base == null ? null
784 : base.getEntryObjectId()),
785 new SubmoduleConflict(ours == null ? null
786 : ours.getEntryObjectId()),
787 new SubmoduleConflict(theirs == null ? null
788 : theirs.getEntryObjectId())));
789 result.setContainsConflicts(true);
790 mergeResults.put(tw.getPathString(), result);
791 if (!ignoreConflicts) {
792 unmergedPaths.add(tw.getPathString());
793 }
794 } else {
795
796 unmergedPaths.add(tw.getPathString());
797 }
798 return true;
799 }
800
801
802 if (worktreeDirty) {
803 return false;
804 }
805
806 MergeResult<RawText> result = contentMerge(base, ours, theirs,
807 attributes);
808 if (ignoreConflicts) {
809 result.setContainsConflicts(false);
810 }
811 updateIndex(base, ours, theirs, result, attributes);
812 String currentPath = tw.getPathString();
813 if (result.containsConflicts() && !ignoreConflicts) {
814 unmergedPaths.add(currentPath);
815 }
816 modifiedFiles.add(currentPath);
817 addCheckoutMetadata(currentPath, attributes);
818 } else if (modeO != modeT) {
819
820 if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
821 .idEqual(T_BASE, T_THEIRS)))) {
822 MergeResult<RawText> result = contentMerge(base, ours, theirs,
823 attributes);
824
825 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
826 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
827 DirCacheEntry e = add(tw.getRawPath(), theirs,
828 DirCacheEntry.STAGE_3, 0, 0);
829
830
831 if (modeO == 0) {
832
833 if (isWorktreeDirty(work, ourDce))
834 return false;
835 if (nonTree(modeT)) {
836 if (e != null) {
837 addToCheckout(tw.getPathString(), e, attributes);
838 }
839 }
840 }
841
842 unmergedPaths.add(tw.getPathString());
843
844
845 mergeResults.put(tw.getPathString(), result);
846 }
847 }
848 return true;
849 }
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864 private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
865 CanonicalTreeParser ours, CanonicalTreeParser theirs,
866 Attributes attributes)
867 throws IOException {
868 RawText baseText;
869 RawText ourText;
870 RawText theirsText;
871
872 try {
873 baseText = base == null ? RawText.EMPTY_TEXT : getRawText(
874 base.getEntryObjectId(), attributes);
875 ourText = ours == null ? RawText.EMPTY_TEXT : getRawText(
876 ours.getEntryObjectId(), attributes);
877 theirsText = theirs == null ? RawText.EMPTY_TEXT : getRawText(
878 theirs.getEntryObjectId(), attributes);
879 } catch (BinaryBlobException e) {
880 MergeResult<RawText> r = new MergeResult<>(Collections.<RawText>emptyList());
881 r.setContainsConflicts(true);
882 return r;
883 }
884 return (mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText,
885 ourText, theirsText));
886 }
887
888 private boolean isIndexDirty() {
889 if (inCore)
890 return false;
891
892 final int modeI = tw.getRawMode(T_INDEX);
893 final int modeO = tw.getRawMode(T_OURS);
894
895
896 final boolean isDirty = nonTree(modeI)
897 && !(modeO == modeI && tw.idEqual(T_INDEX, T_OURS));
898 if (isDirty)
899 failingPaths
900 .put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
901 return isDirty;
902 }
903
904 private boolean isWorktreeDirty(WorkingTreeIterator work,
905 DirCacheEntry ourDce) throws IOException {
906 if (work == null)
907 return false;
908
909 final int modeF = tw.getRawMode(T_FILE);
910 final int modeO = tw.getRawMode(T_OURS);
911
912
913 boolean isDirty;
914 if (ourDce != null)
915 isDirty = work.isModified(ourDce, true, reader);
916 else {
917 isDirty = work.isModeDifferent(modeO);
918 if (!isDirty && nonTree(modeF))
919 isDirty = !tw.idEqual(T_FILE, T_OURS);
920 }
921
922
923 if (isDirty && modeF == FileMode.TYPE_TREE
924 && modeO == FileMode.TYPE_MISSING)
925 isDirty = false;
926 if (isDirty)
927 failingPaths.put(tw.getPathString(),
928 MergeFailureReason.DIRTY_WORKTREE);
929 return isDirty;
930 }
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946 private void updateIndex(CanonicalTreeParser base,
947 CanonicalTreeParser ours, CanonicalTreeParser theirs,
948 MergeResult<RawText> result, Attributes attributes)
949 throws FileNotFoundException,
950 IOException {
951 TemporaryBuffer rawMerged = null;
952 try {
953 rawMerged = doMerge(result);
954 File mergedFile = inCore ? null
955 : writeMergedFile(rawMerged, attributes);
956 if (result.containsConflicts()) {
957
958
959
960 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
961 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
962 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
963 mergeResults.put(tw.getPathString(), result);
964 return;
965 }
966
967
968
969 DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
970
971
972
973 int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
974 tw.getRawMode(2));
975 dce.setFileMode(newMode == FileMode.MISSING.getBits()
976 ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
977 if (mergedFile != null) {
978 dce.setLastModified(
979 nonNullRepo().getFS().lastModified(mergedFile));
980 dce.setLength((int) mergedFile.length());
981 }
982 dce.setObjectId(insertMergeResult(rawMerged, attributes));
983 builder.add(dce);
984 } finally {
985 if (rawMerged != null) {
986 rawMerged.destroy();
987 }
988 }
989 }
990
991
992
993
994
995
996
997
998
999
1000
1001
1002 private File writeMergedFile(TemporaryBuffer rawMerged,
1003 Attributes attributes)
1004 throws FileNotFoundException, IOException {
1005 File workTree = nonNullRepo().getWorkTree();
1006 FS fs = nonNullRepo().getFS();
1007 File of = new File(workTree, tw.getPathString());
1008 File parentFolder = of.getParentFile();
1009 if (!fs.exists(parentFolder)) {
1010 parentFolder.mkdirs();
1011 }
1012 EolStreamType streamType = EolStreamTypeUtil.detectStreamType(
1013 OperationType.CHECKOUT_OP, workingTreeOptions,
1014 attributes);
1015 try (OutputStream os = EolStreamTypeUtil.wrapOutputStream(
1016 new BufferedOutputStream(new FileOutputStream(of)),
1017 streamType)) {
1018 rawMerged.writeTo(os, null);
1019 }
1020 return of;
1021 }
1022
1023 private TemporaryBuffer doMerge(MergeResult<RawText> result)
1024 throws IOException {
1025 TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
1026 db != null ? nonNullRepo().getDirectory() : null, inCoreLimit);
1027 try {
1028 new MergeFormatter().formatMerge(buf, result,
1029 Arrays.asList(commitNames), CHARACTER_ENCODING);
1030 buf.close();
1031 } catch (IOException e) {
1032 buf.destroy();
1033 throw e;
1034 }
1035 return buf;
1036 }
1037
1038 private ObjectId insertMergeResult(TemporaryBuffer buf,
1039 Attributes attributes) throws IOException {
1040 InputStream in = buf.openInputStream();
1041 try (LfsInputStream is = LfsFactory.getInstance().applyCleanFilter(
1042 getRepository(), in,
1043 buf.length(), attributes.get(Constants.ATTR_MERGE))) {
1044 return getObjectInserter().insert(OBJ_BLOB, is.getLength(), is);
1045 }
1046 }
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064 private int mergeFileModes(int modeB, int modeO, int modeT) {
1065 if (modeO == modeT)
1066 return modeO;
1067 if (modeB == modeO)
1068
1069 return (modeT == FileMode.MISSING.getBits()) ? modeO : modeT;
1070 if (modeB == modeT)
1071
1072 return (modeO == FileMode.MISSING.getBits()) ? modeT : modeO;
1073 return FileMode.MISSING.getBits();
1074 }
1075
1076 private RawText getRawText(ObjectId id,
1077 Attributes attributes)
1078 throws IOException, BinaryBlobException {
1079 if (id.equals(ObjectId.zeroId()))
1080 return new RawText(new byte[] {});
1081
1082 ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter(
1083 getRepository(), reader.open(id, OBJ_BLOB),
1084 attributes.get(Constants.ATTR_MERGE));
1085 int threshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
1086 return RawText.load(loader, threshold);
1087 }
1088
1089 private static boolean nonTree(int mode) {
1090 return mode != 0 && !FileMode.TREE.equals(mode);
1091 }
1092
1093 private static boolean isGitLink(int mode) {
1094 return FileMode.GITLINK.equals(mode);
1095 }
1096
1097
1098 @Override
1099 public ObjectId getResultTreeId() {
1100 return (resultTree == null) ? null : resultTree.toObjectId();
1101 }
1102
1103
1104
1105
1106
1107
1108
1109
1110 public void setCommitNames(String[] commitNames) {
1111 this.commitNames = commitNames;
1112 }
1113
1114
1115
1116
1117
1118
1119
1120 public String[] getCommitNames() {
1121 return commitNames;
1122 }
1123
1124
1125
1126
1127
1128
1129
1130
1131 public List<String> getUnmergedPaths() {
1132 return unmergedPaths;
1133 }
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143 public List<String> getModifiedFiles() {
1144 return modifiedFiles;
1145 }
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157 public Map<String, DirCacheEntry> getToBeCheckedOut() {
1158 return toBeCheckedOut;
1159 }
1160
1161
1162
1163
1164
1165
1166 public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
1167 return mergeResults;
1168 }
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178 public Map<String, MergeFailureReason> getFailingPaths() {
1179 return (failingPaths.size() == 0) ? null : failingPaths;
1180 }
1181
1182
1183
1184
1185
1186
1187
1188
1189 public boolean failed() {
1190 return failingPaths.size() > 0;
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206 public void setDirCache(DirCache dc) {
1207 this.dircache = dc;
1208 implicitDirCache = false;
1209 }
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222 public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
1223 this.workingTreeIterator = workingTreeIterator;
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
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 protected boolean mergeTrees(AbstractTreeIterator baseTree,
1261 RevTree headTree, RevTree mergeTree, boolean ignoreConflicts)
1262 throws IOException {
1263
1264 builder = dircache.builder();
1265 DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
1266
1267 tw = new NameConflictTreeWalk(db, reader);
1268 tw.addTree(baseTree);
1269 tw.addTree(headTree);
1270 tw.addTree(mergeTree);
1271 int dciPos = tw.addTree(buildIt);
1272 if (workingTreeIterator != null) {
1273 tw.addTree(workingTreeIterator);
1274 workingTreeIterator.setDirCacheIterator(tw, dciPos);
1275 } else {
1276 tw.setFilter(TreeFilter.ANY_DIFF);
1277 }
1278
1279 if (!mergeTreeWalk(tw, ignoreConflicts)) {
1280 return false;
1281 }
1282
1283 if (!inCore) {
1284
1285
1286
1287 checkout();
1288
1289
1290
1291
1292
1293 if (!builder.commit()) {
1294 cleanUp();
1295 throw new IndexWriteException();
1296 }
1297 builder = null;
1298
1299 } else {
1300 builder.finish();
1301 builder = null;
1302 }
1303
1304 if (getUnmergedPaths().isEmpty() && !failed()) {
1305 resultTree = dircache.writeTree(getObjectInserter());
1306 return true;
1307 } else {
1308 resultTree = null;
1309 return false;
1310 }
1311 }
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325 protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
1326 throws IOException {
1327 boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE;
1328 boolean hasAttributeNodeProvider = treeWalk
1329 .getAttributesNodeProvider() != null;
1330 while (treeWalk.next()) {
1331 if (!processEntry(
1332 treeWalk.getTree(T_BASE, CanonicalTreeParser.class),
1333 treeWalk.getTree(T_OURS, CanonicalTreeParser.class),
1334 treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class),
1335 treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class),
1336 hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
1337 WorkingTreeIterator.class) : null,
1338 ignoreConflicts, hasAttributeNodeProvider
1339 ? treeWalk.getAttributes()
1340 : NO_ATTRIBUTES)) {
1341 cleanUp();
1342 return false;
1343 }
1344 if (treeWalk.isSubtree() && enterSubtree)
1345 treeWalk.enterSubtree();
1346 }
1347 return true;
1348 }
1349 }