Add git checkout --orphan implementation
Change-Id: I7bb583674641efed210d3cd5b86af27d7bb48e97 Signed-off-by: SATO taichi <ryushi@gmail.com>
This commit is contained in:
parent
3db6e05e52
commit
2f425cf30c
|
@ -59,7 +59,9 @@
|
||||||
|
|
||||||
import org.eclipse.jgit.api.CheckoutResult.Status;
|
import org.eclipse.jgit.api.CheckoutResult.Status;
|
||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
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.JGitInternalException;
|
||||||
|
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
|
||||||
import org.eclipse.jgit.api.errors.RefNotFoundException;
|
import org.eclipse.jgit.api.errors.RefNotFoundException;
|
||||||
import org.eclipse.jgit.dircache.DirCache;
|
import org.eclipse.jgit.dircache.DirCache;
|
||||||
import org.eclipse.jgit.dircache.DirCacheEntry;
|
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||||
|
@ -351,4 +353,98 @@ public void testUpdateSmudgedEntries() throws Exception {
|
||||||
assertEquals(size, entry.getLength());
|
assertEquals(size, entry.getLength());
|
||||||
assertEquals(mTime, entry.getLastModified());
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -158,6 +159,8 @@ private Stage(int number) {
|
||||||
|
|
||||||
private boolean createBranch = false;
|
private boolean createBranch = false;
|
||||||
|
|
||||||
|
private boolean orphan = false;
|
||||||
|
|
||||||
private CreateBranchCommand.SetupUpstreamMode upstreamMode;
|
private CreateBranchCommand.SetupUpstreamMode upstreamMode;
|
||||||
|
|
||||||
private String startPoint = null;
|
private String startPoint = null;
|
||||||
|
@ -197,8 +200,8 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
|
||||||
RefNotFoundException, InvalidRefNameException,
|
RefNotFoundException, InvalidRefNameException,
|
||||||
CheckoutConflictException {
|
CheckoutConflictException {
|
||||||
checkCallable();
|
checkCallable();
|
||||||
processOptions();
|
|
||||||
try {
|
try {
|
||||||
|
processOptions();
|
||||||
if (checkoutAllPaths || !paths.isEmpty()) {
|
if (checkoutAllPaths || !paths.isEmpty()) {
|
||||||
checkoutPaths();
|
checkoutPaths();
|
||||||
status = new CheckoutResult(Status.OK, paths);
|
status = new CheckoutResult(Status.OK, paths);
|
||||||
|
@ -219,10 +222,25 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
|
||||||
Ref headRef = repo.getRef(Constants.HEAD);
|
Ref headRef = repo.getRef(Constants.HEAD);
|
||||||
String shortHeadRef = getShortBranchName(headRef);
|
String shortHeadRef = getShortBranchName(headRef);
|
||||||
String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
|
String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
|
||||||
ObjectId branch = repo.resolve(name);
|
ObjectId branch;
|
||||||
if (branch == null)
|
if (orphan) {
|
||||||
throw new RefNotFoundException(MessageFormat.format(JGitText
|
if (startPoint == null && startCommit == null) {
|
||||||
.get().refNotResolved, name));
|
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);
|
RevWalk revWalk = new RevWalk(repo);
|
||||||
AnyObjectId headId = headRef.getObjectId();
|
AnyObjectId headId = headRef.getObjectId();
|
||||||
|
@ -256,7 +274,10 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
|
||||||
Result updateResult;
|
Result updateResult;
|
||||||
if (ref != null)
|
if (ref != null)
|
||||||
updateResult = refUpdate.link(ref.getName());
|
updateResult = refUpdate.link(ref.getName());
|
||||||
else {
|
else if (orphan) {
|
||||||
|
updateResult = refUpdate.link(getBranchName());
|
||||||
|
ref = repo.getRef(Constants.HEAD);
|
||||||
|
} else {
|
||||||
refUpdate.setNewObjectId(newCommit);
|
refUpdate.setNewObjectId(newCommit);
|
||||||
updateResult = refUpdate.forceUpdate();
|
updateResult = refUpdate.forceUpdate();
|
||||||
}
|
}
|
||||||
|
@ -465,12 +486,27 @@ private ObjectId getStartPoint() throws AmbiguousObjectException,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processOptions() throws InvalidRefNameException {
|
private void processOptions() throws InvalidRefNameException,
|
||||||
if ((!checkoutAllPaths && paths.isEmpty())
|
RefAlreadyExistsException, IOException {
|
||||||
|
if (((!checkoutAllPaths && paths.isEmpty()) || orphan)
|
||||||
&& (name == null || !Repository
|
&& (name == null || !Repository
|
||||||
.isValidRefName(Constants.R_HEADS + name)))
|
.isValidRefName(Constants.R_HEADS + name)))
|
||||||
throw new InvalidRefNameException(MessageFormat.format(JGitText
|
throw new InvalidRefNameException(MessageFormat.format(JGitText
|
||||||
.get().branchNameInvalid, name == null ? "<null>" : name)); //$NON-NLS-1$
|
.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;
|
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.
|
* Specify to force the ref update in case of a branch switch.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue