MergeMessageFormatter.java

  1. /*
  2.  * Copyright (C) 2010-2012, Robin Stocker <robin@nibor.org>
  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.util.ArrayList;
  45. import java.util.List;

  46. import org.eclipse.jgit.lib.Constants;
  47. import org.eclipse.jgit.lib.ObjectId;
  48. import org.eclipse.jgit.lib.Ref;
  49. import org.eclipse.jgit.lib.Repository;
  50. import org.eclipse.jgit.util.ChangeIdUtil;
  51. import org.eclipse.jgit.util.StringUtils;

  52. /**
  53.  * Formatter for constructing the commit message for a merge commit.
  54.  * <p>
  55.  * The format should be the same as C Git does it, for compatibility.
  56.  */
  57. public class MergeMessageFormatter {
  58.     /**
  59.      * Construct the merge commit message.
  60.      *
  61.      * @param refsToMerge
  62.      *            the refs which will be merged
  63.      * @param target
  64.      *            the branch ref which will be merged into
  65.      * @return merge commit message
  66.      */
  67.     public String format(List<Ref> refsToMerge, Ref target) {
  68.         StringBuilder sb = new StringBuilder();
  69.         sb.append("Merge "); //$NON-NLS-1$

  70.         List<String> branches = new ArrayList<>();
  71.         List<String> remoteBranches = new ArrayList<>();
  72.         List<String> tags = new ArrayList<>();
  73.         List<String> commits = new ArrayList<>();
  74.         List<String> others = new ArrayList<>();
  75.         for (Ref ref : refsToMerge) {
  76.             if (ref.getName().startsWith(Constants.R_HEADS)) {
  77.                 branches.add("'" + Repository.shortenRefName(ref.getName()) //$NON-NLS-1$
  78.                         + "'"); //$NON-NLS-1$
  79.             } else if (ref.getName().startsWith(Constants.R_REMOTES)) {
  80.                 remoteBranches.add("'" //$NON-NLS-1$
  81.                         + Repository.shortenRefName(ref.getName()) + "'"); //$NON-NLS-1$
  82.             } else if (ref.getName().startsWith(Constants.R_TAGS)) {
  83.                 tags.add("'" + Repository.shortenRefName(ref.getName()) + "'"); //$NON-NLS-1$ //$NON-NLS-2$
  84.             } else {
  85.                 ObjectId objectId = ref.getObjectId();
  86.                 if (objectId != null && ref.getName().equals(objectId.getName())) {
  87.                     commits.add("'" + ref.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
  88.                 } else {
  89.                     others.add(ref.getName());
  90.                 }
  91.             }
  92.         }

  93.         List<String> listings = new ArrayList<>();

  94.         if (!branches.isEmpty())
  95.             listings.add(joinNames(branches, "branch", "branches")); //$NON-NLS-1$//$NON-NLS-2$

  96.         if (!remoteBranches.isEmpty())
  97.             listings.add(joinNames(remoteBranches, "remote-tracking branch", //$NON-NLS-1$
  98.                     "remote-tracking branches")); //$NON-NLS-1$

  99.         if (!tags.isEmpty())
  100.             listings.add(joinNames(tags, "tag", "tags")); //$NON-NLS-1$ //$NON-NLS-2$

  101.         if (!commits.isEmpty())
  102.             listings.add(joinNames(commits, "commit", "commits")); //$NON-NLS-1$ //$NON-NLS-2$

  103.         if (!others.isEmpty())
  104.             listings.add(StringUtils.join(others, ", ", " and ")); //$NON-NLS-1$ //$NON-NLS-2$

  105.         sb.append(StringUtils.join(listings, ", ")); //$NON-NLS-1$

  106.         String targetName = target.getLeaf().getName();
  107.         if (!targetName.equals(Constants.R_HEADS + Constants.MASTER)) {
  108.             String targetShortName = Repository.shortenRefName(targetName);
  109.             sb.append(" into " + targetShortName); //$NON-NLS-1$
  110.         }

  111.         return sb.toString();
  112.     }

  113.     /**
  114.      * Add section with conflicting paths to merge message.
  115.      *
  116.      * @param message
  117.      *            the original merge message
  118.      * @param conflictingPaths
  119.      *            the paths with conflicts
  120.      * @return merge message with conflicting paths added
  121.      */
  122.     public String formatWithConflicts(String message,
  123.             List<String> conflictingPaths) {
  124.         StringBuilder sb = new StringBuilder();
  125.         String[] lines = message.split("\n"); //$NON-NLS-1$
  126.         int firstFooterLine = ChangeIdUtil.indexOfFirstFooterLine(lines);
  127.         for (int i = 0; i < firstFooterLine; i++)
  128.             sb.append(lines[i]).append('\n');
  129.         if (firstFooterLine == lines.length && message.length() != 0)
  130.             sb.append('\n');
  131.         addConflictsMessage(conflictingPaths, sb);
  132.         if (firstFooterLine < lines.length)
  133.             sb.append('\n');
  134.         for (int i = firstFooterLine; i < lines.length; i++)
  135.             sb.append(lines[i]).append('\n');
  136.         return sb.toString();
  137.     }

  138.     private static void addConflictsMessage(List<String> conflictingPaths,
  139.             StringBuilder sb) {
  140.         sb.append("Conflicts:\n"); //$NON-NLS-1$
  141.         for (String conflictingPath : conflictingPaths)
  142.             sb.append('\t').append(conflictingPath).append('\n');
  143.     }

  144.     private static String joinNames(List<String> names, String singular,
  145.             String plural) {
  146.         if (names.size() == 1)
  147.             return singular + " " + names.get(0); //$NON-NLS-1$
  148.         else
  149.             return plural + " " + StringUtils.join(names, ", ", " and "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  150.     }
  151. }