Add "Branch" command

The need for branching becomes more pressing with pull
support: we need to make sure the upstream configuration entries
are written correctly when creating and renaming branches
(and of course are cleaned up when deleting them).
This adds support for listing, adding, deleting and renaming
branches including the more common options.

Bug: 326938
Change-Id: I00bcc19476e835d6fd78fd188acde64946c1505c
Signed-off-by: Mathias Kinzler <mathias.kinzler@sap.com>
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
This commit is contained in:
Mathias Kinzler 2010-10-08 14:01:55 +02:00 committed by Chris Aniszczyk
parent be2ddff6a7
commit 7bdef4583b
14 changed files with 1643 additions and 0 deletions

View File

@ -0,0 +1,416 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api;
import java.util.List;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NotMergedException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
public class BranchCommandTest extends RepositoryTestCase {
private Git git;
RevCommit initialCommit;
RevCommit secondCommit;
@Override
protected void setUp() throws Exception {
super.setUp();
git = new Git(db);
// checkout master
git.commit().setMessage("initial commit").call();
// commit something
writeTrashFile("Test.txt", "Hello world");
git.add().addFilepattern("Test.txt").call();
initialCommit = git.commit().setMessage("Initial commit").call();
writeTrashFile("Test.txt", "Some change");
git.add().addFilepattern("Test.txt").call();
secondCommit = git.commit().setMessage("Second commit").call();
// create a master branch
RefUpdate rup = db.updateRef("refs/heads/master");
rup.setNewObjectId(initialCommit.getId());
rup.setForceUpdate(true);
rup.update();
}
private Git setUpRepoWithRemote() throws Exception {
Repository remoteRepository = createWorkRepository();
Git remoteGit = new Git(remoteRepository);
// commit something
writeTrashFile("Test.txt", "Hello world");
remoteGit.add().addFilepattern("Test.txt").call();
initialCommit = remoteGit.commit().setMessage("Initial commit").call();
writeTrashFile("Test.txt", "Some change");
remoteGit.add().addFilepattern("Test.txt").call();
secondCommit = remoteGit.commit().setMessage("Second commit").call();
// create a master branch
RefUpdate rup = remoteRepository.updateRef("refs/heads/master");
rup.setNewObjectId(initialCommit.getId());
rup.forceUpdate();
Repository localRepository = createWorkRepository();
Git localGit = new Git(localRepository);
StoredConfig config = localRepository.getConfig();
RemoteConfig rc = new RemoteConfig(config, "origin");
rc.addURI(new URIish(remoteRepository.getDirectory().getPath()));
rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
rc.update(config);
config.save();
FetchResult res = localGit.fetch().setRemote("origin").call();
assertFalse(res.getTrackingRefUpdates().isEmpty());
rup = localRepository.updateRef("refs/heads/master");
rup.setNewObjectId(initialCommit.getId());
rup.forceUpdate();
rup = localRepository.updateRef(Constants.HEAD);
rup.link("refs/heads/master");
rup.setNewObjectId(initialCommit.getId());
rup.update();
return localGit;
}
public void testCreateAndList() throws Exception {
int localBefore;
int remoteBefore;
int allBefore;
// invalid name not allowed
try {
git.branchCreate().setName("In va lid").call();
fail("Create branch with invalid ref name should fail");
} catch (InvalidRefNameException e) {
// expected
}
// existing name not allowed w/o force
try {
git.branchCreate().setName("master").call();
fail("Create branch with existing ref name should fail");
} catch (RefAlreadyExistsException e) {
// expected
}
localBefore = git.branchList().call().size();
remoteBefore = git.branchList().setListMode(ListMode.REMOTE).call()
.size();
allBefore = git.branchList().setListMode(ListMode.ALL).call().size();
assertEquals(localBefore + remoteBefore, allBefore);
Ref newBranch = createBranch(git, "NewForTestList", false, "master",
null);
assertEquals("refs/heads/NewForTestList", newBranch.getName());
assertEquals(1, git.branchList().call().size() - localBefore);
assertEquals(0, git.branchList().setListMode(ListMode.REMOTE).call()
.size()
- remoteBefore);
assertEquals(1, git.branchList().setListMode(ListMode.ALL).call()
.size()
- allBefore);
// we can only create local branches
newBranch = createBranch(git,
"refs/remotes/origin/NewRemoteForTestList", false, "master",
null);
assertEquals("refs/heads/refs/remotes/origin/NewRemoteForTestList",
newBranch.getName());
assertEquals(2, git.branchList().call().size() - localBefore);
assertEquals(0, git.branchList().setListMode(ListMode.REMOTE).call()
.size()
- remoteBefore);
assertEquals(2, git.branchList().setListMode(ListMode.ALL).call()
.size()
- allBefore);
}
public void testCreateFromCommit() throws Exception {
Ref branch = git.branchCreate().setName("FromInitial").setStartPoint(
initialCommit).call();
assertEquals(initialCommit.getId(), branch.getObjectId());
branch = git.branchCreate().setName("FromInitial2").setStartPoint(
initialCommit.getId().name()).call();
assertEquals(initialCommit.getId(), branch.getObjectId());
try {
git.branchCreate().setName("FromInitial").setStartPoint(
secondCommit).call();
} catch (RefAlreadyExistsException e) {
// expected
}
branch = git.branchCreate().setName("FromInitial").setStartPoint(
secondCommit).setForce(true).call();
assertEquals(secondCommit.getId(), branch.getObjectId());
}
public void testCreateForce() throws Exception {
// using commits
Ref newBranch = createBranch(git, "NewForce", false, secondCommit
.getId().name(), null);
assertEquals(newBranch.getTarget().getObjectId(), secondCommit.getId());
try {
newBranch = createBranch(git, "NewForce", false, initialCommit
.getId().name(), null);
fail("Should have failed");
} catch (RefAlreadyExistsException e) {
// expected
}
newBranch = createBranch(git, "NewForce", true, initialCommit.getId()
.name(), null);
assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
git.branchDelete().setBranchNames("NewForce").call();
// using names
git.branchCreate().setName("NewForce").setStartPoint("master").call();
assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
try {
git.branchCreate().setName("NewForce").setStartPoint("master")
.call();
fail("Should have failed");
} catch (RefAlreadyExistsException e) {
// expected
}
git.branchCreate().setName("NewForce").setStartPoint("master")
.setForce(true).call();
assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
}
public void testDelete() throws Exception {
createBranch(git, "ForDelete", false, "master", null);
git.branchDelete().setBranchNames("ForDelete").call();
// now point the branch to a non-merged commit
createBranch(git, "ForDelete", false, secondCommit.getId().name(), null);
try {
git.branchDelete().setBranchNames("ForDelete").call();
fail("Deletion of a non-merged branch without force should have failed");
} catch (NotMergedException e) {
// expected
}
List<String> deleted = git.branchDelete().setBranchNames("ForDelete")
.setForce(true).call();
assertEquals(1, deleted.size());
assertEquals(Constants.R_HEADS + "ForDelete", deleted.get(0));
createBranch(git, "ForDelete", false, "master", null);
try {
createBranch(git, "ForDelete", false, "master", null);
fail("Repeated creation of same branch without force should fail");
} catch (RefAlreadyExistsException e) {
// expected
}
// change starting point
Ref newBranch = createBranch(git, "ForDelete", true, initialCommit
.name(), null);
assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
newBranch = createBranch(git, "ForDelete", true, secondCommit.name(),
null);
assertEquals(newBranch.getTarget().getObjectId(), secondCommit.getId());
git.branchDelete().setBranchNames("ForDelete").setForce(true);
try {
git.branchDelete().setBranchNames("master").call();
fail("Deletion of checked out branch without force should have failed");
} catch (CannotDeleteCurrentBranchException e) {
// expected
}
try {
git.branchDelete().setBranchNames("master").setForce(true).call();
fail("Deletion of checked out branch with force should have failed");
} catch (CannotDeleteCurrentBranchException e) {
// expected
}
}
public void testPullConfigRemoteBranch() throws Exception {
Git localGit = setUpRepoWithRemote();
Ref remote = localGit.branchList().setListMode(ListMode.REMOTE).call()
.get(0);
assertEquals("refs/remotes/origin/master", remote.getName());
// by default, we should create pull configuration
createBranch(localGit, "newFromRemote", false, remote.getName(), null);
assertEquals("origin", localGit.getRepository().getConfig().getString(
"branch", "newFromRemote", "remote"));
localGit.branchDelete().setBranchNames("newFromRemote").call();
// the pull configuration should be gone after deletion
assertNull(localGit.getRepository().getConfig().getString("branch",
"newFromRemote", "remote"));
// use --no-track
createBranch(localGit, "newFromRemote", false, remote.getName(),
SetupUpstreamMode.NOTRACK);
assertNull(localGit.getRepository().getConfig().getString("branch",
"newFromRemote", "remote"));
localGit.branchDelete().setBranchNames("newFromRemote").call();
}
public void testPullConfigLocalBranch() throws Exception {
Git localGit = setUpRepoWithRemote();
// by default, we should not create pull configuration
createBranch(localGit, "newFromMaster", false, "master", null);
assertNull(localGit.getRepository().getConfig().getString("branch",
"newFromMaster", "remote"));
localGit.branchDelete().setBranchNames("newFromMaster").call();
// use --track
createBranch(localGit, "newFromMaster", false, "master",
SetupUpstreamMode.TRACK);
assertEquals(".", localGit.getRepository().getConfig().getString(
"branch", "newFromMaster", "remote"));
localGit.branchDelete().setBranchNames("newFromMaster").call();
// the pull configuration should be gone after deletion
assertNull(localGit.getRepository().getConfig().getString("branch",
"newFromRemote", "remote"));
}
public void testPullConfigRenameLocalBranch() throws Exception {
Git localGit = setUpRepoWithRemote();
// by default, we should not create pull configuration
createBranch(localGit, "newFromMaster", false, "master", null);
assertNull(localGit.getRepository().getConfig().getString("branch",
"newFromMaster", "remote"));
localGit.branchDelete().setBranchNames("newFromMaster").call();
// use --track
createBranch(localGit, "newFromMaster", false, "master",
SetupUpstreamMode.TRACK);
assertEquals(".", localGit.getRepository().getConfig().getString(
"branch", "newFromMaster", "remote"));
localGit.branchRename().setOldName("newFromMaster").setNewName(
"renamed").call();
assertNull(".", localGit.getRepository().getConfig().getString(
"branch", "newFromMaster", "remote"));
assertEquals(".", localGit.getRepository().getConfig().getString(
"branch", "renamed", "remote"));
localGit.branchDelete().setBranchNames("renamed").call();
// the pull configuration should be gone after deletion
assertNull(localGit.getRepository().getConfig().getString("branch",
"newFromRemote", "remote"));
}
public void testRenameLocalBranch() throws Exception {
// null newName not allowed
try {
git.branchRename().call();
} catch (InvalidRefNameException e) {
// expected
}
// invalid newName not allowed
try {
git.branchRename().setNewName("In va lid").call();
} catch (InvalidRefNameException e) {
// expected
}
// not existing name not allowed
try {
git.branchRename().setOldName("notexistingbranch").setNewName(
"newname").call();
} catch (RefNotFoundException e) {
// expected
}
// create some branch
createBranch(git, "existing", false, "master", null);
// a local branch
Ref branch = createBranch(git, "fromMasterForRename", false, "master",
null);
assertEquals(Constants.R_HEADS + "fromMasterForRename", branch
.getName());
Ref renamed = git.branchRename().setOldName("fromMasterForRename")
.setNewName("newName").call();
assertEquals(Constants.R_HEADS + "newName", renamed.getName());
try {
git.branchRename().setOldName(renamed.getName()).setNewName(
"existing").call();
fail("Should have failed");
} catch (RefAlreadyExistsException e) {
// expected
}
try {
git.branchRename().setNewName("In va lid").call();
fail("Rename with invalid ref name should fail");
} catch (InvalidRefNameException e) {
// expected
}
// rename without old name and detached head not allowed
RefUpdate rup = git.getRepository().updateRef(Constants.HEAD, true);
rup.setNewObjectId(initialCommit);
rup.forceUpdate();
try {
git.branchRename().setNewName("detached").call();
} catch (DetachedHeadException e) {
// expected
}
}
public void testRenameRemoteTrackingBranch() throws Exception {
Git localGit = setUpRepoWithRemote();
Ref remoteBranch = localGit.branchList().setListMode(ListMode.REMOTE)
.call().get(0);
Ref renamed = localGit.branchRename()
.setOldName(remoteBranch.getName()).setNewName("newRemote")
.call();
assertEquals(Constants.R_REMOTES + "newRemote", renamed.getName());
}
public Ref createBranch(Git actGit, String name, boolean force,
String startPoint, SetupUpstreamMode mode)
throws JGitInternalException, RefAlreadyExistsException,
AmbiguousObjectException, RefNotFoundException,
InvalidRefNameException {
CreateBranchCommand cmd = actGit.branchCreate();
cmd.setName(name);
cmd.setForce(force);
cmd.setStartPoint(startPoint);
cmd.setUpstreamMode(mode);
return cmd.call();
}
}

