Add git checkout --orphan implementation

Change-Id: I7bb583674641efed210d3cd5b86af27d7bb48e97
Signed-off-by: SATO taichi <ryushi@gmail.com>
This commit is contained in:
SATO taichi 2014-01-17 14:57:39 +09:00
parent 3db6e05e52
commit 2f425cf30c
2 changed files with 159 additions and 8 deletions

View File

@ -59,7 +59,9 @@
import org.eclipse.jgit.api.CheckoutResult.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
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.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
@ -351,4 +353,98 @@ public void testUpdateSmudgedEntries() throws Exception {
assertEquals(size, entry.getLength());
assertEquals(mTime, entry.getLastModified());
}
@Test
public void testCheckoutOrphanBranch() throws Exception {
CheckoutCommand co = newOrphanBranchCommand();
assertCheckoutRef(co.call());
File HEAD = new File(trash, ".git/HEAD");
String headRef = read(HEAD);
assertEquals("ref: refs/heads/orphanbranch\n", headRef);
assertEquals(2, trash.list().length);
File heads = new File(trash, ".git/refs/heads");
assertEquals(2, heads.listFiles().length);
this.assertNoHead();
this.assertRepositoryCondition(1);
assertEquals(CheckoutResult.NOT_TRIED_RESULT, co.getResult());
}
private CheckoutCommand newOrphanBranchCommand() {
return git.checkout().setOrphan(true)
.setName("orphanbranch");
}
private static void assertCheckoutRef(Ref ref) {
assertNotNull(ref);
assertEquals("refs/heads/orphanbranch", ref.getTarget().getName());
}
private void assertNoHead() throws IOException {
assertNull(db.resolve("HEAD"));
}
private void assertRepositoryCondition(int files) throws GitAPIException {
org.eclipse.jgit.api.Status status = this.git.status().call();
assertFalse(status.isClean());
assertEquals(files, status.getAdded().size());
}
@Test
public void testCreateOrphanBranchWithStartCommit() throws Exception {
CheckoutCommand co = newOrphanBranchCommand();
Ref ref = co.setStartPoint(initialCommit).call();
assertCheckoutRef(ref);
assertEquals(2, trash.list().length);
this.assertNoHead();
this.assertRepositoryCondition(1);
}
@Test
public void testCreateOrphanBranchWithStartPoint() throws Exception {
CheckoutCommand co = newOrphanBranchCommand();
Ref ref = co.setStartPoint("HEAD^").call();
assertCheckoutRef(ref);
assertEquals(2, trash.list().length);
this.assertNoHead();
this.assertRepositoryCondition(1);
}
@Test
public void testInvalidRefName() throws Exception {
try {
git.checkout().setOrphan(true).setName("../invalidname").call();
fail("Should have failed");
} catch (InvalidRefNameException e) {
// except to hit here
}
}
@Test
public void testNullRefName() throws Exception {
try {
git.checkout().setOrphan(true).setName(null).call();
fail("Should have failed");
} catch (InvalidRefNameException e) {
// except to hit here
}
}
@Test
public void testAlreadyExists() throws Exception {
this.git.checkout().setCreateBranch(true).setName("orphanbranch")
.call();
this.git.checkout().setName("master").call();
try {
newOrphanBranchCommand().call();
fail("Should have failed");
} catch (RefAlreadyExistsException e) {
// except to hit here
}
}
}

View File

@ -47,6 +47,7 @@
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
@ -158,6 +159,8 @@ private Stage(int number) {
private boolean createBranch = false;
private boolean orphan = false;
private CreateBranchCommand.SetupUpstreamMode upstreamMode;
private String startPoint = null;
@ -197,8 +200,8 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
RefNotFoundException, InvalidRefNameException,
CheckoutConflictException {
checkCallable();
processOptions();
try {
processOptions();
if (checkoutAllPaths || !paths.isEmpty()) {
checkoutPaths();
status = new CheckoutResult(Status.OK, paths);
@ -219,10 +222,25 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
Ref headRef = repo.getRef(Constants.HEAD);
String shortHeadRef = getShortBranchName(headRef);
String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
ObjectId branch = repo.resolve(name);
if (branch == null)
throw new RefNotFoundException(MessageFormat.format(JGitText
.get().refNotResolved, name));
ObjectId branch;
if (orphan) {
if (startPoint == null && startCommit == null) {
Result r = repo.updateRef(Constants.HEAD).link(
getBranchName());
if (!EnumSet.of(Result.NEW, Result.FORCED).contains(r))
throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutUnexpectedResult,
r.name()));
this.status = CheckoutResult.NOT_TRIED_RESULT;
return repo.getRef(Constants.HEAD);
}
branch = getStartPoint();
} else {
branch = repo.resolve(name);
if (branch == null)
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved, name));
}
RevWalk revWalk = new RevWalk(repo);
AnyObjectId headId = headRef.getObjectId();
@ -256,7 +274,10 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
Result updateResult;
if (ref != null)
updateResult = refUpdate.link(ref.getName());
else {
else if (orphan) {
updateResult = refUpdate.link(getBranchName());
ref = repo.getRef(Constants.HEAD);
} else {
refUpdate.setNewObjectId(newCommit);
updateResult = refUpdate.forceUpdate();
}
@ -465,12 +486,27 @@ private ObjectId getStartPoint() throws AmbiguousObjectException,
return result;
}
private void processOptions() throws InvalidRefNameException {
if ((!checkoutAllPaths && paths.isEmpty())
private void processOptions() throws InvalidRefNameException,
RefAlreadyExistsException, IOException {
if (((!checkoutAllPaths && paths.isEmpty()) || orphan)
&& (name == null || !Repository
.isValidRefName(Constants.R_HEADS + name)))
throw new InvalidRefNameException(MessageFormat.format(JGitText
.get().branchNameInvalid, name == null ? "<null>" : name)); //$NON-NLS-1$
if (orphan) {
Ref refToCheck = repo.getRef(getBranchName());
if (refToCheck != null)
throw new RefAlreadyExistsException(MessageFormat.format(
JGitText.get().refAlreadyExists, name));
}
}
private String getBranchName() {
if (name.startsWith(Constants.R_REFS))
return name;
return Constants.R_HEADS + name;
}
/**
@ -516,6 +552,25 @@ public CheckoutCommand setCreateBranch(boolean createBranch) {
return this;
}
/**
* Specify whether to create a new orphan branch.
* <p>
* If <code>true</code> is used, the name of the new orphan branch must be
* set using {@link #setName(String)}. The commit at which to start the new
* orphan branch can be set using {@link #setStartPoint(String)} or
* {@link #setStartPoint(RevCommit)}; if not specified, HEAD is used.
*
* @param orphan
* if <code>true</code> a orphan branch will be created as part
* of the checkout to the specified start point
* @return this instance
*/
public CheckoutCommand setOrphan(boolean orphan) {
checkCallable();
this.orphan = orphan;
return this;
}
/**
* Specify to force the ref update in case of a branch switch.
*