Support parsing previous checkout as a revision expresion.
Repository.resolve can only return an ObjectId and will continue to do so, but another method, simplify(), will be able to return a branch name for some cases. Previous checkouts can be specified as @{-n}, where n is an integer speifying the n:th previous branch. The result is the branch name, unless the checkout was a detached head, in which case the object id is returned. Since the result is a branch it may be followed by a references to the reflog, such as @{-1}@{1} if necessary. A simple expression like "master" is resolved to master in simplify, but anything starting with refs gets resolved to its object id, even if it is a branch. A symbolic ref is resolved to its leaf ref, e.g. "HEAD" might be resolved to "master". Change-Id: Ifb815a1247ba2a3e2d9c46249c09be9d47f2b693
This commit is contained in:
parent
f82d1cb5c0
commit
2a2362fbb3
|
@ -126,17 +126,36 @@ public void resolveNonExistingBranch() throws Exception {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void resolveNegativeEntryNumber() throws Exception {
|
||||
public void resolvePreviousBranch() throws Exception {
|
||||
Git git = new Git(db);
|
||||
writeTrashFile("file.txt", "content");
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
git.commit().setMessage("create file").call();
|
||||
RevCommit c1 = git.commit().setMessage("create file").call();
|
||||
writeTrashFile("file.txt", "content2");
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
RevCommit c2 = git.commit().setMessage("edit file").call();
|
||||
|
||||
git.checkout().setCreateBranch(true).setName("newbranch")
|
||||
.setStartPoint(c1).call();
|
||||
|
||||
git.checkout().setName(c1.getName()).call();
|
||||
|
||||
git.checkout().setName("master").call();
|
||||
|
||||
assertEquals(c1.getName(), db.simplify("@{-1}"));
|
||||
assertEquals("newbranch", db.simplify("@{-2}"));
|
||||
assertEquals("master", db.simplify("@{-3}"));
|
||||
|
||||
// chained expression
|
||||
try {
|
||||
db.resolve("master@{-12}");
|
||||
fail("Exception not thrown");
|
||||
// Cannot refer to reflog of detached head
|
||||
db.resolve("@{-1}@{0}");
|
||||
fail();
|
||||
} catch (RevisionSyntaxException e) {
|
||||
assertNotNull(e);
|
||||
}
|
||||
assertEquals(c1.getName(), db.resolve("@{-2}@{0}").getName());
|
||||
|
||||
assertEquals(c2.getName(), db.resolve("@{-3}@{0}").getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -231,6 +232,17 @@ public void testParseLookupPath() throws IOException {
|
|||
assertNull("no not-a-branch:", db.resolve("not-a-branch:"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveExprSimple() throws Exception {
|
||||
Git git = new Git(db);
|
||||
writeTrashFile("file.txt", "content");
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
git.commit().setMessage("create file").call();
|
||||
assertEquals("master", db.simplify("master"));
|
||||
assertEquals("refs/heads/master", db.simplify("refs/heads/master"));
|
||||
assertEquals("HEAD", db.simplify("HEAD"));
|
||||
}
|
||||
|
||||
private static ObjectId id(String name) {
|
||||
return ObjectId.fromString(name);
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
import org.eclipse.jgit.revwalk.RevObject;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.storage.file.CheckoutEntry;
|
||||
import org.eclipse.jgit.storage.file.ReflogEntry;
|
||||
import org.eclipse.jgit.storage.file.ReflogReader;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
|
@ -376,20 +377,58 @@ public ObjectId resolve(final String revstr)
|
|||
throws AmbiguousObjectException, IOException {
|
||||
RevWalk rw = new RevWalk(this);
|
||||
try {
|
||||
return resolve(rw, revstr);
|
||||
Object resolved = resolve(rw, revstr);
|
||||
if (resolved instanceof String) {
|
||||
return getRef((String) resolved).getLeaf().getObjectId();
|
||||
} else {
|
||||
return (ObjectId) resolved;
|
||||
}
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectId resolve(final RevWalk rw, final String revstr) throws IOException {
|
||||
/**
|
||||
* Simplify an expression, but unlike {@link #resolve(String)} it will not
|
||||
* resolve a branch passed or resulting from the expression, such as @{-}.
|
||||
* Thus this method can be used to process an expression to a method that
|
||||
* expects a branch or revision id.
|
||||
*
|
||||
* @param revstr
|
||||
* @return object id or ref name from resolved expression
|
||||
* @throws AmbiguousObjectException
|
||||
* @throws IOException
|
||||
*/
|
||||
public String simplify(final String revstr)
|
||||
throws AmbiguousObjectException, IOException {
|
||||
RevWalk rw = new RevWalk(this);
|
||||
try {
|
||||
Object resolved = resolve(rw, revstr);
|
||||
if (resolved != null)
|
||||
if (resolved instanceof String)
|
||||
return (String) resolved;
|
||||
else
|
||||
return ((AnyObjectId) resolved).getName();
|
||||
return null;
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
private Object resolve(final RevWalk rw, final String revstr)
|
||||
throws IOException {
|
||||
char[] revChars = revstr.toCharArray();
|
||||
RevObject rev = null;
|
||||
String name = null;
|
||||
int done = 0;
|
||||
for (int i = 0; i < revChars.length; ++i) {
|
||||
switch (revChars[i]) {
|
||||
case '^':
|
||||
if (rev == null) {
|
||||
rev = parseSimple(rw, new String(revChars, 0, i));
|
||||
if (name == null)
|
||||
name = new String(revChars, done, i);
|
||||
rev = parseSimple(rw, name);
|
||||
name = null;
|
||||
if (rev == null)
|
||||
return null;
|
||||
}
|
||||
|
@ -429,6 +468,7 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
|||
rev = commit.getParent(pnum - 1);
|
||||
}
|
||||
i = j - 1;
|
||||
done = i;
|
||||
break;
|
||||
case '{':
|
||||
int k;
|
||||
|
@ -456,6 +496,7 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
|||
throw new RevisionSyntaxException(revstr);
|
||||
else
|
||||
throw new RevisionSyntaxException(revstr);
|
||||
done = k;
|
||||
break;
|
||||
default:
|
||||
rev = rw.parseAny(rev);
|
||||
|
@ -485,7 +526,9 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
|||
break;
|
||||
case '~':
|
||||
if (rev == null) {
|
||||
rev = parseSimple(rw, new String(revChars, 0, i));
|
||||
if (name == null)
|
||||
name = new String(revChars, done, i);
|
||||
rev = parseSimple(rw, name);
|
||||
if (rev == null)
|
||||
return null;
|
||||
}
|
||||
|
@ -523,6 +566,8 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
|||
i = l - 1;
|
||||
break;
|
||||
case '@':
|
||||
if (rev != null)
|
||||
throw new RevisionSyntaxException(revstr);
|
||||
int m;
|
||||
String time = null;
|
||||
for (m = i + 2; m < revChars.length; ++m) {
|
||||
|
@ -532,47 +577,48 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
|||
}
|
||||
}
|
||||
if (time != null) {
|
||||
String refName = new String(revChars, 0, i);
|
||||
Ref ref;
|
||||
if (refName.equals("")) {
|
||||
// Currently checked out branch, HEAD if
|
||||
// detached
|
||||
ref = getRef(Constants.HEAD);
|
||||
if (time.matches("^-\\d+$")) {
|
||||
if (name != null)
|
||||
throw new RevisionSyntaxException(revstr);
|
||||
else {
|
||||
String previousCheckout = resolveReflogCheckout(-Integer
|
||||
.parseInt(time));
|
||||
if (ObjectId.isId(previousCheckout))
|
||||
rev = parseSimple(rw, previousCheckout);
|
||||
else
|
||||
name = previousCheckout;
|
||||
}
|
||||
} else {
|
||||
if (name == null)
|
||||
name = new String(revChars, done, i);
|
||||
if (name.equals(""))
|
||||
name = Constants.HEAD;
|
||||
Ref ref = getRef(name);
|
||||
if (ref == null)
|
||||
return null;
|
||||
// @{n} means current branch, not HEAD@{1} unless
|
||||
// detached
|
||||
if (ref.isSymbolic())
|
||||
ref = ref.getLeaf();
|
||||
if (ref.getObjectId() == null)
|
||||
return null;
|
||||
} else
|
||||
ref = getRef(refName);
|
||||
if (ref == null)
|
||||
return null;
|
||||
rev = resolveReflog(rw, ref, time);
|
||||
rev = resolveReflog(rw, ref, time);
|
||||
name = null;
|
||||
}
|
||||
i = m;
|
||||
} else
|
||||
i = m - 1;
|
||||
throw new RevisionSyntaxException(revstr);
|
||||
break;
|
||||
case ':': {
|
||||
RevTree tree;
|
||||
if (rev == null) {
|
||||
// We might not yet have parsed the left hand side.
|
||||
ObjectId id;
|
||||
try {
|
||||
if (i == 0)
|
||||
id = resolve(rw, Constants.HEAD);
|
||||
else
|
||||
id = resolve(rw, new String(revChars, 0, i));
|
||||
} catch (RevisionSyntaxException badSyntax) {
|
||||
throw new RevisionSyntaxException(revstr);
|
||||
}
|
||||
if (id == null)
|
||||
return null;
|
||||
tree = rw.parseTree(id);
|
||||
} else {
|
||||
tree = rw.parseTree(rev);
|
||||
if (name == null)
|
||||
name = new String(revChars, done, i);
|
||||
if (name.equals(""))
|
||||
name = Constants.HEAD;
|
||||
rev = parseSimple(rw, name);
|
||||
}
|
||||
|
||||
if (rev == null)
|
||||
return null;
|
||||
tree = rw.parseTree(rev);
|
||||
if (i == revChars.length - 1)
|
||||
return tree.copy();
|
||||
|
||||
|
@ -581,13 +627,19 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
|||
tree);
|
||||
return tw != null ? tw.getObjectId(0) : null;
|
||||
}
|
||||
|
||||
default:
|
||||
if (rev != null)
|
||||
throw new RevisionSyntaxException(revstr);
|
||||
}
|
||||
}
|
||||
return rev != null ? rev.copy() : resolveSimple(revstr);
|
||||
if (rev != null)
|
||||
return rev.copy();
|
||||
if (name != null)
|
||||
return name;
|
||||
name = revstr.substring(done);
|
||||
if (getRef(name) != null)
|
||||
return name;
|
||||
return resolveSimple(name);
|
||||
}
|
||||
|
||||
private static boolean isHex(char c) {
|
||||
|
@ -634,6 +686,19 @@ && isAllHex(revstr, dashg + 4)) {
|
|||
return null;
|
||||
}
|
||||
|
||||
private String resolveReflogCheckout(int checkoutNo)
|
||||
throws IOException {
|
||||
List<ReflogEntry> reflogEntries = new ReflogReader(this, Constants.HEAD)
|
||||
.getReverseEntries();
|
||||
for (ReflogEntry entry : reflogEntries) {
|
||||
CheckoutEntry checkout = entry.parseCheckout();
|
||||
if (checkout != null)
|
||||
if (checkoutNo-- == 1)
|
||||
return checkout.getFromBranch();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
|
||||
throws IOException {
|
||||
int number;
|
||||
|
@ -643,10 +708,7 @@ private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
|
|||
throw new RevisionSyntaxException(MessageFormat.format(
|
||||
JGitText.get().invalidReflogRevision, time));
|
||||
}
|
||||
if (number < 0)
|
||||
throw new RevisionSyntaxException(MessageFormat.format(
|
||||
JGitText.get().invalidReflogRevision, time));
|
||||
|
||||
assert number >= 0;
|
||||
ReflogReader reader = new ReflogReader(this, ref.getName());
|
||||
ReflogEntry entry = reader.getReverseEntry(number);
|
||||
if (entry == null)
|
||||
|
|
Loading…
Reference in New Issue