DeleteBranchCommand: update config only at the end
When multiple branches were to be removed, the git config was updated after each and every branch. Newly do so only once at the end, after all branches have been deleted. Because there may be an exception after some branches have already been deleted, take care to update the config even if an exception is thrown. Bug: 451508 Change-Id: I645be8a1a59a1476d421e46933c3f7cbd0639fec Signed-off-by: Thomas Wolf <twolf@apache.org>
This commit is contained in:
parent
ecf94d1595
commit
621685d3ca
|
@ -259,6 +259,7 @@ deleteFileFailed=Could not delete file {0}
|
||||||
deletedOrphanInPackDir=Deleted orphaned file {}
|
deletedOrphanInPackDir=Deleted orphaned file {}
|
||||||
deleteRequiresZeroNewId=Delete requires new ID to be zero
|
deleteRequiresZeroNewId=Delete requires new ID to be zero
|
||||||
deleteTagUnexpectedResult=Delete tag returned unexpected result {0}
|
deleteTagUnexpectedResult=Delete tag returned unexpected result {0}
|
||||||
|
deletingBranches=Deleting branches...
|
||||||
deletingNotSupported=Deleting {0} not supported.
|
deletingNotSupported=Deleting {0} not supported.
|
||||||
depthMustBeAt1=Depth must be >= 1
|
depthMustBeAt1=Depth must be >= 1
|
||||||
depthWithUnshallow=Depth and unshallow can\'t be used together
|
depthWithUnshallow=Depth and unshallow can\'t be used together
|
||||||
|
@ -854,6 +855,7 @@ unsupportedReftableVersion=Unsupported reftable version {0}.
|
||||||
unsupportedRepositoryDescription=Repository description not supported
|
unsupportedRepositoryDescription=Repository description not supported
|
||||||
unsupportedSizesObjSizeIndex=Unsupported sizes in object-size-index
|
unsupportedSizesObjSizeIndex=Unsupported sizes in object-size-index
|
||||||
updateRequiresOldIdAndNewId=Update requires both old ID and new ID to be nonzero
|
updateRequiresOldIdAndNewId=Update requires both old ID and new ID to be nonzero
|
||||||
|
updatingConfig=Updating git config
|
||||||
updatingHeadFailed=Updating HEAD failed
|
updatingHeadFailed=Updating HEAD failed
|
||||||
updatingReferences=Updating references
|
updatingReferences=Updating references
|
||||||
updatingRefFailed=Updating the ref {0} to {1} failed. ReturnCode from RefUpdate.update() was {2}
|
updatingRefFailed=Updating the ref {0} to {1} failed. ReturnCode from RefUpdate.update() was {2}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
|
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
|
||||||
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
|
* Copyright (C) 2010, 2023 Chris Aniszczyk <caniszczyk@gmail.com> and others
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -25,6 +26,8 @@
|
||||||
import org.eclipse.jgit.internal.JGitText;
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.lib.ConfigConstants;
|
import org.eclipse.jgit.lib.ConfigConstants;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||||
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.RefUpdate.Result;
|
import org.eclipse.jgit.lib.RefUpdate.Result;
|
||||||
|
@ -47,8 +50,11 @@
|
||||||
* >Git documentation about Branch</a>
|
* >Git documentation about Branch</a>
|
||||||
*/
|
*/
|
||||||
public class DeleteBranchCommand extends GitCommand<List<String>> {
|
public class DeleteBranchCommand extends GitCommand<List<String>> {
|
||||||
|
|
||||||
private final Set<String> branchNames = new HashSet<>();
|
private final Set<String> branchNames = new HashSet<>();
|
||||||
|
|
||||||
|
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
|
||||||
|
|
||||||
private boolean force;
|
private boolean force;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,8 +72,29 @@ public List<String> call() throws GitAPIException,
|
||||||
NotMergedException, CannotDeleteCurrentBranchException {
|
NotMergedException, CannotDeleteCurrentBranchException {
|
||||||
checkCallable();
|
checkCallable();
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
if (branchNames.isEmpty())
|
Set<String> shortNames = new HashSet<>();
|
||||||
|
if (branchNames.isEmpty()) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
Exception error = null;
|
||||||
|
try {
|
||||||
|
deleteBranches(result, shortNames);
|
||||||
|
} catch (Exception e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
monitor.beginTask(JGitText.get().updatingConfig, 1);
|
||||||
|
try {
|
||||||
|
updateConfig(shortNames, error);
|
||||||
|
} finally {
|
||||||
|
monitor.update(1);
|
||||||
|
monitor.endTask();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteBranches(List<String> result,
|
||||||
|
Set<String> shortNames) throws GitAPIException, NotMergedException,
|
||||||
|
CannotDeleteCurrentBranchException {
|
||||||
try {
|
try {
|
||||||
String currentBranch = repo.getFullBranch();
|
String currentBranch = repo.getFullBranch();
|
||||||
if (!force) {
|
if (!force) {
|
||||||
|
@ -77,12 +104,13 @@ public List<String> call() throws GitAPIException,
|
||||||
RevCommit tip = walk
|
RevCommit tip = walk
|
||||||
.parseCommit(repo.resolve(Constants.HEAD));
|
.parseCommit(repo.resolve(Constants.HEAD));
|
||||||
for (String branchName : branchNames) {
|
for (String branchName : branchNames) {
|
||||||
if (branchName == null)
|
if (branchName == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
Ref currentRef = repo.findRef(branchName);
|
Ref currentRef = repo.findRef(branchName);
|
||||||
if (currentRef == null)
|
if (currentRef == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
RevCommit base = walk
|
RevCommit base = walk
|
||||||
.parseCommit(repo.resolve(branchName));
|
.parseCommit(repo.resolve(branchName));
|
||||||
if (!walk.isMergedInto(base, tip)) {
|
if (!walk.isMergedInto(base, tip)) {
|
||||||
|
@ -92,58 +120,105 @@ public List<String> call() throws GitAPIException,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setCallable(false);
|
setCallable(false);
|
||||||
for (String branchName : branchNames) {
|
monitor.start(2);
|
||||||
if (branchName == null)
|
monitor.beginTask(JGitText.get().deletingBranches,
|
||||||
continue;
|
branchNames.size());
|
||||||
Ref currentRef = repo.findRef(branchName);
|
try {
|
||||||
if (currentRef == null)
|
for (String branchName : branchNames) {
|
||||||
continue;
|
if (branchName == null) {
|
||||||
String fullName = currentRef.getName();
|
monitor.update(1);
|
||||||
if (fullName.equals(currentBranch))
|
continue;
|
||||||
throw new CannotDeleteCurrentBranchException(
|
|
||||||
MessageFormat
|
|
||||||
.format(
|
|
||||||
JGitText.get().cannotDeleteCheckedOutBranch,
|
|
||||||
branchName));
|
|
||||||
RefUpdate update = repo.updateRef(fullName);
|
|
||||||
update.setRefLogMessage("branch deleted", false); //$NON-NLS-1$
|
|
||||||
update.setForceUpdate(true);
|
|
||||||
Result deleteResult = update.delete();
|
|
||||||
|
|
||||||
boolean ok = true;
|
|
||||||
switch (deleteResult) {
|
|
||||||
case IO_FAILURE:
|
|
||||||
case LOCK_FAILURE:
|
|
||||||
case REJECTED:
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
result.add(fullName);
|
|
||||||
if (fullName.startsWith(Constants.R_HEADS)) {
|
|
||||||
String shortenedName = fullName
|
|
||||||
.substring(Constants.R_HEADS.length());
|
|
||||||
// remove upstream configuration if any
|
|
||||||
final StoredConfig cfg = repo.getConfig();
|
|
||||||
cfg.unsetSection(
|
|
||||||
ConfigConstants.CONFIG_BRANCH_SECTION,
|
|
||||||
shortenedName);
|
|
||||||
cfg.save();
|
|
||||||
}
|
}
|
||||||
} else
|
Ref currentRef = repo.findRef(branchName);
|
||||||
throw new JGitInternalException(MessageFormat.format(
|
if (currentRef == null) {
|
||||||
JGitText.get().deleteBranchUnexpectedResult,
|
monitor.update(1);
|
||||||
deleteResult.name()));
|
continue;
|
||||||
|
}
|
||||||
|
String fullName = currentRef.getName();
|
||||||
|
if (fullName.equals(currentBranch)) {
|
||||||
|
throw new CannotDeleteCurrentBranchException(
|
||||||
|
MessageFormat.format(JGitText
|
||||||
|
.get().cannotDeleteCheckedOutBranch,
|
||||||
|
branchName));
|
||||||
|
}
|
||||||
|
RefUpdate update = repo.updateRef(fullName);
|
||||||
|
update.setRefLogMessage("branch deleted", false); //$NON-NLS-1$
|
||||||
|
update.setForceUpdate(true);
|
||||||
|
Result deleteResult = update.delete();
|
||||||
|
|
||||||
|
switch (deleteResult) {
|
||||||
|
case IO_FAILURE:
|
||||||
|
case LOCK_FAILURE:
|
||||||
|
case REJECTED:
|
||||||
|
throw new JGitInternalException(MessageFormat.format(
|
||||||
|
JGitText.get().deleteBranchUnexpectedResult,
|
||||||
|
deleteResult.name()));
|
||||||
|
default:
|
||||||
|
result.add(fullName);
|
||||||
|
if (fullName.startsWith(Constants.R_HEADS)) {
|
||||||
|
shortNames.add(fullName
|
||||||
|
.substring(Constants.R_HEADS.length()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
monitor.update(1);
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
monitor.endTask();
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new JGitInternalException(ioe.getMessage(), ioe);
|
throw new JGitInternalException(ioe.getMessage(), ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateConfig(Set<String> shortNames, Exception error)
|
||||||
|
throws GitAPIException {
|
||||||
|
IOException configError = null;
|
||||||
|
if (!shortNames.isEmpty()) {
|
||||||
|
try {
|
||||||
|
// Remove upstream configurations if any
|
||||||
|
StoredConfig cfg = repo.getConfig();
|
||||||
|
boolean changed = false;
|
||||||
|
for (String branchName : shortNames) {
|
||||||
|
changed |= cfg.removeSection(
|
||||||
|
ConfigConstants.CONFIG_BRANCH_SECTION,
|
||||||
|
branchName);
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
cfg.save();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
configError = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error == null) {
|
||||||
|
if (configError != null) {
|
||||||
|
throw new JGitInternalException(configError.getMessage(),
|
||||||
|
configError);
|
||||||
|
}
|
||||||
|
} else if (error instanceof GitAPIException) {
|
||||||
|
if (configError != null) {
|
||||||
|
error.addSuppressed(configError);
|
||||||
|
}
|
||||||
|
throw (GitAPIException) error;
|
||||||
|
} else if (error instanceof RuntimeException) {
|
||||||
|
if (configError != null) {
|
||||||
|
error.addSuppressed(configError);
|
||||||
|
}
|
||||||
|
throw (RuntimeException) error;
|
||||||
|
} else {
|
||||||
|
JGitInternalException internal = new JGitInternalException(
|
||||||
|
error.getMessage(), error);
|
||||||
|
if (configError != null) {
|
||||||
|
internal.addSuppressed(configError);
|
||||||
|
}
|
||||||
|
throw internal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the names of the branches to delete
|
* Set the names of the branches to delete
|
||||||
*
|
*
|
||||||
|
@ -159,6 +234,22 @@ public DeleteBranchCommand setBranchNames(String... branchnames) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the names of the branches to delete
|
||||||
|
*
|
||||||
|
* @param branchNames
|
||||||
|
* the names of the branches to delete; if not set, this will do
|
||||||
|
* nothing; invalid branch names will simply be ignored
|
||||||
|
* @return {@code this}
|
||||||
|
* @since 6.8
|
||||||
|
*/
|
||||||
|
public DeleteBranchCommand setBranchNames(Collection<String> branchNames) {
|
||||||
|
checkCallable();
|
||||||
|
this.branchNames.clear();
|
||||||
|
this.branchNames.addAll(branchNames);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether to forcefully delete branches
|
* Set whether to forcefully delete branches
|
||||||
*
|
*
|
||||||
|
@ -175,4 +266,34 @@ public DeleteBranchCommand setForce(boolean force) {
|
||||||
this.force = force;
|
this.force = force;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the progress monitor.
|
||||||
|
*
|
||||||
|
* @return the {@link ProgressMonitor} for the delete operation
|
||||||
|
* @since 6.8
|
||||||
|
*/
|
||||||
|
public ProgressMonitor getProgressMonitor() {
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the progress monitor associated with the delete operation. By
|
||||||
|
* default, this is set to <code>NullProgressMonitor</code>
|
||||||
|
*
|
||||||
|
* @see NullProgressMonitor
|
||||||
|
* @param monitor
|
||||||
|
* a {@link ProgressMonitor}
|
||||||
|
* @return {@code this}
|
||||||
|
* @since 6.8
|
||||||
|
*/
|
||||||
|
public DeleteBranchCommand setProgressMonitor(ProgressMonitor monitor) {
|
||||||
|
checkCallable();
|
||||||
|
if (monitor == null) {
|
||||||
|
monitor = NullProgressMonitor.INSTANCE;
|
||||||
|
}
|
||||||
|
this.monitor = monitor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,6 +289,7 @@ public static JGitText get() {
|
||||||
/***/ public String deletedOrphanInPackDir;
|
/***/ public String deletedOrphanInPackDir;
|
||||||
/***/ public String deleteRequiresZeroNewId;
|
/***/ public String deleteRequiresZeroNewId;
|
||||||
/***/ public String deleteTagUnexpectedResult;
|
/***/ public String deleteTagUnexpectedResult;
|
||||||
|
/***/ public String deletingBranches;
|
||||||
/***/ public String deletingNotSupported;
|
/***/ public String deletingNotSupported;
|
||||||
/***/ public String depthMustBeAt1;
|
/***/ public String depthMustBeAt1;
|
||||||
/***/ public String depthWithUnshallow;
|
/***/ public String depthWithUnshallow;
|
||||||
|
@ -884,6 +885,7 @@ public static JGitText get() {
|
||||||
/***/ public String unsupportedRepositoryDescription;
|
/***/ public String unsupportedRepositoryDescription;
|
||||||
/***/ public String unsupportedSizesObjSizeIndex;
|
/***/ public String unsupportedSizesObjSizeIndex;
|
||||||
/***/ public String updateRequiresOldIdAndNewId;
|
/***/ public String updateRequiresOldIdAndNewId;
|
||||||
|
/***/ public String updatingConfig;
|
||||||
/***/ public String updatingHeadFailed;
|
/***/ public String updatingHeadFailed;
|
||||||
/***/ public String updatingReferences;
|
/***/ public String updatingReferences;
|
||||||
/***/ public String updatingRefFailed;
|
/***/ public String updatingRefFailed;
|
||||||
|
|
Loading…
Reference in New Issue