StrategySimpleTwoWayInCore.java

  1. /*
  2.  * Copyright (C) 2008-2009, Google Inc.
  3.  * and other copyright owners as documented in the project's IP log.
  4.  *
  5.  * This program and the accompanying materials are made available
  6.  * under the terms of the Eclipse Distribution License v1.0 which
  7.  * accompanies this distribution, is reproduced below, and is
  8.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  9.  *
  10.  * All rights reserved.
  11.  *
  12.  * Redistribution and use in source and binary forms, with or
  13.  * without modification, are permitted provided that the following
  14.  * conditions are met:
  15.  *
  16.  * - Redistributions of source code must retain the above copyright
  17.  *   notice, this list of conditions and the following disclaimer.
  18.  *
  19.  * - Redistributions in binary form must reproduce the above
  20.  *   copyright notice, this list of conditions and the following
  21.  *   disclaimer in the documentation and/or other materials provided
  22.  *   with the distribution.
  23.  *
  24.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  25.  *   names of its contributors may be used to endorse or promote
  26.  *   products derived from this software without specific prior
  27.  *   written permission.
  28.  *
  29.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  */

  43. package org.eclipse.jgit.merge;

  44. import java.io.IOException;

  45. import org.eclipse.jgit.dircache.DirCache;
  46. import org.eclipse.jgit.dircache.DirCacheBuilder;
  47. import org.eclipse.jgit.dircache.DirCacheEntry;
  48. import org.eclipse.jgit.errors.UnmergedPathException;
  49. import org.eclipse.jgit.lib.Config;
  50. import org.eclipse.jgit.lib.FileMode;
  51. import org.eclipse.jgit.lib.ObjectId;
  52. import org.eclipse.jgit.lib.ObjectInserter;
  53. import org.eclipse.jgit.lib.Repository;
  54. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  55. import org.eclipse.jgit.treewalk.NameConflictTreeWalk;

  56. /**
  57.  * Merges two commits together in-memory, ignoring any working directory.
  58.  * <p>
  59.  * The strategy chooses a path from one of the two input trees if the path is
  60.  * unchanged in the other relative to their common merge base tree. This is a
  61.  * trivial 3-way merge (at the file path level only).
  62.  * <p>
  63.  * Modifications of the same file path (content and/or file mode) by both input
  64.  * trees will cause a merge conflict, as this strategy does not attempt to merge
  65.  * file contents.
  66.  */
  67. public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
  68.     /**
  69.      * Create a new instance of the strategy.
  70.      */
  71.     protected StrategySimpleTwoWayInCore() {
  72.         //
  73.     }

  74.     /** {@inheritDoc} */
  75.     @Override
  76.     public String getName() {
  77.         return "simple-two-way-in-core"; //$NON-NLS-1$
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public ThreeWayMerger newMerger(Repository db) {
  82.         return new InCoreMerger(db);
  83.     }

  84.     /** {@inheritDoc} */
  85.     @Override
  86.     public ThreeWayMerger newMerger(Repository db, boolean inCore) {
  87.         // This class is always inCore, so ignore the parameter
  88.         return newMerger(db);
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
  93.         return new InCoreMerger(inserter);
  94.     }

  95.     private static class InCoreMerger extends ThreeWayMerger {
  96.         private static final int T_BASE = 0;

  97.         private static final int T_OURS = 1;

  98.         private static final int T_THEIRS = 2;

  99.         private final NameConflictTreeWalk tw;

  100.         private final DirCache cache;

  101.         private DirCacheBuilder builder;

  102.         private ObjectId resultTree;

  103.         InCoreMerger(Repository local) {
  104.             super(local);
  105.             tw = new NameConflictTreeWalk(local, reader);
  106.             cache = DirCache.newInCore();
  107.         }

  108.         InCoreMerger(ObjectInserter inserter) {
  109.             super(inserter);
  110.             tw = new NameConflictTreeWalk(null, reader);
  111.             cache = DirCache.newInCore();
  112.         }

  113.         @Override
  114.         protected boolean mergeImpl() throws IOException {
  115.             tw.addTree(mergeBase());
  116.             tw.addTree(sourceTrees[0]);
  117.             tw.addTree(sourceTrees[1]);

  118.             boolean hasConflict = false;
  119.             builder = cache.builder();
  120.             while (tw.next()) {
  121.                 final int modeO = tw.getRawMode(T_OURS);
  122.                 final int modeT = tw.getRawMode(T_THEIRS);
  123.                 if (modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
  124.                     add(T_OURS, DirCacheEntry.STAGE_0);
  125.                     continue;
  126.                 }

  127.                 final int modeB = tw.getRawMode(T_BASE);
  128.                 if (modeB == modeO && tw.idEqual(T_BASE, T_OURS))
  129.                     add(T_THEIRS, DirCacheEntry.STAGE_0);
  130.                 else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
  131.                     add(T_OURS, DirCacheEntry.STAGE_0);
  132.                 else {
  133.                     if (nonTree(modeB)) {
  134.                         add(T_BASE, DirCacheEntry.STAGE_1);
  135.                         hasConflict = true;
  136.                     }
  137.                     if (nonTree(modeO)) {
  138.                         add(T_OURS, DirCacheEntry.STAGE_2);
  139.                         hasConflict = true;
  140.                     }
  141.                     if (nonTree(modeT)) {
  142.                         add(T_THEIRS, DirCacheEntry.STAGE_3);
  143.                         hasConflict = true;
  144.                     }
  145.                     if (tw.isSubtree())
  146.                         tw.enterSubtree();
  147.                 }
  148.             }
  149.             builder.finish();
  150.             builder = null;

  151.             if (hasConflict)
  152.                 return false;
  153.             try {
  154.                 ObjectInserter odi = getObjectInserter();
  155.                 resultTree = cache.writeTree(odi);
  156.                 odi.flush();
  157.                 return true;
  158.             } catch (UnmergedPathException upe) {
  159.                 resultTree = null;
  160.                 return false;
  161.             }
  162.         }

  163.         private static boolean nonTree(int mode) {
  164.             return mode != 0 && !FileMode.TREE.equals(mode);
  165.         }

  166.         private void add(int tree, int stage) throws IOException {
  167.             final AbstractTreeIterator i = getTree(tree);
  168.             if (i != null) {
  169.                 if (FileMode.TREE.equals(tw.getRawMode(tree))) {
  170.                     builder.addTree(tw.getRawPath(), stage, reader, tw
  171.                             .getObjectId(tree));
  172.                 } else {
  173.                     final DirCacheEntry e;

  174.                     e = new DirCacheEntry(tw.getRawPath(), stage);
  175.                     e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
  176.                     e.setFileMode(tw.getFileMode(tree));
  177.                     builder.add(e);
  178.                 }
  179.             }
  180.         }

  181.         private AbstractTreeIterator getTree(int tree) {
  182.             return tw.getTree(tree, AbstractTreeIterator.class);
  183.         }

  184.         @Override
  185.         public ObjectId getResultTreeId() {
  186.             return resultTree;
  187.         }
  188.     }

  189. }