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 org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
51 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
52 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
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 Attributess">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/../../../org/eclipse/jgit/dircache/DirCacheEntry.html#DirCacheEntry">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 boolean success = false;
1028 try {
1029 new MergeFormatter().formatMerge(buf, result,
1030 Arrays.asList(commitNames), UTF_8);
1031 buf.close();
1032 success = true;
1033 } finally {
1034 if (!success) {
1035 buf.destroy();
1036 }
1037 }
1038 return buf;
1039 }
1040
1041 private ObjectId insertMergeResult(TemporaryBuffer buf,
1042 Attributes attributes) throws IOException {
1043 InputStream in = buf.openInputStream();
1044 try (LfsInputStream is = LfsFactory.getInstance().applyCleanFilter(
1045 getRepository(), in,
1046 buf.length(), attributes.get(Constants.ATTR_MERGE))) {
1047 return getObjectInserter().insert(OBJ_BLOB, is.getLength(), is);
1048 }
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067 private int mergeFileModes(int modeB, int modeO, int modeT) {
1068 if (modeO == modeT)
1069 return modeO;
1070 if (modeB == modeO)
1071
1072 return (modeT == FileMode.MISSING.getBits()) ? modeO : modeT;
1073 if (modeB == modeT)
1074
1075 return (modeO == FileMode.MISSING.getBits()) ? modeT : modeO;
1076 return FileMode.MISSING.getBits();
1077 }
1078
1079 private RawText getRawText(ObjectId id,
1080 Attributes attributes)
1081 throws IOException, BinaryBlobException {
1082 if (id.equals(ObjectId.zeroId()))
1083 return new RawText(new byte[] {});
1084
1085 ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter(
1086 getRepository(), reader.open(id, OBJ_BLOB),
1087 attributes.get(Constants.ATTR_MERGE));
1088 int threshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
1089 return RawText.load(loader, threshold);
1090 }
1091
1092 private static boolean nonTree(int mode) {
1093 return mode != 0 && !FileMode.TREE.equals(mode);
1094 }
1095
1096 private static boolean isGitLink(int mode) {
1097 return FileMode.GITLINK.equals(mode);
1098 }
1099
1100
1101 @Override
1102 public ObjectId getResultTreeId() {
1103 return (resultTree == null) ? null : resultTree.toObjectId();
1104 }
1105
1106
1107
1108
1109
1110
1111
1112
1113 public void setCommitNames(String[] commitNames) {
1114 this.commitNames = commitNames;
1115 }
1116
1117
1118
1119
1120
1121
1122
1123 public String[] getCommitNames() {
1124 return commitNames;
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134 public List<String> getUnmergedPaths() {
1135 return unmergedPaths;
1136 }
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146 public List<String> getModifiedFiles() {
1147 return modifiedFiles;
1148 }
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160 public Map<String, DirCacheEntry> getToBeCheckedOut() {
1161 return toBeCheckedOut;
1162 }
1163
1164
1165
1166
1167
1168
1169 public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
1170 return mergeResults;
1171 }
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181 public Map<String, MergeFailureReason> getFailingPaths() {
1182 return failingPaths.isEmpty() ? null : failingPaths;
1183 }
1184
1185
1186
1187
1188
1189
1190
1191
1192 public boolean failed() {
1193 return !failingPaths.isEmpty();
1194 }
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 public void setDirCache(DirCache dc) {
1210 this.dircache = dc;
1211 implicitDirCache = false;
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225 public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
1226 this.workingTreeIterator = workingTreeIterator;
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
1261
1262
1263 protected boolean mergeTrees(AbstractTreeIterator baseTree,
1264 RevTree headTree, RevTree mergeTree, boolean ignoreConflicts)
1265 throws IOException {
1266
1267 builder = dircache.builder();
1268 DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
1269
1270 tw = new NameConflictTreeWalk(db, reader);
1271 tw.addTree(baseTree);
1272 tw.addTree(headTree);
1273 tw.addTree(mergeTree);
1274 int dciPos = tw.addTree(buildIt);
1275 if (workingTreeIterator != null) {
1276 tw.addTree(workingTreeIterator);
1277 workingTreeIterator.setDirCacheIterator(tw, dciPos);
1278 } else {
1279 tw.setFilter(TreeFilter.ANY_DIFF);
1280 }
1281
1282 if (!mergeTreeWalk(tw, ignoreConflicts)) {
1283 return false;
1284 }
1285
1286 if (!inCore) {
1287
1288
1289
1290 checkout();
1291
1292
1293
1294
1295
1296 if (!builder.commit()) {
1297 cleanUp();
1298 throw new IndexWriteException();
1299 }
1300 builder = null;
1301
1302 } else {
1303 builder.finish();
1304 builder = null;
1305 }
1306
1307 if (getUnmergedPaths().isEmpty() && !failed()) {
1308 resultTree = dircache.writeTree(getObjectInserter());
1309 return true;
1310 } else {
1311 resultTree = null;
1312 return false;
1313 }
1314 }
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328 protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
1329 throws IOException {
1330 boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE;
1331 boolean hasAttributeNodeProvider = treeWalk
1332 .getAttributesNodeProvider() != null;
1333 while (treeWalk.next()) {
1334 if (!processEntry(
1335 treeWalk.getTree(T_BASE, CanonicalTreeParser.class),
1336 treeWalk.getTree(T_OURS, CanonicalTreeParser.class),
1337 treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class),
1338 treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class),
1339 hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
1340 WorkingTreeIterator.class) : null,
1341 ignoreConflicts, hasAttributeNodeProvider
1342 ? treeWalk.getAttributes()
1343 : NO_ATTRIBUTES)) {
1344 cleanUp();
1345 return false;
1346 }
1347 if (treeWalk.isSubtree() && enterSubtree)
1348 treeWalk.enterSubtree();
1349 }
1350 return true;
1351 }
1352 }