View File

@ -30,6 +30,7 @@ base64InputNotProperlyPadded=Base64 input not properly padded.
baseLengthIncorrect=base length incorrect
bareRepositoryNoWorkdirAndIndex=Bare Repository has neither a working tree, nor an index
blobNotFound=Blob not found: {0}
branchNameInvalid=Branch name {0} is not allowed
blobNotFoundForPath=Blob not found: {0} for path: {1}
cannotBeCombined=Cannot be combined.
cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RefFilter {1}.
@ -40,6 +41,7 @@ cannotConvertScriptToText=Cannot convert script to text
cannotCreateConfig=cannot create config
cannotCreateDirectory=Cannot create directory {0}
cannotCreateHEAD=cannot create HEAD
cannotDeleteCheckedOutBranch=Branch {0} is checked out and can not be deleted
cannotDeleteFile=Cannot delete file: {0}
cannotDeleteStaleTrackingRef2=Cannot delete stale tracking ref {0}: {1}
cannotDeleteStaleTrackingRef=Cannot delete stale tracking ref {0}
@ -118,8 +120,11 @@ couldNotRenameTemporaryIndexFileToIndex=Could not rename temporary index file to
couldNotURLEncodeToUTF8=Could not URL encode to UTF-8
couldNotWriteFile=Could not write file {0}
countingObjects=Counting objects
createBranchFailedUnknownReason=Create branch failed for unknown reason
createBranchUnexpectedResult=Create branch returned unexpected result {0}
creatingDeltasIsNotImplemented=creating deltas is not implemented
daemonAlreadyRunning=Daemon already running
deleteBranchUnexpectedResult=Delete branch returned unexpected result {0}
deletingNotSupported=Deleting {0} not supported.
destinationIsNotAWildcard=Destination is not a wildcard.
detachedHeadDetected=HEAD is detached
@ -277,6 +282,7 @@ notFound=not found.
notValid={0} not valid
nothingToFetch=Nothing to fetch.
nothingToPush=Nothing to push.
notMergedExceptionMessage=Branch was not deleted as it has not been merged yet; use the force option to delete it anyway
objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream
objectAtPathDoesNotHaveId=Object at path "{0}" does not have an id assigned. All object ids must be assigned prior to writing a tree.
objectIsCorrupt=Object {0} is corrupt: {1}
@ -317,6 +323,8 @@ rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
readTimedOut=Read timed out
readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
receivingObjects=Receiving objects
refAlreadExists=Ref {0} already exists
refNotResolved=Ref {0} can not be resolved
refUpdateReturnCodeWas=RefUpdate return code was: {0}
reflogsNotYetSupportedByRevisionParser=reflogs not yet supported by revision parser
remoteConfigHasNoURIAssociated=Remote config "{0}" has no URIs associated
@ -324,6 +332,9 @@ remoteDoesNotHaveSpec=Remote does not have {0} available for fetch.
remoteDoesNotSupportSmartHTTPPush=remote does not support smart HTTP push
remoteHungUpUnexpectedly=remote hung up unexpectedly
remoteNameCantBeNull=Remote name can't be null.
renameBranchFailedBecauseTag=Can not rename as Ref {0} is a tag
renameBranchFailedUnknownReason=Rename failed with unknown reason
renameBranchUnexpectedResult=Unexpected rename result {0}
renamesAlreadyFound=Renames have already been found.
renamesBreakingModifies=Breaking apart modified file pairs
renamesFindingByContent=Finding renames by content similarity

