Browse Source

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
tags/v2.1.0.201209190230-r
Robin Rosenberg 11 years ago
parent
commit
5e21a8916c

+ 56
- 5
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogResolveTest.java View File

@@ -71,6 +71,38 @@ public class ReflogResolveTest extends RepositoryTestCase {
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 class ReflogResolveTest extends RepositoryTestCase {
}

@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

+ 35
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryResolveTest.java View File

@@ -53,7 +53,9 @@ import static org.junit.Assert.fail;

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 class RepositoryResolveTest extends SampleDataRepositoryTestCase {
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);
}

+ 147
- 32
org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java View File

@@ -51,6 +51,7 @@ import java.io.File;
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.RevCommit;
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 abstract class Repository {
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();
}
}

/**
* 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 ObjectId resolve(final RevWalk rw, final String revstr) throws IOException {
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 @@ public abstract class Repository {
rev = commit.getParent(pnum - 1);
}
i = j - 1;
done = i;
break;
case '{':
int k;
@@ -456,6 +499,7 @@ public abstract class Repository {
throw new RevisionSyntaxException(revstr);
else
throw new RevisionSyntaxException(revstr);
done = k;
break;
default:
rev = rw.parseAny(rev);
@@ -485,7 +529,9 @@ public abstract class Repository {
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 @@ public abstract class Repository {
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 @@ public abstract class Repository {
}
}
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 @@ public abstract class Repository {
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 @@ public abstract class Repository {
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 @@ public abstract class Repository {
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…
Cancel
Save