/* * Copyright (C) 2010, 2013 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.internal.storage.file; 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 static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.events.RefsChangedEvent; import org.eclipse.jgit.events.RefsChangedListener; import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.ReceiveCommand.Type; import org.junit.Before; import org.junit.Test; public class RefDirectoryTest extends LocalDiskRepositoryTestCase { private Repository diskRepo; private TestRepository<Repository> repo; private RefDirectory refdir; private RevCommit A; private RevCommit B; private RevTag v1_0; @Before public void setUp() throws Exception { super.setUp(); diskRepo = createBareRepository(); refdir = (RefDirectory) diskRepo.getRefDatabase(); repo = new TestRepository<Repository>(diskRepo); A = repo.commit().create(); B = repo.commit(repo.getRevWalk().parseCommit(A)); v1_0 = repo.tag("v1_0", B); repo.getRevWalk().parseBody(v1_0); } @Test 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))); } @Test 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()); } @Test 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()); } @Test public void testGetRefs_DeatchedHead1() throws IOException { Map<String, Ref> all; Ref head; writeLooseRef(HEAD, A); 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()); } @Test public void testGetRefs_DeatchedHead2() throws IOException { Map<String, Ref> all; Ref head, master; writeLooseRef(HEAD, A); writeLooseRef("refs/heads/master", B); 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()); } @Test 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()); } @Test 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()); } @Test 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()); } @Test 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()); } @Test 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()); } @Test 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()); } @Test 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"); 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()); } @Test public void testFirstExactRef_IgnoresGarbageRef() throws IOException { writeLooseRef("refs/heads/A", A); write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n"); Ref a = refdir.firstExactRef("refs/heads/bad", "refs/heads/A"); assertEquals("refs/heads/A", a.getName()); assertEquals(A, a.getObjectId()); } @Test public void testExactRef_IgnoresGarbageRef() throws IOException { writeLooseRef("refs/heads/A", A); write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n"); Map<String, Ref> refs = refdir.exactRef("refs/heads/bad", "refs/heads/A"); assertNull("no refs/heads/bad", refs.get("refs/heads/bad")); Ref a = refs.get("refs/heads/A"); assertEquals("refs/heads/A", a.getName()); assertEquals(A, a.getObjectId()); assertEquals(1, refs.size()); } @Test 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()); } @Test public void testReadNotExistingBranchConfig() throws IOException { assertNull("find branch config", refdir.getRef("config")); assertNull("find branch config", refdir.getRef("refs/heads/config")); } @Test public void testReadBranchConfig() throws IOException { writeLooseRef("refs/heads/config", A); assertNotNull("find branch config", refdir.getRef("config")); } @Test 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()); } @Test 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()); } @Test 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()); } @Test 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()); } @Test public void testFirstExactRef_Mixed() throws IOException { writeLooseRef("refs/heads/A", A); writePackedRef("refs/tags/v1.0", v1_0); Ref a = refdir.firstExactRef("refs/heads/A", "refs/tags/v1.0"); Ref one = refdir.firstExactRef("refs/tags/v1.0", "refs/heads/A"); assertEquals("refs/heads/A", a.getName()); assertEquals("refs/tags/v1.0", one.getName()); assertEquals(A, a.getObjectId()); assertEquals(v1_0, one.getObjectId()); } @Test 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()); } @Test public void testGetRefs_LooseSortedCorrectly() throws IOException { Map<String, Ref> refs; writeLooseRef("refs/heads/project1/A", A); writeLooseRef("refs/heads/project1-B", B); refs = refdir.getRefs(RefDatabase.ALL); assertEquals(2, refs.size()); assertEquals(A, refs.get("refs/heads/project1/A").getObjectId()); assertEquals(B, refs.get("refs/heads/project1-B").getObjectId()); } @Test public void testGetRefs_LooseSorting_Bug_348834() throws IOException { Map<String, Ref> refs; writeLooseRef("refs/heads/my/a+b", A); writeLooseRef("refs/heads/my/a/b/c", B); final int[] count = new int[1]; ListenerHandle listener = Repository.getGlobalListenerList() .addRefsChangedListener(new RefsChangedListener() { public void onRefsChanged(RefsChangedEvent event) { count[0]++; } }); refs = refdir.getRefs(RefDatabase.ALL); refs = refdir.getRefs(RefDatabase.ALL); listener.remove(); assertEquals(1, count[0]); // Bug 348834 multiple RefsChangedEvents assertEquals(2, refs.size()); assertEquals(A, refs.get("refs/heads/my/a+b").getObjectId()); assertEquals(B, refs.get("refs/heads/my/a/b/c").getObjectId()); } @Test 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()); } @Test 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()); } @Test 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")); } @Test 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); all = refdir.getRefs(RefDatabase.ALL); assertEquals(B, all.get(HEAD).getObjectId()); } @Test 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); Ref master = refdir.getRef("refs/heads/master"); assertEquals(B, master.getObjectId()); } @Test 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()); } @Test 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()); } @Test 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")); } @Test 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")); } @Test 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); } @Test 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")); } @Test 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")); } @Test 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"); all = refdir.getRefs(RefDatabase.ALL); r = all.get("refs/1"); assertNull("mising 1 due to cycle", r); } @Test public void testGetRef_CycleInSymbolicRef() throws IOException { 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); r = refdir.getRef("1"); assertEquals("refs/1", r.getName()); assertEquals(A, r.getObjectId()); assertTrue(r.isSymbolic()); writeLooseRef("refs/5", "ref: refs/6\n"); writeLooseRef("refs/6", "ref: refs/end\n"); r = refdir.getRef("1"); assertNull("missing 1 due to cycle", r); writeLooseRef("refs/heads/1", B); r = refdir.getRef("1"); assertEquals("refs/heads/1", r.getName()); assertEquals(B, r.getObjectId()); assertFalse(r.isSymbolic()); } @Test 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()); } @Test 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()); } @Test 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()); } @Test public void test_repack() throws Exception { 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()); assertEquals(Storage.LOOSE, all.get(HEAD).getStorage()); assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage()); assertEquals(A.getId(), all.get("refs/heads/master").getObjectId()); assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage()); assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage()); repo.update("refs/heads/master", B.getId()); RevTag v0_1 = repo.tag("v0.1", A); repo.update("refs/tags/v0.1", v0_1); all = refdir.getRefs(RefDatabase.ALL); assertEquals(5, all.size()); assertEquals(Storage.LOOSE, all.get(HEAD).getStorage()); // Why isn't the next ref LOOSE_PACKED? assertEquals(Storage.LOOSE, all.get("refs/heads/master") .getStorage()); assertEquals(B.getId(), all.get("refs/heads/master").getObjectId()); assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage()); assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage()); assertEquals(Storage.LOOSE, all.get("refs/tags/v0.1").getStorage()); assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId()); all = refdir.getRefs(RefDatabase.ALL); refdir.pack(new ArrayList<String>(all.keySet())); all = refdir.getRefs(RefDatabase.ALL); assertEquals(5, all.size()); assertEquals(Storage.LOOSE, all.get(HEAD).getStorage()); // Why isn't the next ref LOOSE_PACKED? assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage()); assertEquals(B.getId(), all.get("refs/heads/master").getObjectId()); assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage()); assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage()); assertEquals(Storage.PACKED, all.get("refs/tags/v0.1").getStorage()); assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId()); } @Test 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")); } @Test public void testExactRef_EmptyDatabase() throws IOException { Ref r; r = refdir.exactRef(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.exactRef("refs/heads/master")); assertNull(refdir.exactRef("refs/tags/v1.0")); assertNull(refdir.exactRef("FETCH_HEAD")); assertNull(refdir.exactRef("NOT.A.REF.NAME")); assertNull(refdir.exactRef("master")); assertNull(refdir.exactRef("v1.0")); } @Test 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()); } @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() + "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()); } @Test public void testGetRefs_CorruptSymbolicReference() throws IOException { String name = "refs/heads/A"; writeLooseRef(name, "ref: \n"); assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); } @Test 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); } } @Test 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()); } @Test 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); } } @Test 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")); } @Test 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)); } @Test 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)); } @Test public void testRefsChangedStackOverflow() throws Exception { final FileRepository newRepo = createBareRepository(); final RefDatabase refDb = newRepo.getRefDatabase(); File packedRefs = new File(newRepo.getDirectory(), "packed-refs"); assertTrue(packedRefs.createNewFile()); final AtomicReference<StackOverflowError> error = new AtomicReference<StackOverflowError>(); final AtomicReference<IOException> exception = new AtomicReference<IOException>(); final AtomicInteger changeCount = new AtomicInteger(); newRepo.getListenerList().addRefsChangedListener( new RefsChangedListener() { public void onRefsChanged(RefsChangedEvent event) { try { refDb.getRefs("ref"); changeCount.incrementAndGet(); } catch (StackOverflowError soe) { error.set(soe); } catch (IOException ioe) { exception.set(ioe); } } }); refDb.getRefs("ref"); refDb.getRefs("ref"); assertNull(error.get()); assertNull(exception.get()); assertEquals(1, changeCount.get()); } @Test public void testBatchRefUpdateSimpleNoForce() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); List<ReceiveCommand> commands = Arrays.asList( newCommand(A, B, "refs/heads/master", ReceiveCommand.Type.UPDATE), newCommand(B, A, "refs/heads/masters", ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.addCommand(commands); batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.REJECTED_NONFASTFORWARD, commands .get(1).getResult()); assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs .keySet().toString()); assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId()); } @Test public void testBatchRefUpdateSimpleForce() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); List<ReceiveCommand> commands = Arrays.asList( newCommand(A, B, "refs/heads/master", ReceiveCommand.Type.UPDATE), newCommand(B, A, "refs/heads/masters", ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.setAllowNonFastForwards(true); batchUpdate.addCommand(commands); batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs .keySet().toString()); assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId()); } @Test public void testBatchRefUpdateNonFastForwardDoesNotDoExpensiveMergeCheck() throws IOException { writeLooseRef("refs/heads/master", B); List<ReceiveCommand> commands = Arrays.asList( newCommand(B, A, "refs/heads/master", ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.setAllowNonFastForwards(true); batchUpdate.addCommand(commands); batchUpdate.execute(new RevWalk(diskRepo) { @Override public boolean isMergedInto(RevCommit base, RevCommit tip) { throw new AssertionError("isMergedInto() should not be called"); } }, new StrictWorkMonitor()); Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); } @Test public void testBatchRefUpdateConflict() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); List<ReceiveCommand> commands = Arrays.asList( newCommand(A, B, "refs/heads/master", ReceiveCommand.Type.UPDATE), newCommand(null, A, "refs/heads/master/x", ReceiveCommand.Type.CREATE), newCommand(null, A, "refs/heads", ReceiveCommand.Type.CREATE)); BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.setAllowNonFastForwards(true); batchUpdate.addCommand(commands); batchUpdate .execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(1) .getResult()); assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(2) .getResult()); assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs .keySet().toString()); assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId()); } @Test public void testBatchRefUpdateConflictThanksToDelete() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); List<ReceiveCommand> commands = Arrays.asList( newCommand(A, B, "refs/heads/master", ReceiveCommand.Type.UPDATE), newCommand(null, A, "refs/heads/masters/x", ReceiveCommand.Type.CREATE), newCommand(B, null, "refs/heads/masters", ReceiveCommand.Type.DELETE)); BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.setAllowNonFastForwards(true); batchUpdate.addCommand(commands); batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); assertEquals(ReceiveCommand.Result.OK, commands.get(2).getResult()); assertEquals("[HEAD, refs/heads/master, refs/heads/masters/x]", refs .keySet().toString()); assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId()); } private static ReceiveCommand newCommand(RevCommit a, RevCommit b, String string, Type update) { return new ReceiveCommand(a != null ? a.getId() : null, b != null ? b.getId() : null, string, update); } 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); final long now = System.currentTimeMillis(); final int oneHourAgo = 3600 * 1000; pr.setLastModified(now - oneHourAgo); } private void deleteLooseRef(String name) { File path = new File(diskRepo.getDirectory(), name); assertTrue("deleted " + name, path.delete()); } private static final class StrictWorkMonitor implements ProgressMonitor { private int lastWork, totalWork; public void start(int totalTasks) { // empty } public void beginTask(String title, int total) { this.totalWork = total; lastWork = 0; } public void update(int completed) { lastWork += completed; } public void endTask() { assertEquals("Units of work recorded", totalWork, lastWork); } public boolean isCancelled() { return false; } } }