View File

@ -91,6 +91,7 @@ public static JGitText get() {
/***/ public String bareRepositoryNoWorkdirAndIndex;
/***/ public String blobNotFound;
/***/ public String blobNotFoundForPath;
/***/ public String branchNameInvalid;
/***/ public String cannotBeCombined;
/***/ public String cannotCombineTreeFilterWithRevFilter;
/***/ public String cannotCommitOnARepoWithState;
@ -100,6 +101,7 @@ public static JGitText get() {
/***/ public String cannotCreateConfig;
/***/ public String cannotCreateDirectory;
/***/ public String cannotCreateHEAD;
/***/ public String cannotDeleteCheckedOutBranch;
/***/ public String cannotDeleteFile;
/***/ public String cannotDeleteStaleTrackingRef2;
/***/ public String cannotDeleteStaleTrackingRef;
@ -178,8 +180,11 @@ public static JGitText get() {
/***/ public String couldNotURLEncodeToUTF8;
/***/ public String couldNotWriteFile;
/***/ public String countingObjects;
/***/ public String createBranchFailedUnknownReason;
/***/ public String createBranchUnexpectedResult;
/***/ public String creatingDeltasIsNotImplemented;
/***/ public String daemonAlreadyRunning;
/***/ public String deleteBranchUnexpectedResult;
/***/ public String deletingNotSupported;
/***/ public String destinationIsNotAWildcard;
/***/ public String detachedHeadDetected;
@ -337,6 +342,7 @@ public static JGitText get() {
/***/ public String notValid;
/***/ public String nothingToFetch;
/***/ public String nothingToPush;
/***/ public String notMergedExceptionMessage;
/***/ public String objectAtHasBadZlibStream;
/***/ public String objectAtPathDoesNotHaveId;
/***/ public String objectIsCorrupt;
@ -377,6 +383,8 @@ public static JGitText get() {
/***/ public String readTimedOut;
/***/ public String readingObjectsFromLocalRepositoryFailed;
/***/ public String receivingObjects;
/***/ public String refAlreadExists;
/***/ public String refNotResolved;
/***/ public String refUpdateReturnCodeWas;
/***/ public String reflogsNotYetSupportedByRevisionParser;
/***/ public String remoteConfigHasNoURIAssociated;
@ -384,6 +392,9 @@ public static JGitText get() {
/***/ public String remoteDoesNotSupportSmartHTTPPush;
/***/ public String remoteHungUpUnexpectedly;
/***/ public String remoteNameCantBeNull;
/***/ public String renameBranchFailedBecauseTag;
/***/ public String renameBranchFailedUnknownReason;
/***/ public String renameBranchUnexpectedResult;
/***/ public String renamesAlreadyFound;
/***/ public String renamesBreakingModifies;
/***/ public String renamesFindingByContent;

View File

@ -0,0 +1,365 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api;
import java.io.IOException;
import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
/**
* Used to create a local branch.
*
* @see <a
* href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
* >Git documentation about Branch</a>
*/
public class CreateBranchCommand extends GitCommand<Ref> {
private String name;
private boolean force = false;
private SetupUpstreamMode upstreamMode;
private String startPoint;
private RevCommit startCommit;
/**
* The modes available for setting up the upstream configuration
* (corresponding to the --set-upstream, --track, --no-track options
*
*/
public enum SetupUpstreamMode {
/**
* Corresponds to the --track option
*/
TRACK,
/**
* Corresponds to the --no-track option
*/
NOTRACK,
/**
* Corresponds to the --set-upstream option
*/
SET_UPSTREAM;
}
/**
* @param repo
*/
protected CreateBranchCommand(Repository repo) {
super(repo);
}
/**
* @throws RefAlreadyExistsException
* when trying to create (without force) a branch with a name
* that already exists
* @throws RefNotFoundException
* if the start point can not be found
* @throws AmbiguousObjectException
* if the start point is ambiguous
* @throws InvalidRefNameException
* if the provided name is <code>null</code> or otherwise
* invalid
* @return the newly created branch
*/
public Ref call() throws JGitInternalException, RefAlreadyExistsException,
RefNotFoundException, AmbiguousObjectException,
InvalidRefNameException {
checkCallable();
processOptions();
try {
boolean exists = repo.getRef(name) != null;
if (!force && exists)
throw new RefAlreadyExistsException(MessageFormat.format(
JGitText.get().refAlreadExists, name));
ObjectId startAt = getStartPoint();
String startPointFullName = null;
if (startPoint != null) {
Ref baseRef = repo.getRef(startPoint);
if (baseRef != null)
startPointFullName = baseRef.getName();
}
// determine whether we are based on a commit,
// a branch, or a tag and compose the reflog message
String refLogMessage;
String baseBranch = "";
if (startPointFullName == null) {
String baseCommit;
if (startCommit != null)
baseCommit = startCommit.getShortMessage();
else {
RevCommit commit = new RevWalk(repo).parseCommit(repo
.resolve(startPoint));
baseCommit = commit.getShortMessage();
}
if (exists)
refLogMessage = "branch: Reset start-point to commit "
+ baseCommit;
else
refLogMessage = "branch: Created from commit " + baseCommit;
} else if (startPointFullName.startsWith(Constants.R_HEADS)
|| startPointFullName.startsWith(Constants.R_REMOTES)) {
baseBranch = startPointFullName;
if (exists)
refLogMessage = "branch: Reset start-point to branch "
+ startPointFullName; // TODO
else
refLogMessage = "branch: Created from branch " + baseBranch;
} else {
if (exists)
refLogMessage = "branch: Reset start-point to tag "
+ startPointFullName;
else
refLogMessage = "branch: Created from tag "
+ startPointFullName;
}
RefUpdate updateRef = repo.updateRef(Constants.R_HEADS + name);
updateRef.setNewObjectId(startAt);
updateRef.setRefLogMessage(refLogMessage, false);
Result updateResult;
if (exists && force)
updateResult = updateRef.forceUpdate();
else
updateResult = updateRef.update();
setCallable(false);
boolean ok = false;
switch (updateResult) {
case NEW:
ok = !exists;
break;
case NO_CHANGE:
case FAST_FORWARD:
case FORCED:
ok = exists;
break;
default:
break;
}
if (!ok)
throw new JGitInternalException(MessageFormat.format(JGitText
.get().createBranchUnexpectedResult, updateResult
.name()));
Ref result = repo.getRef(name);
if (result == null)
throw new JGitInternalException(
JGitText.get().createBranchFailedUnknownReason);
if (baseBranch.length() == 0) {
return result;
}
// if we are based on another branch, see
// if we need to configure upstream configuration: first check
// whether the setting was done explicitly
boolean doConfigure;
if (upstreamMode == SetupUpstreamMode.SET_UPSTREAM
|| upstreamMode == SetupUpstreamMode.TRACK)
// explicitly set to configure
doConfigure = true;
else if (upstreamMode == SetupUpstreamMode.NOTRACK)
// explicitly set to not configure
doConfigure = false;
else {
// if there was no explicit setting, check the configuration
String autosetupflag = repo.getConfig().getString(
ConfigConstants.CONFIG_BRANCH_SECTION, null,
ConfigConstants.CONFIG_KEY_AUTOSETUPMERGE);
if ("false".equals(autosetupflag)) {
doConfigure = false;
} else if ("always".equals(autosetupflag)) {
doConfigure = true;
} else {
// in this case, the default is to configure
// only in case the base branch was a remote branch
doConfigure = baseBranch.startsWith(Constants.R_REMOTES);
}
}
if (doConfigure) {
StoredConfig config = repo.getConfig();
String[] tokens = baseBranch.split("/", 4);
boolean isRemote = tokens[1].equals("remotes");
if (isRemote) {
// refs/remotes/<remote name>/<branch>
String remoteName = tokens[2];
String branchName = tokens[3];
config
.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
name, ConfigConstants.CONFIG_KEY_REMOTE,
remoteName);
config.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
name, ConfigConstants.CONFIG_KEY_MERGE,
Constants.R_HEADS + branchName);
} else {
// set "." as remote
config.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
name, ConfigConstants.CONFIG_KEY_REMOTE, ".");
config.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
name, ConfigConstants.CONFIG_KEY_MERGE, baseBranch);
}
config.save();
}
return result;
} catch (AmbiguousObjectException e) {
throw e;
} catch (IOException ioe) {
throw new JGitInternalException(ioe.getMessage(), ioe);
}
}
private ObjectId getStartPoint() throws AmbiguousObjectException,
RefNotFoundException, IOException {
if (startCommit != null)
return startCommit.getId();
ObjectId result = null;
try {
if (startPoint == null)
result = repo.resolve(Constants.HEAD);
result = repo.resolve(startPoint);
} catch (AmbiguousObjectException e) {
throw e;
}
if (result == null)
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved,
startPoint != null ? startPoint : Constants.HEAD));
return result;
}
private void processOptions() throws InvalidRefNameException {
if (name == null
|| !Repository.isValidRefName(Constants.R_HEADS + name))
throw new InvalidRefNameException(MessageFormat.format(JGitText
.get().branchNameInvalid, name == null ? "<null>" : name));
}
/**
* @param name
* the name of the new branch
* @return this instance
*/
public CreateBranchCommand setName(String name) {
checkCallable();
this.name = name;
return this;
}
/**
* @param force
* if <code>true</code> and the branch with the given name
* already exists, the start-point of an existing branch will be
* set to a new start-point; if false, the existing branch will
* not be changed
* @return this instance
*/
public CreateBranchCommand setForce(boolean force) {
checkCallable();
this.force = force;
return this;
}
/**
* @param startPoint
* corresponds to the start-point option; if <code>null</code>,
* the current HEAD will be used
* @return this instance
*/
public CreateBranchCommand setStartPoint(String startPoint) {
checkCallable();
this.startPoint = startPoint;
this.startCommit = null;
return this;
}
/**
* @param startPoint
* corresponds to the start-point option; if <code>null</code>,
* the current HEAD will be used
* @return this instance
*/
public CreateBranchCommand setStartPoint(RevCommit startPoint) {
checkCallable();
this.startCommit = startPoint;
this.startPoint = null;
return this;
}
/**
* @param mode
* corresponds to the --track/--no-track/--set-upstream options;
* may be <code>null</code>
* @return this instance
*/
public CreateBranchCommand setUpstreamMode(SetupUpstreamMode mode) {
checkCallable();
this.upstreamMode = mode;
return this;
}
}

View File

@ -0,0 +1,197 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NotMergedException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
/**
* Used to delete one or several branches.
*
* The result of {@link #call()} is a list with the (full) names of the deleted
* branches.
*
* Note that we don't have a setter corresponding to the -r option; remote
* tracking branches are simply deleted just like local branches.
*
* @see <a
* href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
* >Git documentation about Branch</a>
*/
public class DeleteBranchCommand extends GitCommand<List<String>> {
private final Set<String> branchNames = new HashSet<String>();
private boolean force;
/**
* @param repo
*/
protected DeleteBranchCommand(Repository repo) {
super(repo);
}
/**
* @throws NotMergedException
* when trying to delete a branch which has not been merged into
* the currently checked out branch without force
* @return the list with the (full) names of the deleted branches
*/
public List<String> call() throws JGitInternalException,
NotMergedException, CannotDeleteCurrentBranchException {
checkCallable();
List<String> result = new ArrayList<String>();
if (branchNames.isEmpty())
return result;
try {
String currentBranch = repo.getFullBranch();
if (!force) {
// check if the branches to be deleted
// are all merged into the current branch
RevWalk walk = new RevWalk(repo);
RevCommit tip = walk.parseCommit(repo.resolve(Constants.HEAD));
for (String branchName : branchNames) {
if (branchName == null)
continue;
Ref currentRef = repo.getRef(branchName);
if (currentRef == null)
continue;
RevCommit base = walk.parseCommit(repo.resolve(branchName));
if (!walk.isMergedInto(base, tip)) {
throw new NotMergedException();
}
}
}
setCallable(false);
for (String branchName : branchNames) {
if (branchName == null)
continue;
Ref currentRef = repo.getRef(branchName);
if (currentRef == null)
continue;
if (currentRef.getName().equals(currentBranch))
throw new CannotDeleteCurrentBranchException(
MessageFormat
.format(
JGitText.get().cannotDeleteCheckedOutBranch,
branchName));
RefUpdate update = repo.updateRef(currentRef.getName());
update.setRefLogMessage("branch deleted", false);
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(currentRef.getName());
// remove upstream configuration if any
repo.getConfig().unsetSection(
ConfigConstants.CONFIG_BRANCH_SECTION, branchName);
repo.getConfig().save();
} else
throw new JGitInternalException(MessageFormat.format(
JGitText.get().deleteBranchUnexpectedResult,
deleteResult.name()));
}
return result;
} catch (IOException ioe) {
throw new JGitInternalException(ioe.getMessage(), ioe);
}
}
/**
* @param branchnames
* the names of the branches to delete; if not set, this will do
* nothing; invalid branch names will simply be ignored
* @return this instance
*/
public DeleteBranchCommand setBranchNames(String... branchnames) {
checkCallable();
this.branchNames.clear();
for (String branch : branchnames)
this.branchNames.add(branch);
return this;
}
/**
* @param force
* <code>true</code> corresponds to the -D option,
* <code>false</code> to the -d option (default) <br>
* if <code>false</code> a check will be performed whether the
* branch to be deleted is already merged into the current branch
* and deletion will be refused in this case
* @return this instance
*/
public DeleteBranchCommand setForce(boolean force) {
checkCallable();
this.force = force;
return this;
}
}

