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
@@ -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() |
@@ -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 | |||
* |
@@ -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 |