diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index 21fbe0a13..6e9f85179 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -47,6 +47,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -239,4 +240,20 @@ public void testCheckoutRemoteTrackingWithoutLocalBranch() { fail(e.getMessage()); } } + + @Test + public void testDetachedHeadOnCheckout() throws JGitInternalException, + RefAlreadyExistsException, RefNotFoundException, + InvalidRefNameException, IOException { + CheckoutCommand co = git.checkout(); + co.setName("master").call(); + + String commitId = db.getRef(Constants.MASTER).getObjectId().name(); + co = git.checkout(); + co.setName(commitId).call(); + + Ref head = db.getRef(Constants.HEAD); + assertFalse(head.isSymbolic()); + assertSame(head, head.getTarget()); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java index e6f893338..24af944f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java @@ -167,6 +167,17 @@ public static enum Result { private final Ref ref; + /** + * Is this RefUpdate detaching a symbolic ref? + * + * We need this info since this.ref will normally be peeled of in case of + * detaching a symbolic ref (HEAD for example). + * + * Without this flag we cannot decide whether the ref has to be updated or + * not in case when it was a symbolic ref and the newValue == oldValue. + */ + private boolean detachingSymbolicRef; + /** * Construct a new update operation for the reference. *

@@ -253,6 +264,13 @@ public ObjectId getNewObjectId() { return newValue; } + /** + * Tells this RefUpdate that it is actually detaching a symbolic ref. + */ + public void setDetachingSymbolicRef() { + detachingSymbolicRef = true; + } + /** * Set the new value the ref will update to. * @@ -596,7 +614,7 @@ private Result updateImpl(final RevWalk walk, final Store store) newObj = safeParse(walk, newValue); oldObj = safeParse(walk, oldValue); - if (newObj == oldObj) + if (newObj == oldObj && !detachingSymbolicRef) return store.execute(Result.NO_CHANGE); if (newObj instanceof RevCommit && oldObj instanceof RevCommit) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java index e8ce2c546..c3402df67 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java @@ -491,15 +491,22 @@ void storedSymbolicRef(RefDirectoryUpdate u, long modified, String target) { public RefDirectoryUpdate newUpdate(String name, boolean detach) throws IOException { + boolean detachingSymbolicRef = false; final RefList packed = getPackedRefs(); Ref ref = readRef(name, packed); if (ref != null) ref = resolve(ref, 0, null, null, packed); if (ref == null) ref = new ObjectIdRef.Unpeeled(NEW, name, null); - else if (detach && ref.isSymbolic()) - ref = new ObjectIdRef.Unpeeled(LOOSE, name, ref.getObjectId()); - return new RefDirectoryUpdate(this, ref); + else { + detachingSymbolicRef = detach && ref.isSymbolic(); + if (detachingSymbolicRef) + ref = new ObjectIdRef.Unpeeled(LOOSE, name, ref.getObjectId()); + } + RefDirectoryUpdate refDirUpdate = new RefDirectoryUpdate(this, ref); + if (detachingSymbolicRef) + refDirUpdate.setDetachingSymbolicRef(); + return refDirUpdate; } @Override