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