DeleteBranchCommand.java

  1. /*
  2.  * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
  3.  * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.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.io.IOException;
  46. import java.text.MessageFormat;
  47. import java.util.ArrayList;
  48. import java.util.HashSet;
  49. import java.util.List;
  50. import java.util.Set;

  51. import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
  52. import org.eclipse.jgit.api.errors.GitAPIException;
  53. import org.eclipse.jgit.api.errors.JGitInternalException;
  54. import org.eclipse.jgit.api.errors.NotMergedException;
  55. import org.eclipse.jgit.internal.JGitText;
  56. import org.eclipse.jgit.lib.ConfigConstants;
  57. import org.eclipse.jgit.lib.Constants;
  58. import org.eclipse.jgit.lib.Ref;
  59. import org.eclipse.jgit.lib.RefUpdate;
  60. import org.eclipse.jgit.lib.RefUpdate.Result;
  61. import org.eclipse.jgit.lib.Repository;
  62. import org.eclipse.jgit.lib.StoredConfig;
  63. import org.eclipse.jgit.revwalk.RevCommit;
  64. import org.eclipse.jgit.revwalk.RevWalk;

  65. /**
  66.  * Used to delete one or several branches.
  67.  *
  68.  * The result of {@link #call()} is a list with the (full) names of the deleted
  69.  * branches.
  70.  *
  71.  * Note that we don't have a setter corresponding to the -r option; remote
  72.  * tracking branches are simply deleted just like local branches.
  73.  *
  74.  * @see <a
  75.  *      href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
  76.  *      >Git documentation about Branch</a>
  77.  */
  78. public class DeleteBranchCommand extends GitCommand<List<String>> {
  79.     private final Set<String> branchNames = new HashSet<>();

  80.     private boolean force;

  81.     /**
  82.      * Constructor for DeleteBranchCommand
  83.      *
  84.      * @param repo
  85.      *            the {@link org.eclipse.jgit.lib.Repository}
  86.      */
  87.     protected DeleteBranchCommand(Repository repo) {
  88.         super(repo);
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public List<String> call() throws GitAPIException,
  93.             NotMergedException, CannotDeleteCurrentBranchException {
  94.         checkCallable();
  95.         List<String> result = new ArrayList<>();
  96.         if (branchNames.isEmpty())
  97.             return result;
  98.         try {
  99.             String currentBranch = repo.getFullBranch();
  100.             if (!force) {
  101.                 // check if the branches to be deleted
  102.                 // are all merged into the current branch
  103.                 try (RevWalk walk = new RevWalk(repo)) {
  104.                     RevCommit tip = walk
  105.                             .parseCommit(repo.resolve(Constants.HEAD));
  106.                     for (String branchName : branchNames) {
  107.                         if (branchName == null)
  108.                             continue;
  109.                         Ref currentRef = repo.findRef(branchName);
  110.                         if (currentRef == null)
  111.                             continue;

  112.                         RevCommit base = walk
  113.                                 .parseCommit(repo.resolve(branchName));
  114.                         if (!walk.isMergedInto(base, tip)) {
  115.                             throw new NotMergedException();
  116.                         }
  117.                     }
  118.                 }
  119.             }
  120.             setCallable(false);
  121.             for (String branchName : branchNames) {
  122.                 if (branchName == null)
  123.                     continue;
  124.                 Ref currentRef = repo.findRef(branchName);
  125.                 if (currentRef == null)
  126.                     continue;
  127.                 String fullName = currentRef.getName();
  128.                 if (fullName.equals(currentBranch))
  129.                     throw new CannotDeleteCurrentBranchException(
  130.                             MessageFormat
  131.                                     .format(
  132.                                             JGitText.get().cannotDeleteCheckedOutBranch,
  133.                                             branchName));
  134.                 RefUpdate update = repo.updateRef(fullName);
  135.                 update.setRefLogMessage("branch deleted", false); //$NON-NLS-1$
  136.                 update.setForceUpdate(true);
  137.                 Result deleteResult = update.delete();

  138.                 boolean ok = true;
  139.                 switch (deleteResult) {
  140.                 case IO_FAILURE:
  141.                 case LOCK_FAILURE:
  142.                 case REJECTED:
  143.                     ok = false;
  144.                     break;
  145.                 default:
  146.                     break;
  147.                 }

  148.                 if (ok) {
  149.                     result.add(fullName);
  150.                     if (fullName.startsWith(Constants.R_HEADS)) {
  151.                         String shortenedName = fullName
  152.                                 .substring(Constants.R_HEADS.length());
  153.                         // remove upstream configuration if any
  154.                         final StoredConfig cfg = repo.getConfig();
  155.                         cfg.unsetSection(
  156.                                 ConfigConstants.CONFIG_BRANCH_SECTION,
  157.                                 shortenedName);
  158.                         cfg.save();
  159.                     }
  160.                 } else
  161.                     throw new JGitInternalException(MessageFormat.format(
  162.                             JGitText.get().deleteBranchUnexpectedResult,
  163.                             deleteResult.name()));
  164.             }
  165.             return result;
  166.         } catch (IOException ioe) {
  167.             throw new JGitInternalException(ioe.getMessage(), ioe);
  168.         }
  169.     }

  170.     /**
  171.      * Set the names of the branches to delete
  172.      *
  173.      * @param branchnames
  174.      *            the names of the branches to delete; if not set, this will do
  175.      *            nothing; invalid branch names will simply be ignored
  176.      * @return this instance
  177.      */
  178.     public DeleteBranchCommand setBranchNames(String... branchnames) {
  179.         checkCallable();
  180.         this.branchNames.clear();
  181.         for (String branch : branchnames)
  182.             this.branchNames.add(branch);
  183.         return this;
  184.     }

  185.     /**
  186.      * Set whether to forcefully delete branches
  187.      *
  188.      * @param force
  189.      *            <code>true</code> corresponds to the -D option,
  190.      *            <code>false</code> to the -d option (default) <br>
  191.      *            if <code>false</code> a check will be performed whether the
  192.      *            branch to be deleted is already merged into the current branch
  193.      *            and deletion will be refused in this case
  194.      * @return this instance
  195.      */
  196.     public DeleteBranchCommand setForce(boolean force) {
  197.         checkCallable();
  198.         this.force = force;
  199.         return this;
  200.     }
  201. }