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:
commit
5e21a8916c
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue