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;
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
+ }
+ }
+
}
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
private boolean createBranch = false;
+ private boolean orphan = false;
+
private CreateBranchCommand.SetupUpstreamMode upstreamMode;
private String startPoint = null;
RefNotFoundException, InvalidRefNameException,
CheckoutConflictException {
checkCallable();
- processOptions();
try {
+ processOptions();
if (checkoutAllPaths || !paths.isEmpty()) {
checkoutPaths();
status = new CheckoutResult(Status.OK, paths);
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();
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();
}
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;
}
/**
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.
*