Browse Source
Add the ability to checkout a branch to the working tree. Bug: 330860 Change-Id: Ie06b9e799a9e1be384da0b8996efa7209b32eac3 Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>stable-0.10
Chris Aniszczyk
14 years ago
5 changed files with 410 additions and 0 deletions
@ -0,0 +1,123 @@ |
|||||||
|
/* |
||||||
|
* 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 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.Constants; |
||||||
|
import org.eclipse.jgit.lib.RefUpdate; |
||||||
|
import org.eclipse.jgit.lib.RepositoryTestCase; |
||||||
|
import org.eclipse.jgit.revwalk.RevCommit; |
||||||
|
|
||||||
|
public class CheckoutCommandTest extends RepositoryTestCase { |
||||||
|
private Git git; |
||||||
|
|
||||||
|
RevCommit initialCommit; |
||||||
|
|
||||||
|
RevCommit secondCommit; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void setUp() throws Exception { |
||||||
|
super.setUp(); |
||||||
|
git = new Git(db); |
||||||
|
// commit something
|
||||||
|
writeTrashFile("Test.txt", "Hello world"); |
||||||
|
git.add().addFilepattern("Test.txt").call(); |
||||||
|
initialCommit = git.commit().setMessage("Initial commit").call(); |
||||||
|
|
||||||
|
// create a master branch and switch to it
|
||||||
|
git.branchCreate().setName("test").call(); |
||||||
|
RefUpdate rup = db.updateRef(Constants.HEAD); |
||||||
|
rup.link("refs/heads/test"); |
||||||
|
|
||||||
|
// commit something on the test branch
|
||||||
|
writeTrashFile("Test.txt", "Some change"); |
||||||
|
git.add().addFilepattern("Test.txt").call(); |
||||||
|
secondCommit = git.commit().setMessage("Second commit").call(); |
||||||
|
} |
||||||
|
|
||||||
|
public void testSimpleCheckout() { |
||||||
|
try { |
||||||
|
git.checkout().setName("test").call(); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void testCheckout() { |
||||||
|
try { |
||||||
|
git.checkout().setName("test").call(); |
||||||
|
assertEquals("[Test.txt, mode:100644, content:Some change]", |
||||||
|
indexState(CONTENT)); |
||||||
|
git.checkout().setName("master").call(); |
||||||
|
assertEquals("[Test.txt, mode:100644, content:Hello world]", |
||||||
|
indexState(CONTENT)); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void testCreateBranchOnCheckout() throws IOException { |
||||||
|
try { |
||||||
|
git.checkout().setCreateBranch(true).setName("test2").call(); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(e.getMessage()); |
||||||
|
} |
||||||
|
assertNotNull(db.getRef("test2")); |
||||||
|
} |
||||||
|
|
||||||
|
public void testCheckoutToNonExistingBranch() throws JGitInternalException, |
||||||
|
RefAlreadyExistsException, InvalidRefNameException { |
||||||
|
try { |
||||||
|
git.checkout().setName("badbranch").call(); |
||||||
|
fail("Should have failed"); |
||||||
|
} catch (RefNotFoundException e) { |
||||||
|
// except to hit here
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,272 @@ |
|||||||
|
/* |
||||||
|
* 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.dircache.DirCacheCheckout; |
||||||
|
import org.eclipse.jgit.errors.AmbiguousObjectException; |
||||||
|
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.RefUpdate.Result; |
||||||
|
import org.eclipse.jgit.lib.Repository; |
||||||
|
import org.eclipse.jgit.revwalk.RevCommit; |
||||||
|
import org.eclipse.jgit.revwalk.RevWalk; |
||||||
|
|
||||||
|
/** |
||||||
|
* Checkout a branch to the working tree |
||||||
|
* |
||||||
|
* @see <a |
||||||
|
* href="http://www.kernel.org/pub/software/scm/git/docs/git-checkout.html" |
||||||
|
* >Git documentation about Checkout</a> |
||||||
|
*/ |
||||||
|
public class CheckoutCommand extends GitCommand<Ref> { |
||||||
|
private String name; |
||||||
|
|
||||||
|
private boolean force = false; |
||||||
|
|
||||||
|
private boolean createBranch = false; |
||||||
|
|
||||||
|
private CreateBranchCommand.SetupUpstreamMode upstreamMode; |
||||||
|
|
||||||
|
private String startPoint = Constants.HEAD; |
||||||
|
|
||||||
|
private RevCommit startCommit; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param repo |
||||||
|
*/ |
||||||
|
protected CheckoutCommand(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 or branch can not be found |
||||||
|
* @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, InvalidRefNameException { |
||||||
|
checkCallable(); |
||||||
|
processOptions(); |
||||||
|
try { |
||||||
|
|
||||||
|
if(createBranch) { |
||||||
|
Git git = new Git(repo); |
||||||
|
CreateBranchCommand command = git.branchCreate(); |
||||||
|
command.setName(name); |
||||||
|
command.setStartPoint(getStartPoint().name()); |
||||||
|
if (upstreamMode != null) |
||||||
|
command.setUpstreamMode(upstreamMode); |
||||||
|
command.call(); |
||||||
|
} |
||||||
|
|
||||||
|
RevWalk revWalk = new RevWalk(repo); |
||||||
|
Ref headRef = repo.getRef(Constants.HEAD); |
||||||
|
RevCommit headCommit = revWalk.parseCommit(headRef.getObjectId()); |
||||||
|
String refLogMessage = "checkout: moving from " |
||||||
|
+ headRef.getTarget().getName(); |
||||||
|
ObjectId branch = repo.resolve(name); |
||||||
|
Ref ref = repo.getRef(name); |
||||||
|
if (branch == null) |
||||||
|
throw new RefNotFoundException(MessageFormat.format( |
||||||
|
JGitText.get().refNotResolved, name)); |
||||||
|
|
||||||
|
RevCommit newCommit = revWalk.parseCommit(branch); |
||||||
|
|
||||||
|
DirCacheCheckout dco = new DirCacheCheckout(repo, |
||||||
|
headCommit.getTree(), repo.lockDirCache(), |
||||||
|
newCommit.getTree()); |
||||||
|
dco.setFailOnConflict(true); |
||||||
|
dco.checkout(); |
||||||
|
RefUpdate refUpdate = repo.updateRef(Constants.HEAD); |
||||||
|
refUpdate.setForceUpdate(force); |
||||||
|
refUpdate.setRefLogMessage( |
||||||
|
refLogMessage + "to " + newCommit.getName(), false); |
||||||
|
Result updateResult = refUpdate.link(ref.getName()); |
||||||
|
|
||||||
|
setCallable(false); |
||||||
|
|
||||||
|
boolean ok = false; |
||||||
|
switch (updateResult) { |
||||||
|
case NEW: |
||||||
|
ok = true; |
||||||
|
break; |
||||||
|
case NO_CHANGE: |
||||||
|
case FAST_FORWARD: |
||||||
|
case FORCED: |
||||||
|
ok = true; |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!ok) |
||||||
|
throw new JGitInternalException(MessageFormat.format( |
||||||
|
JGitText.get().checkoutUnexpectedResult, |
||||||
|
updateResult |
||||||
|
.name())); |
||||||
|
|
||||||
|
Ref result = repo.getRef(name); |
||||||
|
|
||||||
|
return result; |
||||||
|
} 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 { |
||||||
|
result = repo.resolve((startPoint == null) ? Constants.HEAD |
||||||
|
: 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 CheckoutCommand setName(String name) { |
||||||
|
checkCallable(); |
||||||
|
this.name = name; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param createBranch |
||||||
|
* if <code>true</code> a branch will be created as part of the |
||||||
|
* checkout and set to the specified start point |
||||||
|
* @return this instance |
||||||
|
*/ |
||||||
|
public CheckoutCommand setCreateBranch(boolean createBranch) { |
||||||
|
checkCallable(); |
||||||
|
this.createBranch = createBranch; |
||||||
|
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 CheckoutCommand 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 CheckoutCommand setStartPoint(String startPoint) { |
||||||
|
checkCallable(); |
||||||
|
this.startPoint = startPoint; |
||||||
|
this.startCommit = null; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param startCommit |
||||||
|
* corresponds to the start-point option; if <code>null</code>, |
||||||
|
* the current HEAD will be used |
||||||
|
* @return this instance |
||||||
|
*/ |
||||||
|
public CheckoutCommand setStartPoint(RevCommit startCommit) { |
||||||
|
checkCallable(); |
||||||
|
this.startCommit = startCommit; |
||||||
|
this.startPoint = null; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param mode |
||||||
|
* corresponds to the --track/--no-track options; may be |
||||||
|
* <code>null</code> |
||||||
|
* @return this instance |
||||||
|
*/ |
||||||
|
public CheckoutCommand setUpstreamMode( |
||||||
|
CreateBranchCommand.SetupUpstreamMode mode) { |
||||||
|
checkCallable(); |
||||||
|
this.upstreamMode = mode; |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue