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