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