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 }
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
672
673 DirCacheEntry e = add(tw.getRawPath(), theirs,
674 DirCacheEntry.STAGE_0, EPOCH, 0);
675 addToCheckout(tw.getPathString(), e, attributes);
676 }
677 return true;
678 }
679
680
681
682 add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
683 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
684 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
685 unmergedPaths.add(tw.getPathString());
686 mergeResults.put(tw.getPathString(),
687 new MergeResult<>(Collections.<RawText> emptyList()));
688 return true;
689 }
690
691 if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
692
693
694 if (ourDce != null)
695 keep(ourDce);
696
697 return true;
698 }
699
700 if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
701
702
703
704
705 if (isWorktreeDirty(work, ourDce))
706 return false;
707 if (nonTree(modeT)) {
708
709
710
711 DirCacheEntry e = add(tw.getRawPath(), theirs,
712 DirCacheEntry.STAGE_0, EPOCH, 0);
713 if (e != null) {
714 addToCheckout(tw.getPathString(), e, attributes);
715 }
716 return true;
717 }
718
719
720
721 if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) {
722
723 return true;
724 }
725 if (modeT != 0 && modeT == modeB) {
726
727 return true;
728 }
729 addDeletion(tw.getPathString(), nonTree(modeO), attributes);
730 return true;
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, EPOCH, 0);
741 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 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, EPOCH, 0);
749 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 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, EPOCH, 0);
777 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
778 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 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, EPOCH, 0);
826 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
827 DirCacheEntry e = add(tw.getRawPath(), theirs,
828 DirCacheEntry.STAGE_3, EPOCH, 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, EPOCH, 0);
961 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
962 add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 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().lastModifiedInstant(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 }
1311 resultTree = null;
1312 return false;
1313 }
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327 protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
1328 throws IOException {
1329 boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE;
1330 boolean hasAttributeNodeProvider = treeWalk
1331 .getAttributesNodeProvider() != null;
1332 while (treeWalk.next()) {
1333 if (!processEntry(
1334 treeWalk.getTree(T_BASE, CanonicalTreeParser.class),
1335 treeWalk.getTree(T_OURS, CanonicalTreeParser.class),
1336 treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class),
1337 treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class),
1338 hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
1339 WorkingTreeIterator.class) : null,
1340 ignoreConflicts, hasAttributeNodeProvider
1341 ? treeWalk.getAttributes()
1342 : NO_ATTRIBUTES)) {
1343 cleanUp();
1344 return false;
1345 }
1346 if (treeWalk.isSubtree() && enterSubtree)
1347 treeWalk.enterSubtree();
1348 }
1349 return true;
1350 }
1351 }