MergeResult.java

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

  45. import java.text.MessageFormat;
  46. import java.util.HashMap;
  47. import java.util.List;
  48. import java.util.Map;

  49. import org.eclipse.jgit.internal.JGitText;
  50. import org.eclipse.jgit.lib.ObjectId;
  51. import org.eclipse.jgit.merge.MergeChunk;
  52. import org.eclipse.jgit.merge.MergeChunk.ConflictState;
  53. import org.eclipse.jgit.merge.MergeStrategy;
  54. import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;

  55. /**
  56.  * Encapsulates the result of a {@link org.eclipse.jgit.api.MergeCommand}.
  57.  */
  58. public class MergeResult {

  59.     /**
  60.      * The status the merge resulted in.
  61.      */
  62.     public enum MergeStatus {
  63.         /** */
  64.         FAST_FORWARD {
  65.             @Override
  66.             public String toString() {
  67.                 return "Fast-forward"; //$NON-NLS-1$
  68.             }

  69.             @Override
  70.             public boolean isSuccessful() {
  71.                 return true;
  72.             }
  73.         },
  74.         /**
  75.          * @since 2.0
  76.          */
  77.         FAST_FORWARD_SQUASHED {
  78.             @Override
  79.             public String toString() {
  80.                 return "Fast-forward-squashed"; //$NON-NLS-1$
  81.             }

  82.             @Override
  83.             public boolean isSuccessful() {
  84.                 return true;
  85.             }
  86.         },
  87.         /** */
  88.         ALREADY_UP_TO_DATE {
  89.             @Override
  90.             public String toString() {
  91.                 return "Already-up-to-date"; //$NON-NLS-1$
  92.             }

  93.             @Override
  94.             public boolean isSuccessful() {
  95.                 return true;
  96.             }
  97.         },
  98.         /** */
  99.         FAILED {
  100.             @Override
  101.             public String toString() {
  102.                 return "Failed"; //$NON-NLS-1$
  103.             }

  104.             @Override
  105.             public boolean isSuccessful() {
  106.                 return false;
  107.             }
  108.         },
  109.         /** */
  110.         MERGED {
  111.             @Override
  112.             public String toString() {
  113.                 return "Merged"; //$NON-NLS-1$
  114.             }

  115.             @Override
  116.             public boolean isSuccessful() {
  117.                 return true;
  118.             }
  119.         },
  120.         /**
  121.          * @since 2.0
  122.          */
  123.         MERGED_SQUASHED {
  124.             @Override
  125.             public String toString() {
  126.                 return "Merged-squashed"; //$NON-NLS-1$
  127.             }

  128.             @Override
  129.             public boolean isSuccessful() {
  130.                 return true;
  131.             }
  132.         },
  133.         /**
  134.          * @since 3.0
  135.          */
  136.         MERGED_SQUASHED_NOT_COMMITTED {
  137.             @Override
  138.             public String toString() {
  139.                 return "Merged-squashed-not-committed"; //$NON-NLS-1$
  140.             }

  141.             @Override
  142.             public boolean isSuccessful() {
  143.                 return true;
  144.             }
  145.         },
  146.         /** */
  147.         CONFLICTING {
  148.             @Override
  149.             public String toString() {
  150.                 return "Conflicting"; //$NON-NLS-1$
  151.             }

  152.             @Override
  153.             public boolean isSuccessful() {
  154.                 return false;
  155.             }
  156.         },
  157.         /**
  158.          * @since 2.2
  159.          */
  160.         ABORTED {
  161.             @Override
  162.             public String toString() {
  163.                 return "Aborted"; //$NON-NLS-1$
  164.             }

  165.             @Override
  166.             public boolean isSuccessful() {
  167.                 return false;
  168.             }
  169.         },
  170.         /**
  171.          * @since 3.0
  172.          **/
  173.         MERGED_NOT_COMMITTED {
  174.             @Override
  175.             public String toString() {
  176.                 return "Merged-not-committed"; //$NON-NLS-1$
  177.             }

  178.             @Override
  179.             public boolean isSuccessful() {
  180.                 return true;
  181.             }
  182.         },
  183.         /** */
  184.         NOT_SUPPORTED {
  185.             @Override
  186.             public String toString() {
  187.                 return "Not-yet-supported"; //$NON-NLS-1$
  188.             }

  189.             @Override
  190.             public boolean isSuccessful() {
  191.                 return false;
  192.             }
  193.         },
  194.         /**
  195.          * Status representing a checkout conflict, meaning that nothing could
  196.          * be merged, as the pre-scan for the trees already failed for certain
  197.          * files (i.e. local modifications prevent checkout of files).
  198.          */
  199.         CHECKOUT_CONFLICT {
  200.             @Override
  201.             public String toString() {
  202.                 return "Checkout Conflict"; //$NON-NLS-1$
  203.             }

  204.             @Override
  205.             public boolean isSuccessful() {
  206.                 return false;
  207.             }
  208.         };

  209.         /**
  210.          * @return whether the status indicates a successful result
  211.          */
  212.         public abstract boolean isSuccessful();
  213.     }

  214.     private ObjectId[] mergedCommits;

  215.     private ObjectId base;

  216.     private ObjectId newHead;

  217.     private Map<String, int[][]> conflicts;

  218.     private MergeStatus mergeStatus;

  219.     private String description;

  220.     private MergeStrategy mergeStrategy;

  221.     private Map<String, MergeFailureReason> failingPaths;

  222.     private List<String> checkoutConflicts;

  223.     /**
  224.      * Constructor for MergeResult.
  225.      *
  226.      * @param newHead
  227.      *            the object the head points at after the merge
  228.      * @param base
  229.      *            the common base which was used to produce a content-merge. May
  230.      *            be <code>null</code> if the merge-result was produced without
  231.      *            computing a common base
  232.      * @param mergedCommits
  233.      *            all the commits which have been merged together
  234.      * @param mergeStatus
  235.      *            the status the merge resulted in
  236.      * @param mergeStrategy
  237.      *            the used {@link org.eclipse.jgit.merge.MergeStrategy}
  238.      * @param lowLevelResults
  239.      *            merge results as returned by
  240.      *            {@link org.eclipse.jgit.merge.ResolveMerger#getMergeResults()}
  241.      * @since 2.0
  242.      */
  243.     public MergeResult(ObjectId newHead, ObjectId base,
  244.             ObjectId[] mergedCommits, MergeStatus mergeStatus,
  245.             MergeStrategy mergeStrategy,
  246.             Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults) {
  247.         this(newHead, base, mergedCommits, mergeStatus, mergeStrategy,
  248.                 lowLevelResults, null);
  249.     }

  250.     /**
  251.      * Constructor for MergeResult.
  252.      *
  253.      * @param newHead
  254.      *            the object the head points at after the merge
  255.      * @param base
  256.      *            the common base which was used to produce a content-merge. May
  257.      *            be <code>null</code> if the merge-result was produced without
  258.      *            computing a common base
  259.      * @param mergedCommits
  260.      *            all the commits which have been merged together
  261.      * @param mergeStatus
  262.      *            the status the merge resulted in
  263.      * @param mergeStrategy
  264.      *            the used {@link org.eclipse.jgit.merge.MergeStrategy}
  265.      * @param lowLevelResults
  266.      *            merge results as returned by
  267.      *            {@link org.eclipse.jgit.merge.ResolveMerger#getMergeResults()}
  268.      * @param description
  269.      *            a user friendly description of the merge result
  270.      */
  271.     public MergeResult(ObjectId newHead, ObjectId base,
  272.             ObjectId[] mergedCommits, MergeStatus mergeStatus,
  273.             MergeStrategy mergeStrategy,
  274.             Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults,
  275.             String description) {
  276.         this(newHead, base, mergedCommits, mergeStatus, mergeStrategy,
  277.                 lowLevelResults, null, description);
  278.     }

  279.     /**
  280.      * Constructor for MergeResult.
  281.      *
  282.      * @param newHead
  283.      *            the object the head points at after the merge
  284.      * @param base
  285.      *            the common base which was used to produce a content-merge. May
  286.      *            be <code>null</code> if the merge-result was produced without
  287.      *            computing a common base
  288.      * @param mergedCommits
  289.      *            all the commits which have been merged together
  290.      * @param mergeStatus
  291.      *            the status the merge resulted in
  292.      * @param mergeStrategy
  293.      *            the used {@link org.eclipse.jgit.merge.MergeStrategy}
  294.      * @param lowLevelResults
  295.      *            merge results as returned by
  296.      *            {@link org.eclipse.jgit.merge.ResolveMerger#getMergeResults()}
  297.      * @param failingPaths
  298.      *            list of paths causing this merge to fail as returned by
  299.      *            {@link org.eclipse.jgit.merge.ResolveMerger#getFailingPaths()}
  300.      * @param description
  301.      *            a user friendly description of the merge result
  302.      */
  303.     public MergeResult(ObjectId newHead, ObjectId base,
  304.             ObjectId[] mergedCommits, MergeStatus mergeStatus,
  305.             MergeStrategy mergeStrategy,
  306.             Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults,
  307.             Map<String, MergeFailureReason> failingPaths, String description) {
  308.         this.newHead = newHead;
  309.         this.mergedCommits = mergedCommits;
  310.         this.base = base;
  311.         this.mergeStatus = mergeStatus;
  312.         this.mergeStrategy = mergeStrategy;
  313.         this.description = description;
  314.         this.failingPaths = failingPaths;
  315.         if (lowLevelResults != null)
  316.             for (Map.Entry<String, org.eclipse.jgit.merge.MergeResult<?>> result : lowLevelResults
  317.                     .entrySet())
  318.                 addConflict(result.getKey(), result.getValue());
  319.     }

  320.     /**
  321.      * Creates a new result that represents a checkout conflict before the
  322.      * operation even started for real.
  323.      *
  324.      * @param checkoutConflicts
  325.      *            the conflicting files
  326.      */
  327.     public MergeResult(List<String> checkoutConflicts) {
  328.         this.checkoutConflicts = checkoutConflicts;
  329.         this.mergeStatus = MergeStatus.CHECKOUT_CONFLICT;
  330.     }

  331.     /**
  332.      * Get the object the head points at after the merge
  333.      *
  334.      * @return the object the head points at after the merge
  335.      */
  336.     public ObjectId getNewHead() {
  337.         return newHead;
  338.     }

  339.     /**
  340.      * Get the merge status
  341.      *
  342.      * @return the status the merge resulted in
  343.      */
  344.     public MergeStatus getMergeStatus() {
  345.         return mergeStatus;
  346.     }

  347.     /**
  348.      * Get the commits which have been merged
  349.      *
  350.      * @return all the commits which have been merged together
  351.      */
  352.     public ObjectId[] getMergedCommits() {
  353.         return mergedCommits;
  354.     }

  355.     /**
  356.      * Get the common base
  357.      *
  358.      * @return base the common base which was used to produce a content-merge.
  359.      *         May be <code>null</code> if the merge-result was produced without
  360.      *         computing a common base
  361.      */
  362.     public ObjectId getBase() {
  363.         return base;
  364.     }

  365.     /** {@inheritDoc} */
  366.     @SuppressWarnings("nls")
  367.     @Override
  368.     public String toString() {
  369.         boolean first = true;
  370.         StringBuilder commits = new StringBuilder();
  371.         for (ObjectId commit : mergedCommits) {
  372.             if (!first)
  373.                 commits.append(", ");
  374.             else
  375.                 first = false;
  376.             commits.append(ObjectId.toString(commit));
  377.         }
  378.         return MessageFormat.format(
  379.                 JGitText.get().mergeUsingStrategyResultedInDescription,
  380.                 commits, ObjectId.toString(base), mergeStrategy.getName(),
  381.                 mergeStatus, (description == null ? "" : ", " + description));
  382.     }

  383.     /**
  384.      * Set conflicts
  385.      *
  386.      * @param conflicts
  387.      *            the conflicts to set
  388.      */
  389.     public void setConflicts(Map<String, int[][]> conflicts) {
  390.         this.conflicts = conflicts;
  391.     }

  392.     /**
  393.      * Add a conflict
  394.      *
  395.      * @param path
  396.      *            path of the file to add a conflict for
  397.      * @param conflictingRanges
  398.      *            the conflicts to set
  399.      */
  400.     public void addConflict(String path, int[][] conflictingRanges) {
  401.         if (conflicts == null)
  402.             conflicts = new HashMap<>();
  403.         conflicts.put(path, conflictingRanges);
  404.     }

  405.     /**
  406.      * Add a conflict
  407.      *
  408.      * @param path
  409.      *            path of the file to add a conflict for
  410.      * @param lowLevelResult
  411.      *            a {@link org.eclipse.jgit.merge.MergeResult}
  412.      */
  413.     public void addConflict(String path, org.eclipse.jgit.merge.MergeResult<?> lowLevelResult) {
  414.         if (!lowLevelResult.containsConflicts())
  415.             return;
  416.         if (conflicts == null)
  417.             conflicts = new HashMap<>();
  418.         int nrOfConflicts = 0;
  419.         // just counting
  420.         for (MergeChunk mergeChunk : lowLevelResult) {
  421.             if (mergeChunk.getConflictState().equals(ConflictState.FIRST_CONFLICTING_RANGE)) {
  422.                 nrOfConflicts++;
  423.             }
  424.         }
  425.         int currentConflict = -1;
  426.         int[][] ret=new int[nrOfConflicts][mergedCommits.length+1];
  427.         for (MergeChunk mergeChunk : lowLevelResult) {
  428.             // to store the end of this chunk (end of the last conflicting range)
  429.             int endOfChunk = 0;
  430.             if (mergeChunk.getConflictState().equals(ConflictState.FIRST_CONFLICTING_RANGE)) {
  431.                 if (currentConflict > -1) {
  432.                     // there was a previous conflicting range for which the end
  433.                     // is not set yet - set it!
  434.                     ret[currentConflict][mergedCommits.length] = endOfChunk;
  435.                 }
  436.                 currentConflict++;
  437.                 endOfChunk = mergeChunk.getEnd();
  438.                 ret[currentConflict][mergeChunk.getSequenceIndex()] = mergeChunk.getBegin();
  439.             }
  440.             if (mergeChunk.getConflictState().equals(ConflictState.NEXT_CONFLICTING_RANGE)) {
  441.                 if (mergeChunk.getEnd() > endOfChunk)
  442.                     endOfChunk = mergeChunk.getEnd();
  443.                 ret[currentConflict][mergeChunk.getSequenceIndex()] = mergeChunk.getBegin();
  444.             }
  445.         }
  446.         conflicts.put(path, ret);
  447.     }

  448.     /**
  449.      * Returns information about the conflicts which occurred during a
  450.      * {@link org.eclipse.jgit.api.MergeCommand}. The returned value maps the
  451.      * path of a conflicting file to a two-dimensional int-array of line-numbers
  452.      * telling where in the file conflict markers for which merged commit can be
  453.      * found.
  454.      * <p>
  455.      * If the returned value contains a mapping "path"-&gt;[x][y]=z then this
  456.      * means
  457.      * <ul>
  458.      * <li>the file with path "path" contains conflicts</li>
  459.      * <li>if y &lt; "number of merged commits": for conflict number x in this
  460.      * file the chunk which was copied from commit number y starts on line
  461.      * number z. All numberings and line numbers start with 0.</li>
  462.      * <li>if y == "number of merged commits": the first non-conflicting line
  463.      * after conflict number x starts at line number z</li>
  464.      * </ul>
  465.      * <p>
  466.      * Example code how to parse this data:
  467.      *
  468.      * <pre>
  469.      * MergeResult m=...;
  470.      * Map&lt;String, int[][]&gt; allConflicts = m.getConflicts();
  471.      * for (String path : allConflicts.keySet()) {
  472.      *  int[][] c = allConflicts.get(path);
  473.      *  System.out.println("Conflicts in file " + path);
  474.      *  for (int i = 0; i &lt; c.length; ++i) {
  475.      *      System.out.println("  Conflict #" + i);
  476.      *      for (int j = 0; j &lt; (c[i].length) - 1; ++j) {
  477.      *          if (c[i][j] &gt;= 0)
  478.      *              System.out.println("    Chunk for "
  479.      *                      + m.getMergedCommits()[j] + " starts on line #"
  480.      *                      + c[i][j]);
  481.      *      }
  482.      *  }
  483.      * }
  484.      * </pre>
  485.      *
  486.      * @return the conflicts or <code>null</code> if no conflict occurred
  487.      */
  488.     public Map<String, int[][]> getConflicts() {
  489.         return conflicts;
  490.     }

  491.     /**
  492.      * Returns a list of paths causing this merge to fail as returned by
  493.      * {@link org.eclipse.jgit.merge.ResolveMerger#getFailingPaths()}
  494.      *
  495.      * @return the list of paths causing this merge to fail or <code>null</code>
  496.      *         if no failure occurred
  497.      */
  498.     public Map<String, MergeFailureReason> getFailingPaths() {
  499.         return failingPaths;
  500.     }

  501.     /**
  502.      * Returns a list of paths that cause a checkout conflict. These paths
  503.      * prevent the operation from even starting.
  504.      *
  505.      * @return the list of files that caused the checkout conflict.
  506.      */
  507.     public List<String> getCheckoutConflicts() {
  508.         return checkoutConflicts;
  509.     }
  510. }