View File

@ -142,6 +142,42 @@ public PullCommand pull() {
return new PullCommand(repo);
}
/**
* Returns a command object used to create branches
*
* @return a {@link CreateBranchCommand}
*/
public CreateBranchCommand branchCreate() {
return new CreateBranchCommand(repo);
}
/**
* Returns a command object used to delete branches
*
* @return a {@link DeleteBranchCommand}
*/
public DeleteBranchCommand branchDelete() {
return new DeleteBranchCommand(repo);
}
/**
* Returns a command object used to list branches
*
* @return a {@link ListBranchCommand}
*/
public ListBranchCommand branchList() {
return new ListBranchCommand(repo);
}
/**
* Returns a command object used to rename branches
*
* @return a {@link RenameBranchCommand}
*/
public RenameBranchCommand branchRename() {
return new RenameBranchCommand(repo);
}
/**
* Returns a command object to execute a {@code Add} command
*

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
/**
* Used to obtain a list of branches.
*
* @see <a
* href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
* >Git documentation about Branch</a>
*/
public class ListBranchCommand extends GitCommand<List<Ref>> {
private ListMode listMode;
/**
* The modes available for listing branches (corresponding to the -r and -a
* options)
*/
public enum ListMode {
/**
* Corresponds to the -a option (all branches)
*/
ALL,
/**
* Corresponds to the -r option (remote branches only)
*/
REMOTE;
}
/**
* @param repo
*/
protected ListBranchCommand(Repository repo) {
super(repo);
}
/**
* @throws JGitInternalException
* upon internal failure
*/
public List<Ref> call() throws JGitInternalException {
checkCallable();
Map<String, Ref> refList;
try {
if (listMode == null) {
refList = repo.getRefDatabase().getRefs(Constants.R_HEADS);
} else if (listMode == ListMode.REMOTE) {
refList = repo.getRefDatabase().getRefs(Constants.R_REMOTES);
} else {
refList = repo.getRefDatabase().getRefs(Constants.R_HEADS);
refList.putAll(repo.getRefDatabase().getRefs(
Constants.R_REMOTES));
}
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
}
List<Ref> resultRefs = new ArrayList<Ref>();
resultRefs.addAll(refList.values());
Collections.sort(resultRefs, new Comparator<Ref>() {
public int compare(Ref o1, Ref o2) {
return o1.getName().compareTo(o2.getName());
}
});
setCallable(false);
return resultRefs;
}
/**
* @param listMode
* optional: corresponds to the -r/-a options; by default, only
* local branches will be listed
* @return this instance
*/
public ListBranchCommand setListMode(ListMode listMode) {
checkCallable();
this.listMode = listMode;
return this;
}
}

