summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test
diff options
context:
space:
mode:
authorShawn O. Pearce <spearce@spearce.org>2010-01-22 14:54:40 -0800
committerShawn O. Pearce <spearce@spearce.org>2010-01-23 11:10:12 -0800
commit01b5392cdbc12ce2e21fd1d1afbd61fdf97e1c38 (patch)
tree56cef587a18e323d04daadbca6927919bbb41d1a /org.eclipse.jgit.test
parentab697ff18b09e5d49e028b7a32844d408b02ccf2 (diff)
downloadjgit-01b5392cdbc12ce2e21fd1d1afbd61fdf97e1c38.tar.gz
jgit-01b5392cdbc12ce2e21fd1d1afbd61fdf97e1c38.zip
Rewrite reference handling to be abstract and accurate
This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Diffstat (limited to 'org.eclipse.jgit.test')
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java115
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java1024
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java37
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java78
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java129
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java22
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java27
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java92
11 files changed, 1481 insertions, 81 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java
new file mode 100644
index 0000000000..1774a428d4
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import junit.framework.TestCase;
+
+public class ObjectIdRefTest extends TestCase {
+ private static final ObjectId ID_A = ObjectId
+ .fromString("41eb0d88f833b558bddeb269b7ab77399cdf98ed");
+
+ private static final ObjectId ID_B = ObjectId
+ .fromString("698dd0b8d0c299f080559a1cffc7fe029479a408");
+
+ private static final String name = "refs/heads/a.test.ref";
+
+ public void testConstructor_PeeledStatusNotKnown() {
+ ObjectIdRef r;
+
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
+ assertSame(Ref.Storage.LOOSE, r.getStorage());
+ assertSame(name, r.getName());
+ assertSame(ID_A, r.getObjectId());
+ assertFalse("not peeled", r.isPeeled());
+ assertNull("no peel id", r.getPeeledObjectId());
+ assertSame("leaf is this", r, r.getLeaf());
+ assertSame("target is this", r, r.getTarget());
+ assertFalse("not symbolic", r.isSymbolic());
+
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, name, ID_A);
+ assertSame(Ref.Storage.PACKED, r.getStorage());
+
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE_PACKED, name, ID_A);
+ assertSame(Ref.Storage.LOOSE_PACKED, r.getStorage());
+
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, name, null);
+ assertSame(Ref.Storage.NEW, r.getStorage());
+ assertSame(name, r.getName());
+ assertNull("no id on new ref", r.getObjectId());
+ assertFalse("not peeled", r.isPeeled());
+ assertNull("no peel id", r.getPeeledObjectId());
+ assertSame("leaf is this", r, r.getLeaf());
+ assertSame("target is this", r, r.getTarget());
+ assertFalse("not symbolic", r.isSymbolic());
+ }
+
+ public void testConstructor_Peeled() {
+ ObjectIdRef r;
+
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
+ assertSame(Ref.Storage.LOOSE, r.getStorage());
+ assertSame(name, r.getName());
+ assertSame(ID_A, r.getObjectId());
+ assertFalse("not peeled", r.isPeeled());
+ assertNull("no peel id", r.getPeeledObjectId());
+ assertSame("leaf is this", r, r.getLeaf());
+ assertSame("target is this", r, r.getTarget());
+ assertFalse("not symbolic", r.isSymbolic());
+
+ r = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, name, ID_A);
+ assertTrue("is peeled", r.isPeeled());
+ assertNull("no peel id", r.getPeeledObjectId());
+
+ r = new ObjectIdRef.PeeledTag(Ref.Storage.LOOSE, name, ID_A, ID_B);
+ assertTrue("is peeled", r.isPeeled());
+ assertSame(ID_B, r.getPeeledObjectId());
+ }
+
+ public void testToString() {
+ ObjectIdRef r;
+
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
+ assertEquals("Ref[" + name + "=" + ID_A.name() + "]", r.toString());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java
new file mode 100644
index 0000000000..a2812901bc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import static org.eclipse.jgit.lib.Constants.R_TAGS;
+import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTag;
+
+public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
+ private Repository diskRepo;
+
+ private TestRepository repo;
+
+ private RefDirectory refdir;
+
+ private RevCommit A;
+
+ private RevCommit B;
+
+ private RevTag v1_0;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ diskRepo = createBareRepository();
+ refdir = (RefDirectory) diskRepo.getRefDatabase();
+
+ repo = new TestRepository(diskRepo);
+ A = repo.commit().create();
+ B = repo.commit(repo.getRevWalk().parseCommit(A));
+ v1_0 = repo.tag("v1_0", B);
+ repo.getRevWalk().parseBody(v1_0);
+ }
+
+ public void testCreate() throws IOException {
+ // setUp above created the directory. We just have to test it.
+ File d = diskRepo.getDirectory();
+ assertSame(diskRepo, refdir.getRepository());
+
+ assertTrue(new File(d, "refs").isDirectory());
+ assertTrue(new File(d, "logs").isDirectory());
+ assertTrue(new File(d, "logs/refs").isDirectory());
+ assertFalse(new File(d, "packed-refs").exists());
+
+ assertTrue(new File(d, "refs/heads").isDirectory());
+ assertTrue(new File(d, "refs/tags").isDirectory());
+ assertEquals(2, new File(d, "refs").list().length);
+ assertEquals(0, new File(d, "refs/heads").list().length);
+ assertEquals(0, new File(d, "refs/tags").list().length);
+
+ assertTrue(new File(d, "logs/refs/heads").isDirectory());
+ assertFalse(new File(d, "logs/HEAD").exists());
+ assertEquals(0, new File(d, "logs/refs/heads").list().length);
+
+ assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
+ }
+
+ public void testGetRefs_EmptyDatabase() throws IOException {
+ Map<String, Ref> all;
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertTrue("no references", all.isEmpty());
+
+ all = refdir.getRefs(R_HEADS);
+ assertTrue("no references", all.isEmpty());
+
+ all = refdir.getRefs(R_TAGS);
+ assertTrue("no references", all.isEmpty());
+ }
+
+ public void testGetRefs_HeadOnOneBranch() throws IOException {
+ Map<String, Ref> all;
+ Ref head, master;
+
+ writeLooseRef("refs/heads/master", A);
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(2, all.size());
+ assertTrue("has HEAD", all.containsKey(HEAD));
+ assertTrue("has master", all.containsKey("refs/heads/master"));
+
+ head = all.get(HEAD);
+ master = all.get("refs/heads/master");
+
+ assertEquals(HEAD, head.getName());
+ assertTrue(head.isSymbolic());
+ assertSame(LOOSE, head.getStorage());
+ assertSame("uses same ref as target", master, head.getTarget());
+
+ assertEquals("refs/heads/master", master.getName());
+ assertFalse(master.isSymbolic());
+ assertSame(LOOSE, master.getStorage());
+ assertEquals(A, master.getObjectId());
+ }
+
+ public void testGetRefs_DeatchedHead1() throws IOException {
+ Map<String, Ref> all;
+ Ref head;
+
+ writeLooseRef(HEAD, A);
+ BUG_WorkAroundRacyGitIssues(HEAD);
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(1, all.size());
+ assertTrue("has HEAD", all.containsKey(HEAD));
+
+ head = all.get(HEAD);
+
+ assertEquals(HEAD, head.getName());
+ assertFalse(head.isSymbolic());
+ assertSame(LOOSE, head.getStorage());
+ assertEquals(A, head.getObjectId());
+ }
+
+ public void testGetRefs_DeatchedHead2() throws IOException {
+ Map<String, Ref> all;
+ Ref head, master;
+
+ writeLooseRef(HEAD, A);
+ writeLooseRef("refs/heads/master", B);
+ BUG_WorkAroundRacyGitIssues(HEAD);
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(2, all.size());
+
+ head = all.get(HEAD);
+ master = all.get("refs/heads/master");
+
+ assertEquals(HEAD, head.getName());
+ assertFalse(head.isSymbolic());
+ assertSame(LOOSE, head.getStorage());
+ assertEquals(A, head.getObjectId());
+
+ assertEquals("refs/heads/master", master.getName());
+ assertFalse(master.isSymbolic());
+ assertSame(LOOSE, master.getStorage());
+ assertEquals(B, master.getObjectId());
+ }
+
+ public void testGetRefs_DeeplyNestedBranch() throws IOException {
+ String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k";
+ Map<String, Ref> all;
+ Ref r;
+
+ writeLooseRef(name, A);
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(1, all.size());
+
+ r = all.get(name);
+ assertEquals(name, r.getName());
+ assertFalse(r.isSymbolic());
+ assertSame(LOOSE, r.getStorage());
+ assertEquals(A, r.getObjectId());
+ }
+
+ public void testGetRefs_HeadBranchNotBorn() throws IOException {
+ Map<String, Ref> all;
+ Ref a, b;
+
+ writeLooseRef("refs/heads/A", A);
+ writeLooseRef("refs/heads/B", B);
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(2, all.size());
+ assertFalse("no HEAD", all.containsKey(HEAD));
+
+ a = all.get("refs/heads/A");
+ b = all.get("refs/heads/B");
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(B, b.getObjectId());
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/heads/B", b.getName());
+ }
+
+ public void testGetRefs_LooseOverridesPacked() throws IOException {
+ Map<String, Ref> heads;
+ Ref a;
+
+ writeLooseRef("refs/heads/master", B);
+ writePackedRef("refs/heads/master", A);
+
+ heads = refdir.getRefs(R_HEADS);
+ assertEquals(1, heads.size());
+
+ a = heads.get("master");
+ assertEquals("refs/heads/master", a.getName());
+ assertEquals(B, a.getObjectId());
+ }
+
+ public void testGetRefs_IgnoresGarbageRef1() throws IOException {
+ Map<String, Ref> heads;
+ Ref a;
+
+ writeLooseRef("refs/heads/A", A);
+ write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
+
+ heads = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(1, heads.size());
+
+ a = heads.get("refs/heads/A");
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+ }
+
+ public void testGetRefs_IgnoresGarbageRef2() throws IOException {
+ Map<String, Ref> heads;
+ Ref a;
+
+ writeLooseRef("refs/heads/A", A);
+ write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "");
+
+ heads = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(1, heads.size());
+
+ a = heads.get("refs/heads/A");
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+ }
+
+ public void testGetRefs_IgnoresGarbageRef3() throws IOException {
+ Map<String, Ref> heads;
+ Ref a;
+
+ writeLooseRef("refs/heads/A", A);
+ write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "\n");
+
+ heads = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(1, heads.size());
+
+ a = heads.get("refs/heads/A");
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+ }
+
+ public void testGetRefs_IgnoresGarbageRef4() throws IOException {
+ Map<String, Ref> heads;
+ Ref a, b, c;
+
+ writeLooseRef("refs/heads/A", A);
+ writeLooseRef("refs/heads/B", B);
+ writeLooseRef("refs/heads/C", A);
+ heads = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(3, heads.size());
+ assertTrue(heads.containsKey("refs/heads/A"));
+ assertTrue(heads.containsKey("refs/heads/B"));
+ assertTrue(heads.containsKey("refs/heads/C"));
+
+ writeLooseRef("refs/heads/B", "FAIL\n");
+ BUG_WorkAroundRacyGitIssues("refs/heads/B");
+
+ heads = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(2, heads.size());
+
+ a = heads.get("refs/heads/A");
+ b = heads.get("refs/heads/B");
+ c = heads.get("refs/heads/C");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+
+ assertNull("no refs/heads/B", b);
+
+ assertEquals("refs/heads/C", c.getName());
+ assertEquals(A, c.getObjectId());
+ }
+
+ public void testGetRefs_InvalidName() throws IOException {
+ writeLooseRef("refs/heads/A", A);
+
+ assertTrue("empty refs/heads", refdir.getRefs("refs/heads").isEmpty());
+ assertTrue("empty objects", refdir.getRefs("objects").isEmpty());
+ assertTrue("empty objects/", refdir.getRefs("objects/").isEmpty());
+ }
+
+ public void testGetRefs_HeadsOnly_AllLoose() throws IOException {
+ Map<String, Ref> heads;
+ Ref a, b;
+
+ writeLooseRef("refs/heads/A", A);
+ writeLooseRef("refs/heads/B", B);
+ writeLooseRef("refs/tags/v1.0", v1_0);
+
+ heads = refdir.getRefs(R_HEADS);
+ assertEquals(2, heads.size());
+
+ a = heads.get("A");
+ b = heads.get("B");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/heads/B", b.getName());
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(B, b.getObjectId());
+ }
+
+ public void testGetRefs_HeadsOnly_AllPacked1() throws IOException {
+ Map<String, Ref> heads;
+ Ref a;
+
+ deleteLooseRef(HEAD);
+ writePackedRef("refs/heads/A", A);
+
+ heads = refdir.getRefs(R_HEADS);
+ assertEquals(1, heads.size());
+
+ a = heads.get("A");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+ }
+
+ public void testGetRefs_HeadsOnly_SymrefToPacked() throws IOException {
+ Map<String, Ref> heads;
+ Ref master, other;
+
+ writeLooseRef("refs/heads/other", "ref: refs/heads/master\n");
+ writePackedRef("refs/heads/master", A);
+
+ heads = refdir.getRefs(R_HEADS);
+ assertEquals(2, heads.size());
+
+ master = heads.get("master");
+ other = heads.get("other");
+
+ assertEquals("refs/heads/master", master.getName());
+ assertEquals(A, master.getObjectId());
+
+ assertEquals("refs/heads/other", other.getName());
+ assertEquals(A, other.getObjectId());
+ assertSame(master, other.getTarget());
+ }
+
+ public void testGetRefs_HeadsOnly_Mixed() throws IOException {
+ Map<String, Ref> heads;
+ Ref a, b;
+
+ writeLooseRef("refs/heads/A", A);
+ writeLooseRef("refs/heads/B", B);
+ writePackedRef("refs/tags/v1.0", v1_0);
+
+ heads = refdir.getRefs(R_HEADS);
+ assertEquals(2, heads.size());
+
+ a = heads.get("A");
+ b = heads.get("B");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/heads/B", b.getName());
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(B, b.getObjectId());
+ }
+
+ public void testGetRefs_TagsOnly_AllLoose() throws IOException {
+ Map<String, Ref> tags;
+ Ref a;
+
+ writeLooseRef("refs/heads/A", A);
+ writeLooseRef("refs/tags/v1.0", v1_0);
+
+ tags = refdir.getRefs(R_TAGS);
+ assertEquals(1, tags.size());
+
+ a = tags.get("v1.0");
+
+ assertEquals("refs/tags/v1.0", a.getName());
+ assertEquals(v1_0, a.getObjectId());
+ }
+
+ public void testGetRefs_TagsOnly_AllPacked() throws IOException {
+ Map<String, Ref> tags;
+ Ref a;
+
+ deleteLooseRef(HEAD);
+ writePackedRef("refs/tags/v1.0", v1_0);
+
+ tags = refdir.getRefs(R_TAGS);
+ assertEquals(1, tags.size());
+
+ a = tags.get("v1.0");
+
+ assertEquals("refs/tags/v1.0", a.getName());
+ assertEquals(v1_0, a.getObjectId());
+ }
+
+ public void testGetRefs_DiscoversNewLoose1() throws IOException {
+ Map<String, Ref> orig, next;
+ Ref orig_r, next_r;
+
+ writeLooseRef("refs/heads/master", A);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ writeLooseRef("refs/heads/next", B);
+ next = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(2, orig.size());
+ assertEquals(3, next.size());
+
+ assertFalse(orig.containsKey("refs/heads/next"));
+ assertTrue(next.containsKey("refs/heads/next"));
+
+ orig_r = orig.get("refs/heads/master");
+ next_r = next.get("refs/heads/master");
+ assertEquals(A, orig_r.getObjectId());
+ assertSame("uses cached instance", orig_r, next_r);
+ assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
+ assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
+
+ next_r = next.get("refs/heads/next");
+ assertSame(LOOSE, next_r.getStorage());
+ assertEquals(B, next_r.getObjectId());
+ }
+
+ public void testGetRefs_DiscoversNewLoose2() throws IOException {
+ Map<String, Ref> orig, next, news;
+
+ writeLooseRef("refs/heads/pu", A);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ writeLooseRef("refs/heads/new/B", B);
+ news = refdir.getRefs("refs/heads/new/");
+ next = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(1, orig.size());
+ assertEquals(2, next.size());
+ assertEquals(1, news.size());
+
+ assertTrue(orig.containsKey("refs/heads/pu"));
+ assertTrue(next.containsKey("refs/heads/pu"));
+ assertFalse(news.containsKey("refs/heads/pu"));
+
+ assertFalse(orig.containsKey("refs/heads/new/B"));
+ assertTrue(next.containsKey("refs/heads/new/B"));
+ assertTrue(news.containsKey("B"));
+ }
+
+ public void testGetRefs_DiscoversModifiedLoose() throws IOException {
+ Map<String, Ref> all;
+
+ writeLooseRef("refs/heads/master", A);
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(A, all.get(HEAD).getObjectId());
+
+ writeLooseRef("refs/heads/master", B);
+ BUG_WorkAroundRacyGitIssues("refs/heads/master");
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(B, all.get(HEAD).getObjectId());
+ }
+
+ public void testGetRef_DiscoversModifiedLoose() throws IOException {
+ Map<String, Ref> all;
+
+ writeLooseRef("refs/heads/master", A);
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(A, all.get(HEAD).getObjectId());
+
+ writeLooseRef("refs/heads/master", B);
+ BUG_WorkAroundRacyGitIssues("refs/heads/master");
+
+ Ref master = refdir.getRef("refs/heads/master");
+ assertEquals(B, master.getObjectId());
+ }
+
+ public void testGetRefs_DiscoversDeletedLoose1() throws IOException {
+ Map<String, Ref> orig, next;
+ Ref orig_r, next_r;
+
+ writeLooseRef("refs/heads/B", B);
+ writeLooseRef("refs/heads/master", A);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ deleteLooseRef("refs/heads/B");
+ next = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(3, orig.size());
+ assertEquals(2, next.size());
+
+ assertTrue(orig.containsKey("refs/heads/B"));
+ assertFalse(next.containsKey("refs/heads/B"));
+
+ orig_r = orig.get("refs/heads/master");
+ next_r = next.get("refs/heads/master");
+ assertEquals(A, orig_r.getObjectId());
+ assertSame("uses cached instance", orig_r, next_r);
+ assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
+ assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
+
+ orig_r = orig.get("refs/heads/B");
+ assertSame(LOOSE, orig_r.getStorage());
+ assertEquals(B, orig_r.getObjectId());
+ }
+
+ public void testGetRef_DiscoversDeletedLoose() throws IOException {
+ Map<String, Ref> all;
+
+ writeLooseRef("refs/heads/master", A);
+ all = refdir.getRefs(RefDatabase.ALL);
+ assertEquals(A, all.get(HEAD).getObjectId());
+
+ deleteLooseRef("refs/heads/master");
+ assertNull(refdir.getRef("refs/heads/master"));
+ assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
+ }
+
+ public void testGetRefs_DiscoversDeletedLoose2() throws IOException {
+ Map<String, Ref> orig, next;
+
+ writeLooseRef("refs/heads/master", A);
+ writeLooseRef("refs/heads/pu", B);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ deleteLooseRef("refs/heads/pu");
+ next = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(3, orig.size());
+ assertEquals(2, next.size());
+
+ assertTrue(orig.containsKey("refs/heads/pu"));
+ assertFalse(next.containsKey("refs/heads/pu"));
+ }
+
+ public void testGetRefs_DiscoversDeletedLoose3() throws IOException {
+ Map<String, Ref> orig, next;
+
+ writeLooseRef("refs/heads/master", A);
+ writeLooseRef("refs/heads/next", B);
+ writeLooseRef("refs/heads/pu", B);
+ writeLooseRef("refs/tags/v1.0", v1_0);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ deleteLooseRef("refs/heads/pu");
+ deleteLooseRef("refs/heads/next");
+ next = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(5, orig.size());
+ assertEquals(3, next.size());
+
+ assertTrue(orig.containsKey("refs/heads/pu"));
+ assertTrue(orig.containsKey("refs/heads/next"));
+ assertFalse(next.containsKey("refs/heads/pu"));
+ assertFalse(next.containsKey("refs/heads/next"));
+ }
+
+ public void testGetRefs_DiscoversDeletedLoose4() throws IOException {
+ Map<String, Ref> orig, next;
+ Ref orig_r, next_r;
+
+ writeLooseRef("refs/heads/B", B);
+ writeLooseRef("refs/heads/master", A);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ deleteLooseRef("refs/heads/master");
+ next = refdir.getRefs("refs/heads/");
+
+ assertEquals(3, orig.size());
+ assertEquals(1, next.size());
+
+ assertTrue(orig.containsKey("refs/heads/B"));
+ assertTrue(orig.containsKey("refs/heads/master"));
+ assertTrue(next.containsKey("B"));
+ assertFalse(next.containsKey("master"));
+
+ orig_r = orig.get("refs/heads/B");
+ next_r = next.get("B");
+ assertEquals(B, orig_r.getObjectId());
+ assertSame("uses cached instance", orig_r, next_r);
+ }
+
+ public void testGetRefs_DiscoversDeletedLoose5() throws IOException {
+ Map<String, Ref> orig, next;
+
+ writeLooseRef("refs/heads/master", A);
+ writeLooseRef("refs/heads/pu", B);
+ orig = refdir.getRefs(RefDatabase.ALL);
+
+ deleteLooseRef("refs/heads/pu");
+ writeLooseRef("refs/tags/v1.0", v1_0);
+ next = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(3, orig.size());
+ assertEquals(3, next.size());
+
+ assertTrue(orig.containsKey("refs/heads/pu"));
+ assertFalse(orig.containsKey("refs/tags/v1.0"));
+ assertFalse(next.containsKey("refs/heads/pu"));
+ assertTrue(next.containsKey("refs/tags/v1.0"));
+ }
+
+ public void testGetRefs_SkipsLockFiles() throws IOException {
+ Map<String, Ref> all;
+
+ writeLooseRef("refs/heads/master", A);
+ writeLooseRef("refs/heads/pu.lock", B);
+ all = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(2, all.size());
+
+ assertTrue(all.containsKey(HEAD));
+ assertTrue(all.containsKey("refs/heads/master"));
+ assertFalse(all.containsKey("refs/heads/pu.lock"));
+ }
+
+ public void testGetRefs_CycleInSymbolicRef() throws IOException {
+ Map<String, Ref> all;
+ Ref r;
+
+ writeLooseRef("refs/1", "ref: refs/2\n");
+ writeLooseRef("refs/2", "ref: refs/3\n");
+ writeLooseRef("refs/3", "ref: refs/4\n");
+ writeLooseRef("refs/4", "ref: refs/5\n");
+ writeLooseRef("refs/5", "ref: refs/end\n");
+ writeLooseRef("refs/end", A);
+
+ all = refdir.getRefs(RefDatabase.ALL);
+ r = all.get("refs/1");
+ assertNotNull("has 1", r);
+
+ assertEquals("refs/1", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/2", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/3", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/4", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/5", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/end", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertFalse(r.isSymbolic());
+
+ writeLooseRef("refs/5", "ref: refs/6\n");
+ writeLooseRef("refs/6", "ref: refs/end\n");
+ BUG_WorkAroundRacyGitIssues("refs/5");
+ all = refdir.getRefs(RefDatabase.ALL);
+ r = all.get("refs/1");
+ assertNull("mising 1 due to cycle", r);
+ }
+
+ public void testGetRefs_PackedNotPeeled_Sorted() throws IOException {
+ Map<String, Ref> all;
+
+ writePackedRefs("" + //
+ A.name() + " refs/heads/master\n" + //
+ B.name() + " refs/heads/other\n" + //
+ v1_0.name() + " refs/tags/v1.0\n");
+ all = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(4, all.size());
+ final Ref head = all.get(HEAD);
+ final Ref master = all.get("refs/heads/master");
+ final Ref other = all.get("refs/heads/other");
+ final Ref tag = all.get("refs/tags/v1.0");
+
+ assertEquals(A, master.getObjectId());
+ assertFalse(master.isPeeled());
+ assertNull(master.getPeeledObjectId());
+
+ assertEquals(B, other.getObjectId());
+ assertFalse(other.isPeeled());
+ assertNull(other.getPeeledObjectId());
+
+ assertSame(master, head.getTarget());
+ assertEquals(A, head.getObjectId());
+ assertFalse(head.isPeeled());
+ assertNull(head.getPeeledObjectId());
+
+ assertEquals(v1_0, tag.getObjectId());
+ assertFalse(tag.isPeeled());
+ assertNull(tag.getPeeledObjectId());
+ }
+
+ public void testGetRef_PackedNotPeeled_WrongSort() throws IOException {
+ writePackedRefs("" + //
+ v1_0.name() + " refs/tags/v1.0\n" + //
+ B.name() + " refs/heads/other\n" + //
+ A.name() + " refs/heads/master\n");
+
+ final Ref head = refdir.getRef(HEAD);
+ final Ref master = refdir.getRef("refs/heads/master");
+ final Ref other = refdir.getRef("refs/heads/other");
+ final Ref tag = refdir.getRef("refs/tags/v1.0");
+
+ assertEquals(A, master.getObjectId());
+ assertFalse(master.isPeeled());
+ assertNull(master.getPeeledObjectId());
+
+ assertEquals(B, other.getObjectId());
+ assertFalse(other.isPeeled());
+ assertNull(other.getPeeledObjectId());
+
+ assertSame(master, head.getTarget());
+ assertEquals(A, head.getObjectId());
+ assertFalse(head.isPeeled());
+ assertNull(head.getPeeledObjectId());
+
+ assertEquals(v1_0, tag.getObjectId());
+ assertFalse(tag.isPeeled());
+ assertNull(tag.getPeeledObjectId());
+ }
+
+ public void testGetRefs_PackedWithPeeled() throws IOException {
+ Map<String, Ref> all;
+
+ writePackedRefs("# pack-refs with: peeled \n" + //
+ A.name() + " refs/heads/master\n" + //
+ B.name() + " refs/heads/other\n" + //
+ v1_0.name() + " refs/tags/v1.0\n" + //
+ "^" + v1_0.getObject().name() + "\n");
+ all = refdir.getRefs(RefDatabase.ALL);
+
+ assertEquals(4, all.size());
+ final Ref head = all.get(HEAD);
+ final Ref master = all.get("refs/heads/master");
+ final Ref other = all.get("refs/heads/other");
+ final Ref tag = all.get("refs/tags/v1.0");
+
+ assertEquals(A, master.getObjectId());
+ assertTrue(master.isPeeled());
+ assertNull(master.getPeeledObjectId());
+
+ assertEquals(B, other.getObjectId());
+ assertTrue(other.isPeeled());
+ assertNull(other.getPeeledObjectId());
+
+ assertSame(master, head.getTarget());
+ assertEquals(A, head.getObjectId());
+ assertTrue(head.isPeeled());
+ assertNull(head.getPeeledObjectId());
+
+ assertEquals(v1_0, tag.getObjectId());
+ assertTrue(tag.isPeeled());
+ assertEquals(v1_0.getObject(), tag.getPeeledObjectId());
+ }
+
+ public void testGetRef_EmptyDatabase() throws IOException {
+ Ref r;
+
+ r = refdir.getRef(HEAD);
+ assertTrue(r.isSymbolic());
+ assertSame(LOOSE, r.getStorage());
+ assertEquals("refs/heads/master", r.getTarget().getName());
+ assertSame(NEW, r.getTarget().getStorage());
+ assertNull(r.getTarget().getObjectId());
+
+ assertNull(refdir.getRef("refs/heads/master"));
+ assertNull(refdir.getRef("refs/tags/v1.0"));
+ assertNull(refdir.getRef("FETCH_HEAD"));
+ assertNull(refdir.getRef("NOT.A.REF.NAME"));
+ assertNull(refdir.getRef("master"));
+ assertNull(refdir.getRef("v1.0"));
+ }
+
+ public void testGetRef_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.getRef("FETCH_HEAD");
+ assertFalse(r.isSymbolic());
+ assertEquals(A, r.getObjectId());
+ assertEquals("FETCH_HEAD", r.getName());
+ assertFalse(r.isPeeled());
+ assertNull(r.getPeeledObjectId());
+ }
+
+ public void testGetRef_AnyHeadWithGarbage() throws IOException {
+ write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name()
+ + "012345 . this is not a standard reference\n"
+ + "#and even more junk\n");
+
+ Ref r = refdir.getRef("refs/heads/A");
+ assertFalse(r.isSymbolic());
+ assertEquals(A, r.getObjectId());
+ assertEquals("refs/heads/A", r.getName());
+ assertFalse(r.isPeeled());
+ assertNull(r.getPeeledObjectId());
+ }
+
+ public void testGetRefs_CorruptSymbolicReference() throws IOException {
+ String name = "refs/heads/A";
+ writeLooseRef(name, "ref: \n");
+ assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
+ }
+
+ public void testGetRef_CorruptSymbolicReference() throws IOException {
+ String name = "refs/heads/A";
+ writeLooseRef(name, "ref: \n");
+ try {
+ refdir.getRef(name);
+ fail("read an invalid reference");
+ } catch (IOException err) {
+ String msg = err.getMessage();
+ assertEquals("Not a ref: " + name + ": ref:", msg);
+ }
+ }
+
+ public void testGetRefs_CorruptObjectIdReference() throws IOException {
+ String name = "refs/heads/A";
+ String content = "zoo" + A.name();
+ writeLooseRef(name, content + "\n");
+ assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
+ }
+
+ public void testGetRef_CorruptObjectIdReference() throws IOException {
+ String name = "refs/heads/A";
+ String content = "zoo" + A.name();
+ writeLooseRef(name, content + "\n");
+ try {
+ refdir.getRef(name);
+ fail("read an invalid reference");
+ } catch (IOException err) {
+ String msg = err.getMessage();
+ assertEquals("Not a ref: " + name + ": " + content, msg);
+ }
+ }
+
+ public void testIsNameConflicting() throws IOException {
+ writeLooseRef("refs/heads/a/b", A);
+ writePackedRef("refs/heads/q", B);
+
+ // new references cannot replace an existing container
+ assertTrue(refdir.isNameConflicting("refs"));
+ assertTrue(refdir.isNameConflicting("refs/heads"));
+ assertTrue(refdir.isNameConflicting("refs/heads/a"));
+
+ // existing reference is not conflicting
+ assertFalse(refdir.isNameConflicting("refs/heads/a/b"));
+
+ // new references are not conflicting
+ assertFalse(refdir.isNameConflicting("refs/heads/a/d"));
+ assertFalse(refdir.isNameConflicting("refs/heads/master"));
+
+ // existing reference must not be used as a container
+ assertTrue(refdir.isNameConflicting("refs/heads/a/b/c"));
+ assertTrue(refdir.isNameConflicting("refs/heads/q/master"));
+ }
+
+ public void testPeelLooseTag() throws IOException {
+ writeLooseRef("refs/tags/v1_0", v1_0);
+ writeLooseRef("refs/tags/current", "ref: refs/tags/v1_0\n");
+
+ final Ref tag = refdir.getRef("refs/tags/v1_0");
+ final Ref cur = refdir.getRef("refs/tags/current");
+
+ assertEquals(v1_0, tag.getObjectId());
+ assertFalse(tag.isSymbolic());
+ assertFalse(tag.isPeeled());
+ assertNull(tag.getPeeledObjectId());
+
+ assertEquals(v1_0, cur.getObjectId());
+ assertTrue(cur.isSymbolic());
+ assertFalse(cur.isPeeled());
+ assertNull(cur.getPeeledObjectId());
+
+ final Ref tag_p = refdir.peel(tag);
+ final Ref cur_p = refdir.peel(cur);
+
+ assertNotSame(tag, tag_p);
+ assertFalse(tag_p.isSymbolic());
+ assertTrue(tag_p.isPeeled());
+ assertEquals(v1_0, tag_p.getObjectId());
+ assertEquals(v1_0.getObject(), tag_p.getPeeledObjectId());
+ assertSame(tag_p, refdir.peel(tag_p));
+
+ assertNotSame(cur, cur_p);
+ assertEquals("refs/tags/current", cur_p.getName());
+ assertTrue(cur_p.isSymbolic());
+ assertEquals("refs/tags/v1_0", cur_p.getTarget().getName());
+ assertTrue(cur_p.isPeeled());
+ assertEquals(v1_0, cur_p.getObjectId());
+ assertEquals(v1_0.getObject(), cur_p.getPeeledObjectId());
+
+ // reuses cached peeling later, but not immediately due to
+ // the implementation so we have to fetch it once.
+ final Ref tag_p2 = refdir.getRef("refs/tags/v1_0");
+ assertFalse(tag_p2.isSymbolic());
+ assertTrue(tag_p2.isPeeled());
+ assertEquals(v1_0, tag_p2.getObjectId());
+ assertEquals(v1_0.getObject(), tag_p2.getPeeledObjectId());
+
+ assertSame(tag_p2, refdir.getRef("refs/tags/v1_0"));
+ assertSame(tag_p2, refdir.getRef("refs/tags/current").getTarget());
+ assertSame(tag_p2, refdir.peel(tag_p2));
+ }
+
+ public void testPeelCommit() throws IOException {
+ writeLooseRef("refs/heads/master", A);
+
+ Ref master = refdir.getRef("refs/heads/master");
+ assertEquals(A, master.getObjectId());
+ assertFalse(master.isPeeled());
+ assertNull(master.getPeeledObjectId());
+
+ Ref master_p = refdir.peel(master);
+ assertNotSame(master, master_p);
+ assertEquals(A, master_p.getObjectId());
+ assertTrue(master_p.isPeeled());
+ assertNull(master_p.getPeeledObjectId());
+
+ // reuses cached peeling later, but not immediately due to
+ // the implementation so we have to fetch it once.
+ Ref master_p2 = refdir.getRef("refs/heads/master");
+ assertNotSame(master, master_p2);
+ assertEquals(A, master_p2.getObjectId());
+ assertTrue(master_p2.isPeeled());
+ assertNull(master_p2.getPeeledObjectId());
+ assertSame(master_p2, refdir.peel(master_p2));
+ }
+
+ private void writeLooseRef(String name, AnyObjectId id) throws IOException {
+ writeLooseRef(name, id.name() + "\n");
+ }
+
+ private void writeLooseRef(String name, String content) throws IOException {
+ write(new File(diskRepo.getDirectory(), name), content);
+ }
+
+ private void writePackedRef(String name, AnyObjectId id) throws IOException {
+ writePackedRefs(id.name() + " " + name + "\n");
+ }
+
+ private void writePackedRefs(String content) throws IOException {
+ File pr = new File(diskRepo.getDirectory(), "packed-refs");
+ write(pr, content);
+ }
+
+ private void deleteLooseRef(String name) {
+ File path = new File(diskRepo.getDirectory(), name);
+ assertTrue("deleted " + name, path.delete());
+ }
+
+ /**
+ * Kick the timestamp of a local file.
+ * <p>
+ * We shouldn't have to make these method calls. The cache is using file
+ * system timestamps, and on many systems unit tests run faster than the
+ * modification clock. Dumping the cache after we make an edit behind
+ * RefDirectory's back allows the tests to pass.
+ *
+ * @param name
+ * the file in the repository to force a time change on.
+ */
+ private void BUG_WorkAroundRacyGitIssues(String name) {
+ File path = new File(diskRepo.getDirectory(), name);
+ long old = path.lastModified();
+ long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
+ path.setLastModified(set);
+ assertTrue("time changed", old != path.lastModified());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index 67ed6abb21..42c8a92b93 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -75,7 +75,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
Ref refHEAD = allRefs.get("refs/remotes/origin/HEAD");
assertNotNull(refHEAD);
assertEquals(masterId, refHEAD.getObjectId());
- assertTrue(refHEAD.isPeeled());
+ assertFalse(refHEAD.isPeeled());
assertNull(refHEAD.getPeeledObjectId());
Ref refmaster = allRefs.get("refs/remotes/origin/master");
@@ -87,7 +87,11 @@ public class RefTest extends SampleDataRepositoryTestCase {
public void testReadSymRefToPacked() throws IOException {
db.writeSymref("HEAD", "refs/heads/b");
Ref ref = db.getRef("HEAD");
- assertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage());
+ assertEquals(Ref.Storage.LOOSE, ref.getStorage());
+ assertTrue("is symref", ref.isSymbolic());
+ ref = ref.getTarget();
+ assertEquals("refs/heads/b", ref.getName());
+ assertEquals(Ref.Storage.PACKED, ref.getStorage());
}
public void testReadSymRefToLoosePacked() throws IOException {
@@ -100,7 +104,10 @@ public class RefTest extends SampleDataRepositoryTestCase {
db.writeSymref("HEAD", "refs/heads/master");
Ref ref = db.getRef("HEAD");
- assertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage());
+ assertEquals(Ref.Storage.LOOSE, ref.getStorage());
+ ref = ref.getTarget();
+ assertEquals("refs/heads/master", ref.getName());
+ assertEquals(Ref.Storage.LOOSE, ref.getStorage());
}
public void testReadLooseRef() throws IOException {
@@ -129,7 +136,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
os.close();
ref = db.getRef("refs/heads/master");
- assertEquals(Storage.LOOSE_PACKED, ref.getStorage());
+ assertEquals(Storage.LOOSE, ref.getStorage());
}
/**
@@ -149,18 +156,26 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals(Result.FORCED, update);
ref = db.getRef("refs/heads/master");
- assertEquals(Storage.LOOSE_PACKED, ref.getStorage());
+ assertEquals(Storage.LOOSE, ref.getStorage());
}
- public void testOrigResolvedNamesBranch() throws IOException {
+ public void testResolvedNamesBranch() throws IOException {
Ref ref = db.getRef("a");
assertEquals("refs/heads/a", ref.getName());
- assertEquals("refs/heads/a", ref.getOrigName());
}
- public void testOrigResolvedNamesSymRef() throws IOException {
- Ref ref = db.getRef("HEAD");
- assertEquals("refs/heads/master", ref.getName());
- assertEquals("HEAD", ref.getOrigName());
+ public void testResolvedSymRef() throws IOException {
+ Ref ref = db.getRef(Constants.HEAD);
+ assertEquals(Constants.HEAD, ref.getName());
+ assertTrue("is symbolic ref", ref.isSymbolic());
+ assertSame(Ref.Storage.LOOSE, ref.getStorage());
+
+ Ref dst = ref.getTarget();
+ assertNotNull("has target", dst);
+ assertEquals("refs/heads/master", dst.getName());
+
+ assertSame(dst.getObjectId(), ref.getObjectId());
+ assertSame(dst.getPeeledObjectId(), ref.getPeeledObjectId());
+ assertEquals(dst.isPeeled(), ref.isPeeled());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java
index d851528cdd..cb9111758a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java
@@ -260,10 +260,10 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
delete(ref, Result.FORCED);
}
- public void testRefKeySameAsOrigName() {
+ public void testRefKeySameAsName() {
Map<String, Ref> allRefs = db.getAllRefs();
for (Entry<String, Ref> e : allRefs.entrySet()) {
- assertEquals(e.getKey(), e.getValue().getOrigName());
+ assertEquals(e.getKey(), e.getValue().getName());
}
}
@@ -308,7 +308,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(ppid, db.resolve("HEAD"));
Ref ref = db.getRef("HEAD");
assertEquals("HEAD", ref.getName());
- assertEquals("HEAD", ref.getOrigName());
+ assertTrue("is detached", !ref.isSymbolic());
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
@@ -337,7 +337,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(ppid, db.resolve("HEAD"));
Ref ref = db.getRef("HEAD");
assertEquals("HEAD", ref.getName());
- assertEquals("HEAD", ref.getOrigName());
+ assertTrue("is detached", !ref.isSymbolic());
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
@@ -414,11 +414,14 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
updateRef.setNewObjectId(oldValue);
update = updateRef.update();
assertEquals(Result.FAST_FORWARD, update);
+
allRefs = db.getAllRefs();
- assertEquals("refs/heads/master", allRefs.get("refs/heads/master").getName());
- assertEquals("refs/heads/master", allRefs.get("refs/heads/master").getOrigName());
- assertEquals("refs/heads/master", allRefs.get("HEAD").getName());
- assertEquals("HEAD", allRefs.get("HEAD").getOrigName());
+ Ref master = allRefs.get("refs/heads/master");
+ Ref head = allRefs.get("HEAD");
+ assertEquals("refs/heads/master", master.getName());
+ assertEquals("HEAD", head.getName());
+ assertTrue("is symbolic reference", head.isSymbolic());
+ assertSame(master, head.getTarget());
}
/**
@@ -430,7 +433,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
*
* @throws Exception
*/
- public void testRefsCacheAfterUpdateLoosOnly() throws Exception {
+ public void testRefsCacheAfterUpdateLooseOnly() throws Exception {
// Do not use the defalt repo for this case.
Map<String, Ref> allRefs = db.getAllRefs();
ObjectId oldValue = db.resolve("HEAD");
@@ -440,11 +443,14 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
updateRef.setNewObjectId(oldValue);
Result update = updateRef.update();
assertEquals(Result.NEW, update);
+
allRefs = db.getAllRefs();
- assertEquals("refs/heads/newref", allRefs.get("HEAD").getName());
- assertEquals("HEAD", allRefs.get("HEAD").getOrigName());
- assertEquals("refs/heads/newref", allRefs.get("refs/heads/newref").getName());
- assertEquals("refs/heads/newref", allRefs.get("refs/heads/newref").getOrigName());
+ Ref head = allRefs.get("HEAD");
+ Ref newref = allRefs.get("refs/heads/newref");
+ assertEquals("refs/heads/newref", newref.getName());
+ assertEquals("HEAD", head.getName());
+ assertTrue("is symbolic reference", head.isSymbolic());
+ assertSame(newref, head.getTarget());
}
/**
@@ -575,8 +581,8 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
ObjectId oldHead = db.resolve(Constants.HEAD);
assertFalse("precondition for this test, branch b != HEAD", rb
.equals(oldHead));
- RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
- assertTrue("no log on old branch", new File(db.getDirectory(),
+ writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue("log on old branch", new File(db.getDirectory(),
"logs/refs/heads/b").exists());
RefRename renameRef = db.renameRef("refs/heads/b",
"refs/heads/new/name");
@@ -598,8 +604,8 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
db.writeSymref(Constants.HEAD, "refs/heads/b");
ObjectId oldHead = db.resolve(Constants.HEAD);
assertTrue("internal test condition, b == HEAD", rb.equals(oldHead));
- RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
- assertTrue("no log on old branch", new File(db.getDirectory(),
+ writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue("log on old branch", new File(db.getDirectory(),
"logs/refs/heads/b").exists());
RefRename renameRef = db.renameRef("refs/heads/b",
"refs/heads/new/name");
@@ -625,10 +631,9 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
updateRef.setForceUpdate(true);
Result update = updateRef.update();
assertEquals("internal check new ref is loose", Result.FORCED, update);
- assertEquals(Ref.Storage.LOOSE_PACKED, db.getRef("refs/heads/b")
- .getStorage());
- RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
- assertTrue("no log on old branch", new File(db.getDirectory(),
+ assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage());
+ writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue("log on old branch", new File(db.getDirectory(),
"logs/refs/heads/b").exists());
RefRename renameRef = db.renameRef("refs/heads/b",
"refs/heads/new/name");
@@ -657,7 +662,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
db.writeSymref(Constants.HEAD, headPointsTo);
ObjectId oldfromId = db.resolve(fromName);
ObjectId oldHeadId = db.resolve(Constants.HEAD);
- RefLogWriter.writeReflog(db, oldfromId, oldfromId, "Just a message",
+ writeReflog(db, oldfromId, oldfromId, "Just a message",
fromName);
List<org.eclipse.jgit.lib.ReflogReader.Entry> oldFromLog = db
.getReflogReader(fromName).getReverseEntries();
@@ -691,8 +696,8 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
.getReverseEntries().toString());
if (oldHeadId != null)
- assertEquals(oldHeadLog, db.getReflogReader(Constants.HEAD)
- .getReverseEntries());
+ assertEquals(oldHeadLog.toString(), db.getReflogReader(
+ Constants.HEAD).getReverseEntries().toString());
} finally {
lockFile.unlock();
}
@@ -733,12 +738,6 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
"refs/heads/new/name", "refs/heads/new/name");
}
- public void testRenameBranchCannotLockAFileHEADisToLockTmp()
- throws IOException {
- tryRenameWhenLocked("RENAMED-REF.." + Thread.currentThread().getId(),
- "refs/heads/b", "refs/heads/new/name", "refs/heads/new/name");
- }
-
public void testRenameBranchCannotLockAFileHEADisOtherLockFrom()
throws IOException {
tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
@@ -751,12 +750,6 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
"refs/heads/new/name", "refs/heads/a");
}
- public void testRenameBranchCannotLockAFileHEADisOtherLockTmp()
- throws IOException {
- tryRenameWhenLocked("RENAMED-REF.." + Thread.currentThread().getId(),
- "refs/heads/b", "refs/heads/new/name", "refs/heads/a");
- }
-
public void testRenameRefNameColission1avoided() throws IOException {
// setup
ObjectId rb = db.resolve("refs/heads/b");
@@ -767,7 +760,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.FAST_FORWARD, updateRef.update());
ObjectId oldHead = db.resolve(Constants.HEAD);
assertTrue(rb.equals(oldHead)); // assumption for this test
- RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/a");
+ writeReflog(db, rb, rb, "Just a message", "refs/heads/a");
assertTrue("internal check, we have a log", new File(db.getDirectory(),
"logs/refs/heads/a").exists());
@@ -800,7 +793,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.FORCED, updateRef.update());
ObjectId oldHead = db.resolve(Constants.HEAD);
assertTrue(rb.equals(oldHead)); // assumption for this test
- RefLogWriter.writeReflog(db, rb, rb, "Just a message",
+ writeReflog(db, rb, rb, "Just a message",
"refs/heads/prefix/a");
assertTrue("internal check, we have a log", new File(db.getDirectory(),
"logs/refs/heads/prefix/a").exists());
@@ -823,4 +816,13 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
"HEAD").getReverseEntries().get(0).getComment());
}
+
+ private void writeReflog(Repository db, ObjectId oldId, ObjectId newId,
+ String msg, String refName) throws IOException {
+ RefDirectory refs = (RefDirectory) db.getRefDatabase();
+ RefDirectoryUpdate update = refs.newUpdate(refName, true);
+ update.setOldObjectId(oldId);
+ update.setNewObjectId(newId);
+ refs.log(update, msg);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index 8e5f6fc83b..88bcf76710 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2009, Christian Halstrick, Matthias Sohn, SAP AG
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -54,7 +54,7 @@ public class ReflogConfigTest extends RepositoryTestCase {
// check that there are no entries in the reflog and turn off writing
// reflogs
- assertNull(db.getReflogReader(Constants.HEAD));
+ assertEquals(0, db.getReflogReader(Constants.HEAD).getReverseEntries().size());
db.getConfig().setBoolean("core", null, "logallrefupdates", false);
// do one commit and check that reflog size is 0: no reflogs should be
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java
new file mode 100644
index 0000000000..bff4fc34fa
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import junit.framework.TestCase;
+
+public class SymbolicRefTest extends TestCase {
+ private static final ObjectId ID_A = ObjectId
+ .fromString("41eb0d88f833b558bddeb269b7ab77399cdf98ed");
+
+ private static final ObjectId ID_B = ObjectId
+ .fromString("698dd0b8d0c299f080559a1cffc7fe029479a408");
+
+ private static final String targetName = "refs/heads/a.test.ref";
+
+ private static final String name = "refs/remotes/origin/HEAD";
+
+ public void testConstructor() {
+ Ref t;
+ SymbolicRef r;
+
+ t = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, targetName, null);
+ r = new SymbolicRef(name, t);
+ assertSame(Ref.Storage.LOOSE, r.getStorage());
+ assertSame(name, r.getName());
+ assertNull("no id on new ref", r.getObjectId());
+ assertFalse("not peeled", r.isPeeled());
+ assertNull("no peel id", r.getPeeledObjectId());
+ assertSame("leaf is t", t, r.getLeaf());
+ assertSame("target is t", t, r.getTarget());
+ assertTrue("is symbolic", r.isSymbolic());
+
+ t = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, targetName, ID_A);
+ r = new SymbolicRef(name, t);
+ assertSame(Ref.Storage.LOOSE, r.getStorage());
+ assertSame(name, r.getName());
+ assertSame(ID_A, r.getObjectId());
+ assertFalse("not peeled", r.isPeeled());
+ assertNull("no peel id", r.getPeeledObjectId());
+ assertSame("leaf is t", t, r.getLeaf());
+ assertSame("target is t", t, r.getTarget());
+ assertTrue("is symbolic", r.isSymbolic());
+ }
+
+ public void testLeaf() {
+ Ref a;
+ SymbolicRef b, c, d;
+
+ a = new ObjectIdRef.PeeledTag(Ref.Storage.PACKED, targetName, ID_A, ID_B);
+ b = new SymbolicRef("B", a);
+ c = new SymbolicRef("C", b);
+ d = new SymbolicRef("D", c);
+
+ assertSame(c, d.getTarget());
+ assertSame(b, c.getTarget());
+ assertSame(a, b.getTarget());
+
+ assertSame(a, d.getLeaf());
+ assertSame(a, c.getLeaf());
+ assertSame(a, b.getLeaf());
+ assertSame(a, a.getLeaf());
+
+ assertSame(ID_A, d.getObjectId());
+ assertSame(ID_A, c.getObjectId());
+ assertSame(ID_A, b.getObjectId());
+
+ assertTrue(d.isPeeled());
+ assertTrue(c.isPeeled());
+ assertTrue(b.isPeeled());
+
+ assertSame(ID_B, d.getPeeledObjectId());
+ assertSame(ID_B, c.getPeeledObjectId());
+ assertSame(ID_B, b.getPeeledObjectId());
+ }
+
+ public void testToString() {
+ Ref a;
+ SymbolicRef b, c, d;
+
+ a = new ObjectIdRef.PeeledTag(Ref.Storage.PACKED, targetName, ID_A, ID_B);
+ b = new SymbolicRef("B", a);
+ c = new SymbolicRef("C", b);
+ d = new SymbolicRef("D", c);
+
+ assertEquals("SymbolicRef[D -> C -> B -> " + targetName + "="
+ + ID_A.name() + "]", d.toString());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
index e29e9e7214..d4231bfa66 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
@@ -543,6 +543,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
w.println("0ce2ebdb36076ef0b38adbe077a07d43b43e3807 refs/tags/test022");
w.println("^b5d3b45a96b340441f5abb9080411705c51cc86c");
w.close();
+ ((RefDirectory)db.getRefDatabase()).rescan();
Tag mapTag20 = db.mapTag("test020");
assertNotNull("have tag test020", mapTag20);
@@ -673,6 +674,8 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
public void test028_LockPackedRef() throws IOException {
writeTrashFile(".git/packed-refs", "7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar");
writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n");
+ BUG_WorkAroundRacyGitIssues("packed-refs");
+ BUG_WorkAroundRacyGitIssues("HEAD");
ObjectId resolve = db.resolve("HEAD");
assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name());
@@ -727,4 +730,23 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkDir(), file));
}
+
+ /**
+ * Kick the timestamp of a local file.
+ * <p>
+ * We shouldn't have to make these method calls. The cache is using file
+ * system timestamps, and on many systems unit tests run faster than the
+ * modification clock. Dumping the cache after we make an edit behind
+ * RefDirectory's back allows the tests to pass.
+ *
+ * @param name
+ * the file in the repository to force a time change on.
+ */
+ private void BUG_WorkAroundRacyGitIssues(String name) {
+ File path = new File(db.getDirectory(), name);
+ long old = path.lastModified();
+ long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
+ path.setLastModified(set);
+ assertTrue("time changed", old != path.lastModified());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
index 8e997e3f8c..99edbd98ca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
@@ -51,6 +51,7 @@ import java.util.Map;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -88,7 +89,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
testOneUpdateStatus(rru, ref, Status.OK, true);
}
@@ -103,7 +104,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("0000000000000000000000000000000000000001"));
testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
}
@@ -118,7 +119,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
}
@@ -132,7 +133,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
"refs/heads/master", true, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
testOneUpdateStatus(rru, ref, Status.OK, false);
}
@@ -157,7 +158,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
public void testUpdateDelete() throws IOException {
final RemoteRefUpdate rru = new RemoteRefUpdate(db, null,
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
testOneUpdateStatus(rru, ref, Status.OK, true);
}
@@ -183,7 +184,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
testOneUpdateStatus(rru, ref, Status.UP_TO_DATE, null);
}
@@ -198,7 +199,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, ObjectId
.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
testOneUpdateStatus(rru, ref, Status.OK, true);
}
@@ -214,7 +215,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, ObjectId
.fromString("0000000000000000000000000000000000000001"));
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
}
@@ -231,7 +232,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", true, null, ObjectId
.fromString("0000000000000000000000000000000000000001"));
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
}
@@ -246,7 +247,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
testOneUpdateStatus(rru, ref, Status.REJECTED_OTHER_REASON, null);
}
@@ -260,7 +261,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
public void testUpdateMixedCases() throws IOException {
final RemoteRefUpdate rruOk = new RemoteRefUpdate(db, null,
"refs/heads/master", false, null, null);
- final Ref refToChange = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref refToChange = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
final RemoteRefUpdate rruReject = new RemoteRefUpdate(db, null,
"refs/heads/nonexisting", false, null, null);
@@ -282,7 +283,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, "refs/remotes/test/master", null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
refUpdates.add(rru);
advertisedRefs.add(ref);
@@ -303,7 +304,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
refUpdates.add(rru);
advertisedRefs.add(ref);
@@ -320,7 +321,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
"refs/heads/master", false, null, null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
final PushResult result = testOneUpdateStatus(rru, ref,
Status.REJECTED_NONFASTFORWARD, null);
@@ -336,7 +337,7 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
"refs/heads/master", false, "refs/remotes/test/master", null);
- final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
+ final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
refUpdates.add(rru);
advertisedRefs.add(ref);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
index 38dbe0962e..955b0c95ec 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.transport;
import junit.framework.TestCase;
+import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
public class RefSpecTest extends TestCase {
@@ -59,12 +60,12 @@ public class RefSpecTest extends TestCase {
assertEquals(sn + ":" + sn, rs.toString());
assertEquals(rs, new RefSpec(rs.toString()));
- Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
+ Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
assertTrue(rs.matchSource(r));
assertTrue(rs.matchDestination(r));
assertSame(rs, rs.expandFromSource(r));
- r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
assertFalse(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
}
@@ -91,12 +92,12 @@ public class RefSpecTest extends TestCase {
assertEquals("+" + sn + ":" + sn, rs.toString());
assertEquals(rs, new RefSpec(rs.toString()));
- Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
+ Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
assertTrue(rs.matchSource(r));
assertTrue(rs.matchDestination(r));
assertSame(rs, rs.expandFromSource(r));
- r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
assertFalse(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
}
@@ -111,12 +112,12 @@ public class RefSpecTest extends TestCase {
assertEquals(sn, rs.toString());
assertEquals(rs, new RefSpec(rs.toString()));
- Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
+ Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
assertTrue(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
assertSame(rs, rs.expandFromSource(r));
- r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
assertFalse(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
}
@@ -131,12 +132,12 @@ public class RefSpecTest extends TestCase {
assertEquals("+" + sn, rs.toString());
assertEquals(rs, new RefSpec(rs.toString()));
- Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
+ Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
assertTrue(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
assertSame(rs, rs.expandFromSource(r));
- r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
assertFalse(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
}
@@ -151,12 +152,12 @@ public class RefSpecTest extends TestCase {
assertEquals(":" + sn, rs.toString());
assertEquals(rs, new RefSpec(rs.toString()));
- Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
+ Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
assertFalse(rs.matchSource(r));
assertTrue(rs.matchDestination(r));
assertSame(rs, rs.expandFromSource(r));
- r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
assertFalse(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
}
@@ -175,7 +176,7 @@ public class RefSpecTest extends TestCase {
Ref r;
RefSpec expanded;
- r = new Ref(Ref.Storage.LOOSE, "refs/heads/master", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master", null);
assertTrue(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
expanded = rs.expandFromSource(r);
@@ -185,11 +186,11 @@ public class RefSpecTest extends TestCase {
assertEquals(r.getName(), expanded.getSource());
assertEquals("refs/remotes/origin/master", expanded.getDestination());
- r = new Ref(Ref.Storage.LOOSE, "refs/remotes/origin/next", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/remotes/origin/next", null);
assertFalse(rs.matchSource(r));
assertTrue(rs.matchDestination(r));
- r = new Ref(Ref.Storage.LOOSE, "refs/tags/v1.0", null);
+ r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/tags/v1.0", null);
assertFalse(rs.matchSource(r));
assertFalse(rs.matchDestination(r));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
index c9522341b0..c6471ded9b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
@@ -49,6 +49,7 @@ import java.util.NoSuchElementException;
import junit.framework.TestCase;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
public class RefListTest extends TestCase {
@@ -426,6 +427,6 @@ public class RefListTest extends TestCase {
}
private static Ref newRef(final String name) {
- return new Ref(Ref.Storage.LOOSE, name, ID);
+ return new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java
index 6ce206e013..c4c2383f5c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java
@@ -50,7 +50,9 @@ import java.util.NoSuchElementException;
import junit.framework.TestCase;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.SymbolicRef;
public class RefMapTest extends TestCase {
private static final ObjectId ID_ONE = ObjectId
@@ -176,6 +178,49 @@ public class RefMapTest extends TestCase {
}
}
+ public void testIterator_MissingUnresolvedSymbolicRefIsBug() {
+ final Ref master = newRef("refs/heads/master", ID_ONE);
+ final Ref headR = newRef("HEAD", master);
+
+ loose = toList(master);
+ // loose should have added newRef("HEAD", "refs/heads/master")
+ resolved = toList(headR);
+
+ RefMap map = new RefMap("", packed, loose, resolved);
+ Iterator<Ref> itr = map.values().iterator();
+ try {
+ itr.hasNext();
+ fail("iterator did not catch bad input");
+ } catch (IllegalStateException err) {
+ // expected
+ }
+ }
+
+ public void testMerge_HeadMaster() {
+ final Ref master = newRef("refs/heads/master", ID_ONE);
+ final Ref headU = newRef("HEAD", "refs/heads/master");
+ final Ref headR = newRef("HEAD", master);
+
+ loose = toList(headU, master);
+ resolved = toList(headR);
+
+ RefMap map = new RefMap("", packed, loose, resolved);
+ assertEquals(2, map.size());
+ assertFalse(map.isEmpty());
+ assertTrue(map.containsKey("refs/heads/master"));
+ assertSame(master, map.get("refs/heads/master"));
+
+ // resolved overrides loose given same name
+ assertSame(headR, map.get("HEAD"));
+
+ Iterator<Ref> itr = map.values().iterator();
+ assertTrue(itr.hasNext());
+ assertSame(headR, itr.next());
+ assertTrue(itr.hasNext());
+ assertSame(master, itr.next());
+ assertFalse(itr.hasNext());
+ }
+
public void testMerge_PackedLooseLoose() {
final Ref refA = newRef("A", ID_ONE);
final Ref refB_ONE = newRef("B", ID_ONE);
@@ -297,6 +342,42 @@ public class RefMapTest extends TestCase {
assertSame(refA_one, map.get("A"));
}
+ public void testPut_CollapseResolved() {
+ final Ref master = newRef("refs/heads/master", ID_ONE);
+ final Ref headU = newRef("HEAD", "refs/heads/master");
+ final Ref headR = newRef("HEAD", master);
+ final Ref a = newRef("refs/heads/A", ID_ONE);
+
+ loose = toList(headU, master);
+ resolved = toList(headR);
+
+ RefMap map = new RefMap("", packed, loose, resolved);
+ assertNull(map.put(a.getName(), a));
+ assertSame(a, map.get(a.getName()));
+ assertSame(headR, map.get("HEAD"));
+ }
+
+ public void testRemove() {
+ final Ref master = newRef("refs/heads/master", ID_ONE);
+ final Ref headU = newRef("HEAD", "refs/heads/master");
+ final Ref headR = newRef("HEAD", master);
+
+ packed = toList(master);
+ loose = toList(headU, master);
+ resolved = toList(headR);
+
+ RefMap map = new RefMap("", packed, loose, resolved);
+ assertNull(map.remove("not.a.reference"));
+
+ assertSame(master, map.remove("refs/heads/master"));
+ assertNull(map.get("refs/heads/master"));
+
+ assertSame(headR, map.remove("HEAD"));
+ assertNull(map.get("HEAD"));
+
+ assertTrue(map.isEmpty());
+ }
+
public void testToString_NoPrefix() {
final Ref a = newRef("refs/heads/A", ID_ONE);
final Ref b = newRef("refs/heads/B", ID_TWO);
@@ -376,7 +457,16 @@ public class RefMapTest extends TestCase {
return b.toRefList();
}
+ private static Ref newRef(String name, String dst) {
+ return newRef(name,
+ new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null));
+ }
+
+ private static Ref newRef(String name, Ref dst) {
+ return new SymbolicRef(name, dst);
+ }
+
private static Ref newRef(String name, ObjectId id) {
- return new Ref(Ref.Storage.LOOSE, name, id);
+ return new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, id);
}
}