View Javadoc
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  
13  import java.io.IOException;
14  import java.text.MessageFormat;
15  import java.util.ArrayList;
16  import java.util.Arrays;
17  import java.util.HashSet;
18  import java.util.List;
19  import java.util.Set;
20  
21  import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
22  import org.eclipse.jgit.api.errors.GitAPIException;
23  import org.eclipse.jgit.api.errors.JGitInternalException;
24  import org.eclipse.jgit.api.errors.NotMergedException;
25  import org.eclipse.jgit.internal.JGitText;
26  import org.eclipse.jgit.lib.ConfigConstants;
27  import org.eclipse.jgit.lib.Constants;
28  import org.eclipse.jgit.lib.Ref;
29  import org.eclipse.jgit.lib.RefUpdate;
30  import org.eclipse.jgit.lib.RefUpdate.Result;
31  import org.eclipse.jgit.lib.Repository;
32  import org.eclipse.jgit.lib.StoredConfig;
33  import org.eclipse.jgit.revwalk.RevCommit;
34  import org.eclipse.jgit.revwalk.RevWalk;
35  
36  /**
37   * Used to delete one or several branches.
38   *
39   * The result of {@link #call()} is a list with the (full) names of the deleted
40   * branches.
41   *
42   * Note that we don't have a setter corresponding to the -r option; remote
43   * tracking branches are simply deleted just like local branches.
44   *
45   * @see <a
46   *      href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
47   *      >Git documentation about Branch</a>
48   */
49  public class DeleteBranchCommand extends GitCommand<List<String>> {
50  	private final Set<String> branchNames = new HashSet<>();
51  
52  	private boolean force;
53  
54  	/**
55  	 * Constructor for DeleteBranchCommand
56  	 *
57  	 * @param repo
58  	 *            the {@link org.eclipse.jgit.lib.Repository}
59  	 */
60  	protected DeleteBranchCommand(Repository repo) {
61  		super(repo);
62  	}
63  
64  	/** {@inheritDoc} */
65  	@Override
66  	public List<String> call() throws GitAPIException,
67  			NotMergedException, CannotDeleteCurrentBranchException {
68  		checkCallable();
69  		List<String> result = new ArrayList<>();
70  		if (branchNames.isEmpty())
71  			return result;
72  		try {
73  			String currentBranch = repo.getFullBranch();
74  			if (!force) {
75  				// check if the branches to be deleted
76  				// are all merged into the current branch
77  				try (RevWalkvWalk.html#RevWalk">RevWalk walk = new RevWalk(repo)) {
78  					RevCommit tip = walk
79  							.parseCommit(repo.resolve(Constants.HEAD));
80  					for (String branchName : branchNames) {
81  						if (branchName == null)
82  							continue;
83  						Ref currentRef = repo.findRef(branchName);
84  						if (currentRef == null)
85  							continue;
86  
87  						RevCommit base = walk
88  								.parseCommit(repo.resolve(branchName));
89  						if (!walk.isMergedInto(base, tip)) {
90  							throw new NotMergedException();
91  						}
92  					}
93  				}
94  			}
95  			setCallable(false);
96  			for (String branchName : branchNames) {
97  				if (branchName == null)
98  					continue;
99  				Ref currentRef = repo.findRef(branchName);
100 				if (currentRef == null)
101 					continue;
102 				String fullName = currentRef.getName();
103 				if (fullName.equals(currentBranch))
104 					throw new CannotDeleteCurrentBranchException(
105 							MessageFormat
106 									.format(
107 											JGitText.get().cannotDeleteCheckedOutBranch,
108 											branchName));
109 				RefUpdate update = repo.updateRef(fullName);
110 				update.setRefLogMessage("branch deleted", false); //$NON-NLS-1$
111 				update.setForceUpdate(true);
112 				Result deleteResult = update.delete();
113 
114 				boolean ok = true;
115 				switch (deleteResult) {
116 				case IO_FAILURE:
117 				case LOCK_FAILURE:
118 				case REJECTED:
119 					ok = false;
120 					break;
121 				default:
122 					break;
123 				}
124 
125 				if (ok) {
126 					result.add(fullName);
127 					if (fullName.startsWith(Constants.R_HEADS)) {
128 						String shortenedName = fullName
129 								.substring(Constants.R_HEADS.length());
130 						// remove upstream configuration if any
131 						final StoredConfig cfg = repo.getConfig();
132 						cfg.unsetSection(
133 								ConfigConstants.CONFIG_BRANCH_SECTION,
134 								shortenedName);
135 						cfg.save();
136 					}
137 				} else
138 					throw new JGitInternalException(MessageFormat.format(
139 							JGitText.get().deleteBranchUnexpectedResult,
140 							deleteResult.name()));
141 			}
142 			return result;
143 		} catch (IOException ioe) {
144 			throw new JGitInternalException(ioe.getMessage(), ioe);
145 		}
146 	}
147 
148 	/**
149 	 * Set the names of the branches to delete
150 	 *
151 	 * @param branchnames
152 	 *            the names of the branches to delete; if not set, this will do
153 	 *            nothing; invalid branch names will simply be ignored
154 	 * @return this instance
155 	 */
156 	public DeleteBranchCommand setBranchNames(String... branchnames) {
157 		checkCallable();
158 		this.branchNames.clear();
159 		this.branchNames.addAll(Arrays.asList(branchnames));
160 		return this;
161 	}
162 
163 	/**
164 	 * Set whether to forcefully delete branches
165 	 *
166 	 * @param force
167 	 *            <code>true</code> corresponds to the -D option,
168 	 *            <code>false</code> to the -d option (default) <br>
169 	 *            if <code>false</code> a check will be performed whether the
170 	 *            branch to be deleted is already merged into the current branch
171 	 *            and deletion will be refused in this case
172 	 * @return this instance
173 	 */
174 	public DeleteBranchCommand setForce(boolean force) {
175 		checkCallable();
176 		this.force = force;
177 		return this;
178 	}
179 }