]> source.dussan.org Git - jgit.git/commitdiff
Add git checkout --orphan implementation 95/20095/5
authorSATO taichi <ryushi@gmail.com>
Fri, 17 Jan 2014 05:57:39 +0000 (14:57 +0900)
committerSATO taichi <ryushi@gmail.com>
Fri, 17 Jan 2014 06:37:39 +0000 (15:37 +0900)
Change-Id: I7bb583674641efed210d3cd5b86af27d7bb48e97
Signed-off-by: SATO taichi <ryushi@gmail.com>
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java

index 4087fb0ddab5db8cfcb7dbd843ac53384409fc11..614cdd0cef24afc71e846312a4694b58efe2c3dc 100644 (file)
@@ -59,7 +59,9 @@ import java.io.IOException;
 
 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 class CheckoutCommandTest extends RepositoryTestCase {
                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
+               }
+       }
+
 }
index 8dfd211a0810ce3780cc8826a3a879c33f8815c6..ee37bbfb8c9c30235b6fded0fa35544f9b6d89da 100644 (file)
@@ -47,6 +47,7 @@ import java.io.File;
 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 @@ public class CheckoutCommand extends GitCommand<Ref> {
 
        private boolean createBranch = false;
 
+       private boolean orphan = false;
+
        private CreateBranchCommand.SetupUpstreamMode upstreamMode;
 
        private String startPoint = null;
@@ -197,8 +200,8 @@ public class CheckoutCommand extends GitCommand<Ref> {
                        RefNotFoundException, InvalidRefNameException,
                        CheckoutConflictException {
                checkCallable();
-               processOptions();
                try {
+                       processOptions();
                        if (checkoutAllPaths || !paths.isEmpty()) {
                                checkoutPaths();
                                status = new CheckoutResult(Status.OK, paths);
@@ -219,10 +222,25 @@ public class CheckoutCommand extends GitCommand<Ref> {
                        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 class CheckoutCommand extends GitCommand<Ref> {
                        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 @@ public class CheckoutCommand extends GitCommand<Ref> {
                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 class CheckoutCommand extends GitCommand<Ref> {
                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.
         *