Detaching HEAD when checking out the same commit.
Detaching HEAD didn't work in some corner checkout cases. If, for example, HEAD is symbolic ref to refs/heads/master, refs/heads/master is ref to commit c0ffee... then: checkout c0ffee... would leave the HEAD unchanged. The same symptom occurs when checking out a remote tracking branch or a tag that references the same commit as refs/heads/master. In the above case, the RefUpdate class didn't have enough information to decide if the update needed to detach symbolic ref because it dealt only with new/old objectIDs. Therefore, this fix introduced the RefUpdate.detachingSymbolicRef flag. Bug: 315166 Change-Id: I085c98b77ea8f9104a213978ea0d4ac6fd58f49b Signed-off-by: Sasa Zivkov <sasa.zivkov@sap.com>
This commit is contained in:
parent
55b7bd247e
commit
3a86868c08
|
@ -47,6 +47,7 @@
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@ -239,4 +240,20 @@ public void testCheckoutRemoteTrackingWithoutLocalBranch() {
|
||||||
fail(e.getMessage());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,17 @@ public static enum Result {
|
||||||
|
|
||||||
private final Ref ref;
|
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.
|
* Construct a new update operation for the reference.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -253,6 +264,13 @@ public ObjectId getNewObjectId() {
|
||||||
return newValue;
|
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.
|
* 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);
|
newObj = safeParse(walk, newValue);
|
||||||
oldObj = safeParse(walk, oldValue);
|
oldObj = safeParse(walk, oldValue);
|
||||||
if (newObj == oldObj)
|
if (newObj == oldObj && !detachingSymbolicRef)
|
||||||
return store.execute(Result.NO_CHANGE);
|
return store.execute(Result.NO_CHANGE);
|
||||||
|
|
||||||
if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
|
if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
|
||||||
|
|
|
@ -491,15 +491,22 @@ void storedSymbolicRef(RefDirectoryUpdate u, long modified, String target) {
|
||||||
|
|
||||||
public RefDirectoryUpdate newUpdate(String name, boolean detach)
|
public RefDirectoryUpdate newUpdate(String name, boolean detach)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
boolean detachingSymbolicRef = false;
|
||||||
final RefList<Ref> packed = getPackedRefs();
|
final RefList<Ref> packed = getPackedRefs();
|
||||||
Ref ref = readRef(name, packed);
|
Ref ref = readRef(name, packed);
|
||||||
if (ref != null)
|
if (ref != null)
|
||||||
ref = resolve(ref, 0, null, null, packed);
|
ref = resolve(ref, 0, null, null, packed);
|
||||||
if (ref == null)
|
if (ref == null)
|
||||||
ref = new ObjectIdRef.Unpeeled(NEW, name, null);
|
ref = new ObjectIdRef.Unpeeled(NEW, name, null);
|
||||||
else if (detach && ref.isSymbolic())
|
else {
|
||||||
ref = new ObjectIdRef.Unpeeled(LOOSE, name, ref.getObjectId());
|
detachingSymbolicRef = detach && ref.isSymbolic();
|
||||||
return new RefDirectoryUpdate(this, ref);
|
if (detachingSymbolicRef)
|
||||||
|
ref = new ObjectIdRef.Unpeeled(LOOSE, name, ref.getObjectId());
|
||||||
|
}
|
||||||
|
RefDirectoryUpdate refDirUpdate = new RefDirectoryUpdate(this, ref);
|
||||||
|
if (detachingSymbolicRef)
|
||||||
|
refDirUpdate.setDetachingSymbolicRef();
|
||||||
|
return refDirUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue