Browse Source

Introduce exactRef to read a ref whose exact name is known

Unlike getRef(name), the new exactRef method does not walk the search
path.  This should produce a less confusing result than getRef when the
exact ref name is known: it will not try to resolve refs/foo/bar to
refs/heads/refs/foo/bar even when refs/foo/bar does not exist.

It can be faster than both getRefs(ALL).get(name) and getRef(name)
because it only needs to examine a single ref.

A follow-up change will introduce a findRef synonym to getRef and
deprecate getRef to make the choice a caller is making more obvious
(exactRef or findRef, with the same semantics as getRefs(ALL).get and
getRefs(ALL).findRef).

Change-Id: If1bd09bcfc9919e7976a4d77f13184ea58dcda52
Signed-off-by: Jonathan Nieder <jrn@google.com>
tags/v4.1.0.201509280440-r
Jonathan Nieder 9 years ago
parent
commit
a04d1ad2f1

+ 17
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java View File

@@ -999,6 +999,23 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
assertNull(r.getPeeledObjectId());
}

@Test
public void testExactRef_FetchHead() throws IOException {
// This is an odd special case where we need to make sure we read
// exactly the first 40 bytes of the file and nothing further on
// that line, or the remainder of the file.
write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
+ "\tnot-for-merge"
+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");

Ref r = refdir.exactRef("FETCH_HEAD");
assertFalse(r.isSymbolic());
assertEquals(A, r.getObjectId());
assertEquals("FETCH_HEAD", r.getName());
assertFalse(r.isPeeled());
assertNull(r.getPeeledObjectId());
}

@Test
public void testGetRef_AnyHeadWithGarbage() throws IOException {
write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name()

+ 50
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java View File

@@ -84,6 +84,12 @@ public class RefTest extends SampleDataRepositoryTestCase {
}
}

private void writeNewRef(String name, ObjectId value) throws IOException {
RefUpdate updateRef = db.updateRef(name);
updateRef.setNewObjectId(value);
assertEquals(RefUpdate.Result.NEW, updateRef.update());
}

@Test
public void testRemoteNames() throws Exception {
FileBasedConfig config = db.getConfig();
@@ -192,6 +198,50 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals(Storage.LOOSE, ref.getStorage());
}

@Test
public void testGetShortRef() throws IOException {
Ref ref = db.getRef("master");
assertEquals("refs/heads/master", ref.getName());
assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
}

@Test
public void testGetShortExactRef() throws IOException {
assertNull(db.getRefDatabase().exactRef("master"));

Ref ref = db.getRefDatabase().exactRef("HEAD");
assertEquals("HEAD", ref.getName());
assertEquals("refs/heads/master", ref.getTarget().getName());
assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
}

@Test
public void testRefsUnderRefs() throws IOException {
ObjectId masterId = db.resolve("refs/heads/master");
writeNewRef("refs/heads/refs/foo/bar", masterId);

assertNull(db.getRefDatabase().exactRef("refs/foo/bar"));

Ref ref = db.getRef("refs/foo/bar");
assertEquals("refs/heads/refs/foo/bar", ref.getName());
assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
}

@Test
public void testAmbiguousRefsUnderRefs() throws IOException {
ObjectId masterId = db.resolve("refs/heads/master");
writeNewRef("refs/foo/bar", masterId);
writeNewRef("refs/heads/refs/foo/bar", masterId);

Ref exactRef = db.getRefDatabase().exactRef("refs/foo/bar");
assertEquals("refs/foo/bar", exactRef.getName());
assertEquals(masterId, exactRef.getObjectId());

Ref ref = db.getRef("refs/foo/bar");
assertEquals("refs/foo/bar", ref.getName());
assertEquals(masterId, ref.getObjectId());
}

/**
* Let an "outsider" create a loose ref with the same name as a packed one
*

+ 34
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java View File

@@ -211,6 +211,9 @@ public abstract class RefDatabase {
* Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
* able to more quickly resolve a single reference name than obtaining the
* complete namespace by {@code getRefs(ALL).get(name)}.
* <p>
* To read a specific reference without using @{link #SEARCH_PATH}, see
* {@link #exactRef(String)}.
*
* @param name
* the name of the reference. May be a short name which must be
@@ -221,6 +224,36 @@ public abstract class RefDatabase {
*/
public abstract Ref getRef(String name) throws IOException;

/**
* Read a single reference.
* <p>
* Unlike {@link #getRef}, this method expects an unshortened reference
* name and does not search using the standard {@link #SEARCH_PATH}.
*
* @param name
* the unabbreviated name of the reference.
* @return the reference (if it exists); else {@code null}.
* @throws IOException
* the reference space cannot be accessed.
* @since 4.1
*/
public Ref exactRef(String name) throws IOException {
int slash = name.lastIndexOf('/');
String prefix = name.substring(0, slash + 1);
String rest = name.substring(slash + 1);
Ref result = getRefs(prefix).get(rest);
if (result != null || slash != -1) {
return result;
}

for (Ref ref : getAdditionalRefs()) {
if (name.equals(ref.getName())) {
return ref;
}
}
return null;
}

/**
* Get a section of the reference namespace.
*
@@ -242,6 +275,7 @@ public abstract class RefDatabase {
* The result list includes non-ref items such as MERGE_HEAD and
* FETCH_RESULT cast to be refs. The names of these refs are not returned by
* <code>getRefs(ALL)</code> but are accepted by {@link #getRef(String)}
* and {@link exactRef(String)}.
*
* @return a list of additional refs
* @throws IOException

Loading…
Cancel
Save