Merge changes I98df46ce,Ifb815a12,I051a1724

* changes:
  Support [<ref>]@{upstream} revision syntax
  Support parsing previous checkout as a revision expresion.
  Allow a @ without branch in revision syntax
This commit is contained in:
Robin Rosenberg 2012-08-21 17:34:45 -04:00 committed by Gerrit Code Review @ Eclipse.org
commit 5e21a8916c
3 changed files with 238 additions and 37 deletions

View File

@ -71,6 +71,38 @@ public void resolveMasterCommits() throws Exception {
assertEquals(c1, db.resolve("master@{1}"));
}
@Test
public void resolveUnnamedCurrentBranchCommits() throws Exception {
Git git = new Git(db);
writeTrashFile("file.txt", "content");
git.add().addFilepattern("file.txt").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();
assertEquals(c2, db.resolve("master@{0}"));
assertEquals(c1, db.resolve("master@{1}"));
git.checkout().setCreateBranch(true).setName("newbranch")
.setStartPoint(c1).call();
// same as current branch, e.g. master
assertEquals(c1, db.resolve("@{0}"));
try {
assertEquals(c1, db.resolve("@{1}"));
fail(); // Looking at wrong ref, e.g HEAD
} catch (RevisionSyntaxException e) {
assertNotNull(e);
}
// detached head, read HEAD reflog
git.checkout().setName(c2.getName()).call();
assertEquals(c2, db.resolve("@{0}"));
assertEquals(c1, db.resolve("@{1}"));
assertEquals(c2, db.resolve("@{2}"));
}
@Test
public void resolveReflogParent() throws Exception {
Git git = new Git(db);
@ -94,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

View File

@ -53,7 +53,9 @@
import java.io.IOException;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class RepositoryResolveTest extends SampleDataRepositoryTestCase {
@ -231,6 +233,39 @@ 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"));
}
@Test
public void resolveUpstream() throws Exception {
Git git = new Git(db);
writeTrashFile("file.txt", "content");
git.add().addFilepattern("file.txt").call();
RevCommit c1 = git.commit().setMessage("create file").call();
writeTrashFile("file2.txt", "content");
RefUpdate updateRemoteRef = db.updateRef("refs/remotes/origin/main");
updateRemoteRef.setNewObjectId(c1);
updateRemoteRef.update();
db.getConfig().setString("branch", "master", "remote", "origin");
db.getConfig()
.setString("branch", "master", "merge", "refs/heads/main");
db.getConfig().setString("remote", "origin", "url",
"git://example.com/here");
db.getConfig().setString("remote", "origin", "fetch",
"+refs/heads/*:refs/remotes/origin/*");
git.add().addFilepattern("file2.txt").call();
git.commit().setMessage("create file").call();
assertEquals("refs/remotes/origin/main", db.simplify("@{upstream}"));
}
private static ObjectId id(String name) {
return ObjectId.fromString(name);
}

View File

@ -51,6 +51,7 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
@ -79,8 +80,11 @@
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.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
@ -376,20 +380,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 +471,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 +499,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 +529,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 +569,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,35 +580,86 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
}
}
if (time != null) {
String refName = new String(revChars, 0, i);
Ref resolved = getRefDatabase().getRef(refName);
if (resolved == null)
return null;
rev = resolveReflog(rw, resolved, time);
if (time.equals("upstream")) {
if (name == null)
name = new String(revChars, done, i);
if (name.equals(""))
// Currently checked out branch, HEAD if
// detached
name = Constants.HEAD;
Ref ref = getRef(name);
if (ref == null)
return null;
if (ref.isSymbolic())
ref = ref.getLeaf();
name = ref.getName();
RemoteConfig remoteConfig;
try {
remoteConfig = new RemoteConfig(getConfig(),
"origin");
} catch (URISyntaxException e) {
throw new RevisionSyntaxException(revstr);
}
String remoteBranchName = getConfig()
.getString(
ConfigConstants.CONFIG_BRANCH_SECTION,
Repository.shortenRefName(ref.getName()),
ConfigConstants.CONFIG_KEY_MERGE);
List<RefSpec> fetchRefSpecs = remoteConfig
.getFetchRefSpecs();
for (RefSpec refSpec : fetchRefSpecs) {
if (refSpec.matchSource(remoteBranchName)) {
RefSpec expandFromSource = refSpec
.expandFromSource(remoteBranchName);
name = expandFromSource.getDestination();
break;
}
}
if (name == null)
throw new RevisionSyntaxException(revstr);
} else 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();
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();
@ -569,13 +668,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) {
@ -622,6 +727,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;
@ -631,10 +749,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)