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