View File

@ -0,0 +1,207 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api;
import java.io.IOException;
import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RefUpdate.Result;
/**
* Used to rename branches.
*
* @see <a
* href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
* >Git documentation about Branch</a>
*/
public class RenameBranchCommand extends GitCommand<Ref> {
private String oldName;
private String newName;
/**
* @param repo
*/
protected RenameBranchCommand(Repository repo) {
super(repo);
}
/**
* @throws RefNotFoundException
* if the old branch can not be found (branch with provided old
* name does not exist or old name resolves to a tag)
* @throws InvalidRefNameException
* if the provided new name is <code>null</code> or otherwise
* invalid
* @throws RefAlreadyExistsException
* if a branch with the new name already exists
* @throws DetachedHeadException
* if rename is tried without specifying the old name and HEAD
* is detached
*/
public Ref call() throws RefNotFoundException, InvalidRefNameException,
RefAlreadyExistsException, DetachedHeadException {
checkCallable();
if (newName == null)
throw new InvalidRefNameException(MessageFormat.format(JGitText
.get().branchNameInvalid, "<null>"));
try {
String fullOldName;
String fullNewName;
if (repo.getRef(newName) != null)
throw new RefAlreadyExistsException(MessageFormat.format(
JGitText.get().refAlreadExists, newName));
if (oldName != null) {
Ref ref = repo.getRef(oldName);
if (ref == null)
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved, oldName));
if (ref.getName().startsWith(Constants.R_TAGS))
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().renameBranchFailedBecauseTag,
oldName));
fullOldName = ref.getName();
} else {
fullOldName = repo.getFullBranch();
if (ObjectId.isId(fullOldName))
throw new DetachedHeadException();
}
if (fullOldName.startsWith(Constants.R_REMOTES))
fullNewName = Constants.R_REMOTES + newName;
else {
fullNewName = Constants.R_HEADS + newName;
}
if (!Repository.isValidRefName(fullNewName))
throw new InvalidRefNameException(MessageFormat.format(JGitText
.get().branchNameInvalid, fullNewName));
RefRename rename = repo.renameRef(fullOldName, fullNewName);
Result renameResult = rename.rename();
setCallable(false);
boolean ok = Result.RENAMED == renameResult;
if (ok) {
if (fullNewName.startsWith(Constants.R_HEADS)) {
// move the upstream configuration over to the new branch
String shortOldName = fullOldName
.substring(Constants.R_HEADS.length());
String oldRemote = repo.getConfig().getString(
ConfigConstants.CONFIG_BRANCH_SECTION,
shortOldName, ConfigConstants.CONFIG_KEY_REMOTE);
if (oldRemote != null) {
repo.getConfig().setString(
ConfigConstants.CONFIG_BRANCH_SECTION, newName,
ConfigConstants.CONFIG_KEY_REMOTE, oldRemote);
}
String oldMerge = repo.getConfig().getString(
ConfigConstants.CONFIG_BRANCH_SECTION,
shortOldName, ConfigConstants.CONFIG_KEY_MERGE);
if (oldMerge != null) {
repo.getConfig().setString(
ConfigConstants.CONFIG_BRANCH_SECTION, newName,
ConfigConstants.CONFIG_KEY_MERGE, oldMerge);
}
repo.getConfig()
.unsetSection(
ConfigConstants.CONFIG_BRANCH_SECTION,
shortOldName);
repo.getConfig().save();
}
} else
throw new JGitInternalException(MessageFormat.format(JGitText
.get().renameBranchUnexpectedResult, renameResult
.name()));
Ref resultRef = repo.getRef(newName);
if (resultRef == null)
throw new JGitInternalException(
JGitText.get().renameBranchFailedUnknownReason);
return resultRef;
} catch (IOException ioe) {
throw new JGitInternalException(ioe.getMessage(), ioe);
}
}
/**
* @param newName
* the new name
* @return this instance
*/
public RenameBranchCommand setNewName(String newName) {
checkCallable();
this.newName = newName;
return this;
}
/**
* @param oldName
* the name of the branch to rename; if not set, the currently
* checked out branch (if any) will be renamed
* @return this instance
*/
public RenameBranchCommand setOldName(String oldName) {
checkCallable();
this.oldName = oldName;
return this;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v1.0 which accompanies this
* distribution, is reproduced below, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api.errors;
/**
* Thrown when trying to delete a branch which is currently checked out
*/
public class CannotDeleteCurrentBranchException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* @param message
* the message
*/
public CannotDeleteCurrentBranchException(String message) {
super(message);
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v1.0 which accompanies this
* distribution, is reproduced below, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api.errors;
/**
* Exception thrown when an invalid Ref name was encountered
*/
public class InvalidRefNameException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* @param msg
*/
public InvalidRefNameException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v1.0 which accompanies this
* distribution, is reproduced below, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api.errors;
import org.eclipse.jgit.JGitText;
/**
* Thrown when branch deletion fails due to unmerged data
*/
public class NotMergedException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* The default constructor with a default message
*/
public NotMergedException() {
super(JGitText.get().notMergedExceptionMessage);
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v1.0 which accompanies this
* distribution, is reproduced below, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api.errors;
import org.eclipse.jgit.lib.Ref;
/**
* Thrown when trying to create a {@link Ref} with the same name as an exsiting
* one
*/
public class RefAlreadyExistsException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* @param message
*/
public RefAlreadyExistsException(String message) {
super(message);
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v1.0 which accompanies this
* distribution, is reproduced below, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api.errors;
/**
* Thrown when a Ref can not be resolved
*/
public class RefNotFoundException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* @param message
*/
public RefNotFoundException(String message) {
super(message);
}
}

View File

@ -87,4 +87,6 @@ public class ConfigConstants {
/** The "url" key */
public static final String CONFIG_KEY_URL = "url";
/** The "autosetupmerge" key */
public static final String CONFIG_KEY_AUTOSETUPMERGE = "autosetupmerge";
}