StrategySimpleTwoWayInCore.java

  1. /*
  2.  * Copyright (C) 2008-2009, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.merge;

  11. import java.io.IOException;

  12. import org.eclipse.jgit.dircache.DirCache;
  13. import org.eclipse.jgit.dircache.DirCacheBuilder;
  14. import org.eclipse.jgit.dircache.DirCacheEntry;
  15. import org.eclipse.jgit.errors.UnmergedPathException;
  16. import org.eclipse.jgit.lib.Config;
  17. import org.eclipse.jgit.lib.FileMode;
  18. import org.eclipse.jgit.lib.ObjectId;
  19. import org.eclipse.jgit.lib.ObjectInserter;
  20. import org.eclipse.jgit.lib.Repository;
  21. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  22. import org.eclipse.jgit.treewalk.NameConflictTreeWalk;

  23. /**
  24.  * Merges two commits together in-memory, ignoring any working directory.
  25.  * <p>
  26.  * The strategy chooses a path from one of the two input trees if the path is
  27.  * unchanged in the other relative to their common merge base tree. This is a
  28.  * trivial 3-way merge (at the file path level only).
  29.  * <p>
  30.  * Modifications of the same file path (content and/or file mode) by both input
  31.  * trees will cause a merge conflict, as this strategy does not attempt to merge
  32.  * file contents.
  33.  */
  34. public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
  35.     /**
  36.      * Create a new instance of the strategy.
  37.      */
  38.     protected StrategySimpleTwoWayInCore() {
  39.         //
  40.     }

  41.     /** {@inheritDoc} */
  42.     @Override
  43.     public String getName() {
  44.         return "simple-two-way-in-core"; //$NON-NLS-1$
  45.     }

  46.     /** {@inheritDoc} */
  47.     @Override
  48.     public ThreeWayMerger newMerger(Repository db) {
  49.         return new InCoreMerger(db);
  50.     }

  51.     /** {@inheritDoc} */
  52.     @Override
  53.     public ThreeWayMerger newMerger(Repository db, boolean inCore) {
  54.         // This class is always inCore, so ignore the parameter
  55.         return newMerger(db);
  56.     }

  57.     /** {@inheritDoc} */
  58.     @Override
  59.     public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
  60.         return new InCoreMerger(inserter);
  61.     }

  62.     private static class InCoreMerger extends ThreeWayMerger {
  63.         private static final int T_BASE = 0;

  64.         private static final int T_OURS = 1;

  65.         private static final int T_THEIRS = 2;

  66.         private final NameConflictTreeWalk tw;

  67.         private final DirCache cache;

  68.         private DirCacheBuilder builder;

  69.         private ObjectId resultTree;

  70.         InCoreMerger(Repository local) {
  71.             super(local);
  72.             tw = new NameConflictTreeWalk(local, reader);
  73.             cache = DirCache.newInCore();
  74.         }

  75.         InCoreMerger(ObjectInserter inserter) {
  76.             super(inserter);
  77.             tw = new NameConflictTreeWalk(null, reader);
  78.             cache = DirCache.newInCore();
  79.         }

  80.         @Override
  81.         protected boolean mergeImpl() throws IOException {
  82.             tw.addTree(mergeBase());
  83.             tw.addTree(sourceTrees[0]);
  84.             tw.addTree(sourceTrees[1]);

  85.             boolean hasConflict = false;
  86.             builder = cache.builder();
  87.             while (tw.next()) {
  88.                 final int modeO = tw.getRawMode(T_OURS);
  89.                 final int modeT = tw.getRawMode(T_THEIRS);
  90.                 if (modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
  91.                     add(T_OURS, DirCacheEntry.STAGE_0);
  92.                     continue;
  93.                 }

  94.                 final int modeB = tw.getRawMode(T_BASE);
  95.                 if (modeB == modeO && tw.idEqual(T_BASE, T_OURS))
  96.                     add(T_THEIRS, DirCacheEntry.STAGE_0);
  97.                 else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
  98.                     add(T_OURS, DirCacheEntry.STAGE_0);
  99.                 else {
  100.                     if (nonTree(modeB)) {
  101.                         add(T_BASE, DirCacheEntry.STAGE_1);
  102.                         hasConflict = true;
  103.                     }
  104.                     if (nonTree(modeO)) {
  105.                         add(T_OURS, DirCacheEntry.STAGE_2);
  106.                         hasConflict = true;
  107.                     }
  108.                     if (nonTree(modeT)) {
  109.                         add(T_THEIRS, DirCacheEntry.STAGE_3);
  110.                         hasConflict = true;
  111.                     }
  112.                     if (tw.isSubtree())
  113.                         tw.enterSubtree();
  114.                 }
  115.             }
  116.             builder.finish();
  117.             builder = null;

  118.             if (hasConflict)
  119.                 return false;
  120.             try {
  121.                 ObjectInserter odi = getObjectInserter();
  122.                 resultTree = cache.writeTree(odi);
  123.                 odi.flush();
  124.                 return true;
  125.             } catch (UnmergedPathException upe) {
  126.                 resultTree = null;
  127.                 return false;
  128.             }
  129.         }

  130.         private static boolean nonTree(int mode) {
  131.             return mode != 0 && !FileMode.TREE.equals(mode);
  132.         }

  133.         private void add(int tree, int stage) throws IOException {
  134.             final AbstractTreeIterator i = getTree(tree);
  135.             if (i != null) {
  136.                 if (FileMode.TREE.equals(tw.getRawMode(tree))) {
  137.                     builder.addTree(tw.getRawPath(), stage, reader, tw
  138.                             .getObjectId(tree));
  139.                 } else {
  140.                     final DirCacheEntry e;

  141.                     e = new DirCacheEntry(tw.getRawPath(), stage);
  142.                     e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
  143.                     e.setFileMode(tw.getFileMode(tree));
  144.                     builder.add(e);
  145.                 }
  146.             }
  147.         }

  148.         private AbstractTreeIterator getTree(int tree) {
  149.             return tw.getTree(tree, AbstractTreeIterator.class);
  150.         }

  151.         @Override
  152.         public ObjectId getResultTreeId() {
  153.             return resultTree;
  154.         }
  155.     }

  156. }