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}"));
|
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
|
@Test
|
||||||
public void resolveReflogParent() throws Exception {
|
public void resolveReflogParent() throws Exception {
|
||||||
Git git = new Git(db);
|
Git git = new Git(db);
|
||||||
|
@ -94,17 +126,36 @@ public void resolveNonExistingBranch() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveNegativeEntryNumber() throws Exception {
|
public void resolvePreviousBranch() throws Exception {
|
||||||
Git git = new Git(db);
|
Git git = new Git(db);
|
||||||
writeTrashFile("file.txt", "content");
|
writeTrashFile("file.txt", "content");
|
||||||
git.add().addFilepattern("file.txt").call();
|
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 {
|
try {
|
||||||
db.resolve("master@{-12}");
|
// Cannot refer to reflog of detached head
|
||||||
fail("Exception not thrown");
|
db.resolve("@{-1}@{0}");
|
||||||
|
fail();
|
||||||
} catch (RevisionSyntaxException e) {
|
} catch (RevisionSyntaxException e) {
|
||||||
assertNotNull(e);
|
|
||||||
}
|
}
|
||||||
|
assertEquals(c1.getName(), db.resolve("@{-2}@{0}").getName());
|
||||||
|
|
||||||
|
assertEquals(c2.getName(), db.resolve("@{-3}@{0}").getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -53,7 +53,9 @@
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class RepositoryResolveTest extends SampleDataRepositoryTestCase {
|
public class RepositoryResolveTest extends SampleDataRepositoryTestCase {
|
||||||
|
@ -231,6 +233,39 @@ public void testParseLookupPath() throws IOException {
|
||||||
assertNull("no not-a-branch:", db.resolve("not-a-branch:"));
|
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) {
|
private static ObjectId id(String name) {
|
||||||
return ObjectId.fromString(name);
|
return ObjectId.fromString(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -79,8 +80,11 @@
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
import org.eclipse.jgit.revwalk.RevTree;
|
import org.eclipse.jgit.revwalk.RevTree;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
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.ReflogEntry;
|
||||||
import org.eclipse.jgit.storage.file.ReflogReader;
|
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.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
import org.eclipse.jgit.util.FileUtils;
|
import org.eclipse.jgit.util.FileUtils;
|
||||||
|
@ -376,20 +380,58 @@ public ObjectId resolve(final String revstr)
|
||||||
throws AmbiguousObjectException, IOException {
|
throws AmbiguousObjectException, IOException {
|
||||||
RevWalk rw = new RevWalk(this);
|
RevWalk rw = new RevWalk(this);
|
||||||
try {
|
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 {
|
} finally {
|
||||||
rw.release();
|
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();
|
char[] revChars = revstr.toCharArray();
|
||||||
RevObject rev = null;
|
RevObject rev = null;
|
||||||
|
String name = null;
|
||||||
|
int done = 0;
|
||||||
for (int i = 0; i < revChars.length; ++i) {
|
for (int i = 0; i < revChars.length; ++i) {
|
||||||
switch (revChars[i]) {
|
switch (revChars[i]) {
|
||||||
case '^':
|
case '^':
|
||||||
if (rev == null) {
|
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)
|
if (rev == null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -429,6 +471,7 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
||||||
rev = commit.getParent(pnum - 1);
|
rev = commit.getParent(pnum - 1);
|
||||||
}
|
}
|
||||||
i = j - 1;
|
i = j - 1;
|
||||||
|
done = i;
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
int k;
|
int k;
|
||||||
|
@ -456,6 +499,7 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
||||||
throw new RevisionSyntaxException(revstr);
|
throw new RevisionSyntaxException(revstr);
|
||||||
else
|
else
|
||||||
throw new RevisionSyntaxException(revstr);
|
throw new RevisionSyntaxException(revstr);
|
||||||
|
done = k;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rev = rw.parseAny(rev);
|
rev = rw.parseAny(rev);
|
||||||
|
@ -485,7 +529,9 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
||||||
break;
|
break;
|
||||||
case '~':
|
case '~':
|
||||||
if (rev == null) {
|
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)
|
if (rev == null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -523,6 +569,8 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
||||||
i = l - 1;
|
i = l - 1;
|
||||||
break;
|
break;
|
||||||
case '@':
|
case '@':
|
||||||
|
if (rev != null)
|
||||||
|
throw new RevisionSyntaxException(revstr);
|
||||||
int m;
|
int m;
|
||||||
String time = null;
|
String time = null;
|
||||||
for (m = i + 2; m < revChars.length; ++m) {
|
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) {
|
if (time != null) {
|
||||||
String refName = new String(revChars, 0, i);
|
if (time.equals("upstream")) {
|
||||||
Ref resolved = getRefDatabase().getRef(refName);
|
if (name == null)
|
||||||
if (resolved == null)
|
name = new String(revChars, done, i);
|
||||||
return null;
|
if (name.equals(""))
|
||||||
rev = resolveReflog(rw, resolved, time);
|
// 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;
|
i = m;
|
||||||
} else
|
} else
|
||||||
i = m - 1;
|
throw new RevisionSyntaxException(revstr);
|
||||||
break;
|
break;
|
||||||
case ':': {
|
case ':': {
|
||||||
RevTree tree;
|
RevTree tree;
|
||||||
if (rev == null) {
|
if (rev == null) {
|
||||||
// We might not yet have parsed the left hand side.
|
if (name == null)
|
||||||
ObjectId id;
|
name = new String(revChars, done, i);
|
||||||
try {
|
if (name.equals(""))
|
||||||
if (i == 0)
|
name = Constants.HEAD;
|
||||||
id = resolve(rw, Constants.HEAD);
|
rev = parseSimple(rw, name);
|
||||||
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 (rev == null)
|
||||||
|
return null;
|
||||||
|
tree = rw.parseTree(rev);
|
||||||
if (i == revChars.length - 1)
|
if (i == revChars.length - 1)
|
||||||
return tree.copy();
|
return tree.copy();
|
||||||
|
|
||||||
|
@ -569,13 +668,19 @@ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOExcepti
|
||||||
tree);
|
tree);
|
||||||
return tw != null ? tw.getObjectId(0) : null;
|
return tw != null ? tw.getObjectId(0) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (rev != null)
|
if (rev != null)
|
||||||
throw new RevisionSyntaxException(revstr);
|
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) {
|
private static boolean isHex(char c) {
|
||||||
|
@ -622,6 +727,19 @@ && isAllHex(revstr, dashg + 4)) {
|
||||||
return null;
|
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)
|
private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
int number;
|
int number;
|
||||||
|
@ -631,10 +749,7 @@ private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
|
||||||
throw new RevisionSyntaxException(MessageFormat.format(
|
throw new RevisionSyntaxException(MessageFormat.format(
|
||||||
JGitText.get().invalidReflogRevision, time));
|
JGitText.get().invalidReflogRevision, time));
|
||||||
}
|
}
|
||||||
if (number < 0)
|
assert number >= 0;
|
||||||
throw new RevisionSyntaxException(MessageFormat.format(
|
|
||||||
JGitText.get().invalidReflogRevision, time));
|
|
||||||
|
|
||||||
ReflogReader reader = new ReflogReader(this, ref.getName());
|
ReflogReader reader = new ReflogReader(this, ref.getName());
|
||||||
ReflogEntry entry = reader.getReverseEntry(number);
|
ReflogEntry entry = reader.getReverseEntry(number);
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
|
|
Loading…
Reference in New Issue