DeleteBranchCommand.java

  1. /*
  2.  * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
  3.  * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */
  11. package org.eclipse.jgit.api;

  12. import java.io.IOException;
  13. import java.text.MessageFormat;
  14. import java.util.ArrayList;
  15. import java.util.Arrays;
  16. import java.util.HashSet;
  17. import java.util.List;
  18. import java.util.Set;

  19. import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
  20. import org.eclipse.jgit.api.errors.GitAPIException;
  21. import org.eclipse.jgit.api.errors.JGitInternalException;
  22. import org.eclipse.jgit.api.errors.NotMergedException;
  23. import org.eclipse.jgit.internal.JGitText;
  24. import org.eclipse.jgit.lib.ConfigConstants;
  25. import org.eclipse.jgit.lib.Constants;
  26. import org.eclipse.jgit.lib.Ref;
  27. import org.eclipse.jgit.lib.RefUpdate;
  28. import org.eclipse.jgit.lib.RefUpdate.Result;
  29. import org.eclipse.jgit.lib.Repository;
  30. import org.eclipse.jgit.lib.StoredConfig;
  31. import org.eclipse.jgit.revwalk.RevCommit;
  32. import org.eclipse.jgit.revwalk.RevWalk;

  33. /**
  34.  * Used to delete one or several branches.
  35.  *
  36.  * The result of {@link #call()} is a list with the (full) names of the deleted
  37.  * branches.
  38.  *
  39.  * Note that we don't have a setter corresponding to the -r option; remote
  40.  * tracking branches are simply deleted just like local branches.
  41.  *
  42.  * @see <a
  43.  *      href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
  44.  *      >Git documentation about Branch</a>
  45.  */
  46. public class DeleteBranchCommand extends GitCommand<List<String>> {
  47.     private final Set<String> branchNames = new HashSet<>();

  48.     private boolean force;

  49.     /**
  50.      * Constructor for DeleteBranchCommand
  51.      *
  52.      * @param repo
  53.      *            the {@link org.eclipse.jgit.lib.Repository}
  54.      */
  55.     protected DeleteBranchCommand(Repository repo) {
  56.         super(repo);
  57.     }

  58.     /** {@inheritDoc} */
  59.     @Override
  60.     public List<String> call() throws GitAPIException,
  61.             NotMergedException, CannotDeleteCurrentBranchException {
  62.         checkCallable();
  63.         List<String> result = new ArrayList<>();
  64.         if (branchNames.isEmpty())
  65.             return result;
  66.         try {
  67.             String currentBranch = repo.getFullBranch();
  68.             if (!force) {
  69.                 // check if the branches to be deleted
  70.                 // are all merged into the current branch
  71.                 try (RevWalk walk = new RevWalk(repo)) {
  72.                     RevCommit tip = walk
  73.                             .parseCommit(repo.resolve(Constants.HEAD));
  74.                     for (String branchName : branchNames) {
  75.                         if (branchName == null)
  76.                             continue;
  77.                         Ref currentRef = repo.findRef(branchName);
  78.                         if (currentRef == null)
  79.                             continue;

  80.                         RevCommit base = walk
  81.                                 .parseCommit(repo.resolve(branchName));
  82.                         if (!walk.isMergedInto(base, tip)) {
  83.                             throw new NotMergedException();
  84.                         }
  85.                     }
  86.                 }
  87.             }
  88.             setCallable(false);
  89.             for (String branchName : branchNames) {
  90.                 if (branchName == null)
  91.                     continue;
  92.                 Ref currentRef = repo.findRef(branchName);
  93.                 if (currentRef == null)
  94.                     continue;
  95.                 String fullName = currentRef.getName();
  96.                 if (fullName.equals(currentBranch))
  97.                     throw new CannotDeleteCurrentBranchException(
  98.                             MessageFormat
  99.                                     .format(
  100.                                             JGitText.get().cannotDeleteCheckedOutBranch,
  101.                                             branchName));
  102.                 RefUpdate update = repo.updateRef(fullName);
  103.                 update.setRefLogMessage("branch deleted", false); //$NON-NLS-1$
  104.                 update.setForceUpdate(true);
  105.                 Result deleteResult = update.delete();

  106.                 boolean ok = true;
  107.                 switch (deleteResult) {
  108.                 case IO_FAILURE:
  109.                 case LOCK_FAILURE:
  110.                 case REJECTED:
  111.                     ok = false;
  112.                     break;
  113.                 default:
  114.                     break;
  115.                 }

  116.                 if (ok) {
  117.                     result.add(fullName);
  118.                     if (fullName.startsWith(Constants.R_HEADS)) {
  119.                         String shortenedName = fullName
  120.                                 .substring(Constants.R_HEADS.length());
  121.                         // remove upstream configuration if any
  122.                         final StoredConfig cfg = repo.getConfig();
  123.                         cfg.unsetSection(
  124.                                 ConfigConstants.CONFIG_BRANCH_SECTION,
  125.                                 shortenedName);
  126.                         cfg.save();
  127.                     }
  128.                 } else
  129.                     throw new JGitInternalException(MessageFormat.format(
  130.                             JGitText.get().deleteBranchUnexpectedResult,
  131.                             deleteResult.name()));
  132.             }
  133.             return result;
  134.         } catch (IOException ioe) {
  135.             throw new JGitInternalException(ioe.getMessage(), ioe);
  136.         }
  137.     }

  138.     /**
  139.      * Set the names of the branches to delete
  140.      *
  141.      * @param branchnames
  142.      *            the names of the branches to delete; if not set, this will do
  143.      *            nothing; invalid branch names will simply be ignored
  144.      * @return this instance
  145.      */
  146.     public DeleteBranchCommand setBranchNames(String... branchnames) {
  147.         checkCallable();
  148.         this.branchNames.clear();
  149.         this.branchNames.addAll(Arrays.asList(branchnames));
  150.         return this;
  151.     }

  152.     /**
  153.      * Set whether to forcefully delete branches
  154.      *
  155.      * @param force
  156.      *            <code>true</code> corresponds to the -D option,
  157.      *            <code>false</code> to the -d option (default) <br>
  158.      *            if <code>false</code> a check will be performed whether the
  159.      *            branch to be deleted is already merged into the current branch
  160.      *            and deletion will be refused in this case
  161.      * @return this instance
  162.      */
  163.     public DeleteBranchCommand setForce(boolean force) {
  164.         checkCallable();
  165.         this.force = force;
  166.         return this;
  167.     }
  168. }