View Javadoc
1   /*
2    * Copyright (C) 2010, 2013 Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.internal.storage.file;
45  
46  import static org.eclipse.jgit.lib.Constants.HEAD;
47  import static org.eclipse.jgit.lib.Constants.R_HEADS;
48  import static org.eclipse.jgit.lib.Constants.R_TAGS;
49  import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
50  import static org.eclipse.jgit.lib.Ref.Storage.NEW;
51  import static org.junit.Assert.assertEquals;
52  import static org.junit.Assert.assertFalse;
53  import static org.junit.Assert.assertNotNull;
54  import static org.junit.Assert.assertNotSame;
55  import static org.junit.Assert.assertNull;
56  import static org.junit.Assert.assertSame;
57  import static org.junit.Assert.assertTrue;
58  import static org.junit.Assert.fail;
59  
60  import java.io.File;
61  import java.io.IOException;
62  import java.util.ArrayList;
63  import java.util.Arrays;
64  import java.util.Map;
65  import java.util.concurrent.atomic.AtomicInteger;
66  import java.util.concurrent.atomic.AtomicReference;
67  
68  import org.eclipse.jgit.errors.LockFailedException;
69  import org.eclipse.jgit.events.ListenerHandle;
70  import org.eclipse.jgit.events.RefsChangedEvent;
71  import org.eclipse.jgit.events.RefsChangedListener;
72  import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
73  import org.eclipse.jgit.junit.TestRepository;
74  import org.eclipse.jgit.lib.AnyObjectId;
75  import org.eclipse.jgit.lib.Ref;
76  import org.eclipse.jgit.lib.Ref.Storage;
77  import org.eclipse.jgit.lib.RefDatabase;
78  import org.eclipse.jgit.lib.Repository;
79  import org.eclipse.jgit.revwalk.RevCommit;
80  import org.eclipse.jgit.revwalk.RevTag;
81  import org.junit.Before;
82  import org.junit.Test;
83  
84  @SuppressWarnings("boxing")
85  public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
86  	private Repository diskRepo;
87  
88  	private TestRepository<Repository> repo;
89  
90  	private RefDirectory refdir;
91  
92  	private RevCommit A;
93  
94  	private RevCommit B;
95  
96  	private RevTag v1_0;
97  
98  	@Override
99  	@Before
100 	public void setUp() throws Exception {
101 		super.setUp();
102 
103 		diskRepo = createBareRepository();
104 		refdir = (RefDirectory) diskRepo.getRefDatabase();
105 
106 		repo = new TestRepository<>(diskRepo);
107 		A = repo.commit().create();
108 		B = repo.commit(repo.getRevWalk().parseCommit(A));
109 		v1_0 = repo.tag("v1_0", B);
110 		repo.getRevWalk().parseBody(v1_0);
111 	}
112 
113 	@Test
114 	public void testCreate() throws IOException {
115 		// setUp above created the directory. We just have to test it.
116 		File d = diskRepo.getDirectory();
117 		assertSame(diskRepo, refdir.getRepository());
118 
119 		assertTrue(new File(d, "refs").isDirectory());
120 		assertTrue(new File(d, "logs").isDirectory());
121 		assertTrue(new File(d, "logs/refs").isDirectory());
122 		assertFalse(new File(d, "packed-refs").exists());
123 
124 		assertTrue(new File(d, "refs/heads").isDirectory());
125 		assertTrue(new File(d, "refs/tags").isDirectory());
126 		assertEquals(2, new File(d, "refs").list().length);
127 		assertEquals(0, new File(d, "refs/heads").list().length);
128 		assertEquals(0, new File(d, "refs/tags").list().length);
129 
130 		assertTrue(new File(d, "logs/refs/heads").isDirectory());
131 		assertFalse(new File(d, "logs/HEAD").exists());
132 		assertEquals(0, new File(d, "logs/refs/heads").list().length);
133 
134 		assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
135 	}
136 
137 	@Test
138 	public void testGetRefs_EmptyDatabase() throws IOException {
139 		Map<String, Ref> all;
140 
141 		all = refdir.getRefs(RefDatabase.ALL);
142 		assertTrue("no references", all.isEmpty());
143 
144 		all = refdir.getRefs(R_HEADS);
145 		assertTrue("no references", all.isEmpty());
146 
147 		all = refdir.getRefs(R_TAGS);
148 		assertTrue("no references", all.isEmpty());
149 	}
150 
151 	@Test
152 	public void testGetRefs_HeadOnOneBranch() throws IOException {
153 		Map<String, Ref> all;
154 		Ref head, master;
155 
156 		writeLooseRef("refs/heads/master", A);
157 
158 		all = refdir.getRefs(RefDatabase.ALL);
159 		assertEquals(2, all.size());
160 		assertTrue("has HEAD", all.containsKey(HEAD));
161 		assertTrue("has master", all.containsKey("refs/heads/master"));
162 
163 		head = all.get(HEAD);
164 		master = all.get("refs/heads/master");
165 
166 		assertEquals(HEAD, head.getName());
167 		assertTrue(head.isSymbolic());
168 		assertSame(LOOSE, head.getStorage());
169 		assertSame("uses same ref as target", master, head.getTarget());
170 
171 		assertEquals("refs/heads/master", master.getName());
172 		assertFalse(master.isSymbolic());
173 		assertSame(LOOSE, master.getStorage());
174 		assertEquals(A, master.getObjectId());
175 	}
176 
177 	@Test
178 	public void testGetRefs_DeatchedHead1() throws IOException {
179 		Map<String, Ref> all;
180 		Ref head;
181 
182 		writeLooseRef(HEAD, A);
183 
184 		all = refdir.getRefs(RefDatabase.ALL);
185 		assertEquals(1, all.size());
186 		assertTrue("has HEAD", all.containsKey(HEAD));
187 
188 		head = all.get(HEAD);
189 
190 		assertEquals(HEAD, head.getName());
191 		assertFalse(head.isSymbolic());
192 		assertSame(LOOSE, head.getStorage());
193 		assertEquals(A, head.getObjectId());
194 	}
195 
196 	@Test
197 	public void testGetRefs_DeatchedHead2() throws IOException {
198 		Map<String, Ref> all;
199 		Ref head, master;
200 
201 		writeLooseRef(HEAD, A);
202 		writeLooseRef("refs/heads/master", B);
203 
204 		all = refdir.getRefs(RefDatabase.ALL);
205 		assertEquals(2, all.size());
206 
207 		head = all.get(HEAD);
208 		master = all.get("refs/heads/master");
209 
210 		assertEquals(HEAD, head.getName());
211 		assertFalse(head.isSymbolic());
212 		assertSame(LOOSE, head.getStorage());
213 		assertEquals(A, head.getObjectId());
214 
215 		assertEquals("refs/heads/master", master.getName());
216 		assertFalse(master.isSymbolic());
217 		assertSame(LOOSE, master.getStorage());
218 		assertEquals(B, master.getObjectId());
219 	}
220 
221 	@Test
222 	public void testGetRefs_DeeplyNestedBranch() throws IOException {
223 		String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k";
224 		Map<String, Ref> all;
225 		Ref r;
226 
227 		writeLooseRef(name, A);
228 
229 		all = refdir.getRefs(RefDatabase.ALL);
230 		assertEquals(1, all.size());
231 
232 		r = all.get(name);
233 		assertEquals(name, r.getName());
234 		assertFalse(r.isSymbolic());
235 		assertSame(LOOSE, r.getStorage());
236 		assertEquals(A, r.getObjectId());
237 	}
238 
239 	@Test
240 	public void testGetRefs_HeadBranchNotBorn() throws IOException {
241 		Map<String, Ref> all;
242 		Ref a, b;
243 
244 		writeLooseRef("refs/heads/A", A);
245 		writeLooseRef("refs/heads/B", B);
246 
247 		all = refdir.getRefs(RefDatabase.ALL);
248 		assertEquals(2, all.size());
249 		assertFalse("no HEAD", all.containsKey(HEAD));
250 
251 		a = all.get("refs/heads/A");
252 		b = all.get("refs/heads/B");
253 
254 		assertEquals(A, a.getObjectId());
255 		assertEquals(B, b.getObjectId());
256 
257 		assertEquals("refs/heads/A", a.getName());
258 		assertEquals("refs/heads/B", b.getName());
259 	}
260 
261 	@Test
262 	public void testGetRefs_LooseOverridesPacked() throws IOException {
263 		Map<String, Ref> heads;
264 		Ref a;
265 
266 		writeLooseRef("refs/heads/master", B);
267 		writePackedRef("refs/heads/master", A);
268 
269 		heads = refdir.getRefs(R_HEADS);
270 		assertEquals(1, heads.size());
271 
272 		a = heads.get("master");
273 		assertEquals("refs/heads/master", a.getName());
274 		assertEquals(B, a.getObjectId());
275 	}
276 
277 	@Test
278 	public void testGetRefs_IgnoresGarbageRef1() throws IOException {
279 		Map<String, Ref> heads;
280 		Ref a;
281 
282 		writeLooseRef("refs/heads/A", A);
283 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
284 
285 		heads = refdir.getRefs(RefDatabase.ALL);
286 		assertEquals(1, heads.size());
287 
288 		a = heads.get("refs/heads/A");
289 		assertEquals("refs/heads/A", a.getName());
290 		assertEquals(A, a.getObjectId());
291 	}
292 
293 	@Test
294 	public void testGetRefs_IgnoresGarbageRef2() throws IOException {
295 		Map<String, Ref> heads;
296 		Ref a;
297 
298 		writeLooseRef("refs/heads/A", A);
299 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "");
300 
301 		heads = refdir.getRefs(RefDatabase.ALL);
302 		assertEquals(1, heads.size());
303 
304 		a = heads.get("refs/heads/A");
305 		assertEquals("refs/heads/A", a.getName());
306 		assertEquals(A, a.getObjectId());
307 	}
308 
309 	@Test
310 	public void testGetRefs_IgnoresGarbageRef3() throws IOException {
311 		Map<String, Ref> heads;
312 		Ref a;
313 
314 		writeLooseRef("refs/heads/A", A);
315 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "\n");
316 
317 		heads = refdir.getRefs(RefDatabase.ALL);
318 		assertEquals(1, heads.size());
319 
320 		a = heads.get("refs/heads/A");
321 		assertEquals("refs/heads/A", a.getName());
322 		assertEquals(A, a.getObjectId());
323 	}
324 
325 	@Test
326 	public void testGetRefs_IgnoresGarbageRef4() throws IOException {
327 		Map<String, Ref> heads;
328 		Ref a, b, c;
329 
330 		writeLooseRef("refs/heads/A", A);
331 		writeLooseRef("refs/heads/B", B);
332 		writeLooseRef("refs/heads/C", A);
333 		heads = refdir.getRefs(RefDatabase.ALL);
334 		assertEquals(3, heads.size());
335 		assertTrue(heads.containsKey("refs/heads/A"));
336 		assertTrue(heads.containsKey("refs/heads/B"));
337 		assertTrue(heads.containsKey("refs/heads/C"));
338 
339 		writeLooseRef("refs/heads/B", "FAIL\n");
340 
341 		heads = refdir.getRefs(RefDatabase.ALL);
342 		assertEquals(2, heads.size());
343 
344 		a = heads.get("refs/heads/A");
345 		b = heads.get("refs/heads/B");
346 		c = heads.get("refs/heads/C");
347 
348 		assertEquals("refs/heads/A", a.getName());
349 		assertEquals(A, a.getObjectId());
350 
351 		assertNull("no refs/heads/B", b);
352 
353 		assertEquals("refs/heads/C", c.getName());
354 		assertEquals(A, c.getObjectId());
355 	}
356 
357 	@Test
358 	public void testFirstExactRef_IgnoresGarbageRef() throws IOException {
359 		writeLooseRef("refs/heads/A", A);
360 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
361 
362 		Ref a = refdir.firstExactRef("refs/heads/bad", "refs/heads/A");
363 		assertEquals("refs/heads/A", a.getName());
364 		assertEquals(A, a.getObjectId());
365 	}
366 
367 	@Test
368 	public void testExactRef_IgnoresGarbageRef() throws IOException {
369 		writeLooseRef("refs/heads/A", A);
370 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
371 
372 		Map<String, Ref> refs =
373 				refdir.exactRef("refs/heads/bad", "refs/heads/A");
374 
375 		assertNull("no refs/heads/bad", refs.get("refs/heads/bad"));
376 
377 		Ref a = refs.get("refs/heads/A");
378 		assertEquals("refs/heads/A", a.getName());
379 		assertEquals(A, a.getObjectId());
380 
381 		assertEquals(1, refs.size());
382 	}
383 
384 	@Test
385 	public void testGetRefs_InvalidName() throws IOException {
386 		writeLooseRef("refs/heads/A", A);
387 
388 		assertTrue("empty refs/heads", refdir.getRefs("refs/heads").isEmpty());
389 		assertTrue("empty objects", refdir.getRefs("objects").isEmpty());
390 		assertTrue("empty objects/", refdir.getRefs("objects/").isEmpty());
391 	}
392 
393 	@Test
394 	public void testReadNotExistingBranchConfig() throws IOException {
395 		assertNull("find branch config", refdir.getRef("config"));
396 		assertNull("find branch config", refdir.getRef("refs/heads/config"));
397 	}
398 
399 	@Test
400 	public void testReadBranchConfig() throws IOException {
401 		writeLooseRef("refs/heads/config", A);
402 
403 		assertNotNull("find branch config", refdir.getRef("config"));
404 	}
405 
406 	@Test
407 	public void testGetRefs_HeadsOnly_AllLoose() throws IOException {
408 		Map<String, Ref> heads;
409 		Ref a, b;
410 
411 		writeLooseRef("refs/heads/A", A);
412 		writeLooseRef("refs/heads/B", B);
413 		writeLooseRef("refs/tags/v1.0", v1_0);
414 
415 		heads = refdir.getRefs(R_HEADS);
416 		assertEquals(2, heads.size());
417 
418 		a = heads.get("A");
419 		b = heads.get("B");
420 
421 		assertEquals("refs/heads/A", a.getName());
422 		assertEquals("refs/heads/B", b.getName());
423 
424 		assertEquals(A, a.getObjectId());
425 		assertEquals(B, b.getObjectId());
426 	}
427 
428 	@Test
429 	public void testGetRefs_HeadsOnly_AllPacked1() throws IOException {
430 		Map<String, Ref> heads;
431 		Ref a;
432 
433 		deleteLooseRef(HEAD);
434 		writePackedRef("refs/heads/A", A);
435 
436 		heads = refdir.getRefs(R_HEADS);
437 		assertEquals(1, heads.size());
438 
439 		a = heads.get("A");
440 
441 		assertEquals("refs/heads/A", a.getName());
442 		assertEquals(A, a.getObjectId());
443 	}
444 
445 	@Test
446 	public void testGetRefs_HeadsOnly_SymrefToPacked() throws IOException {
447 		Map<String, Ref> heads;
448 		Ref master, other;
449 
450 		writeLooseRef("refs/heads/other", "ref: refs/heads/master\n");
451 		writePackedRef("refs/heads/master", A);
452 
453 		heads = refdir.getRefs(R_HEADS);
454 		assertEquals(2, heads.size());
455 
456 		master = heads.get("master");
457 		other = heads.get("other");
458 
459 		assertEquals("refs/heads/master", master.getName());
460 		assertEquals(A, master.getObjectId());
461 
462 		assertEquals("refs/heads/other", other.getName());
463 		assertEquals(A, other.getObjectId());
464 		assertSame(master, other.getTarget());
465 	}
466 
467 	@Test
468 	public void testGetRefs_HeadsOnly_Mixed() throws IOException {
469 		Map<String, Ref> heads;
470 		Ref a, b;
471 
472 		writeLooseRef("refs/heads/A", A);
473 		writeLooseRef("refs/heads/B", B);
474 		writePackedRef("refs/tags/v1.0", v1_0);
475 
476 		heads = refdir.getRefs(R_HEADS);
477 		assertEquals(2, heads.size());
478 
479 		a = heads.get("A");
480 		b = heads.get("B");
481 
482 		assertEquals("refs/heads/A", a.getName());
483 		assertEquals("refs/heads/B", b.getName());
484 
485 		assertEquals(A, a.getObjectId());
486 		assertEquals(B, b.getObjectId());
487 	}
488 
489 	@Test
490 	public void testFirstExactRef_Mixed() throws IOException {
491 		writeLooseRef("refs/heads/A", A);
492 		writePackedRef("refs/tags/v1.0", v1_0);
493 
494 		Ref a = refdir.firstExactRef("refs/heads/A", "refs/tags/v1.0");
495 		Ref one = refdir.firstExactRef("refs/tags/v1.0", "refs/heads/A");
496 
497 		assertEquals("refs/heads/A", a.getName());
498 		assertEquals("refs/tags/v1.0", one.getName());
499 
500 		assertEquals(A, a.getObjectId());
501 		assertEquals(v1_0, one.getObjectId());
502 	}
503 
504 	@Test
505 	public void testGetRefs_TagsOnly_AllLoose() throws IOException {
506 		Map<String, Ref> tags;
507 		Ref a;
508 
509 		writeLooseRef("refs/heads/A", A);
510 		writeLooseRef("refs/tags/v1.0", v1_0);
511 
512 		tags = refdir.getRefs(R_TAGS);
513 		assertEquals(1, tags.size());
514 
515 		a = tags.get("v1.0");
516 
517 		assertEquals("refs/tags/v1.0", a.getName());
518 		assertEquals(v1_0, a.getObjectId());
519 	}
520 
521 	@Test
522 	public void testGetRefs_LooseSortedCorrectly() throws IOException {
523 		Map<String, Ref> refs;
524 
525 		writeLooseRef("refs/heads/project1/A", A);
526 		writeLooseRef("refs/heads/project1-B", B);
527 
528 		refs = refdir.getRefs(RefDatabase.ALL);
529 		assertEquals(2, refs.size());
530 		assertEquals(A, refs.get("refs/heads/project1/A").getObjectId());
531 		assertEquals(B, refs.get("refs/heads/project1-B").getObjectId());
532 	}
533 
534 	@Test
535 	public void testGetRefs_LooseSorting_Bug_348834() throws IOException {
536 		Map<String, Ref> refs;
537 
538 		writeLooseRef("refs/heads/my/a+b", A);
539 		writeLooseRef("refs/heads/my/a/b/c", B);
540 
541 		final int[] count = new int[1];
542 
543 		ListenerHandle listener = Repository.getGlobalListenerList()
544 				.addRefsChangedListener(new RefsChangedListener() {
545 
546 					@Override
547 					public void onRefsChanged(RefsChangedEvent event) {
548 						count[0]++;
549 					}
550 				});
551 
552 		refs = refdir.getRefs(RefDatabase.ALL);
553 		refs = refdir.getRefs(RefDatabase.ALL);
554 		listener.remove();
555 		assertEquals(1, count[0]); // Bug 348834 multiple RefsChangedEvents
556 		assertEquals(2, refs.size());
557 		assertEquals(A, refs.get("refs/heads/my/a+b").getObjectId());
558 		assertEquals(B, refs.get("refs/heads/my/a/b/c").getObjectId());
559 
560 	}
561 
562 	@Test
563 	public void testGetRefs_TagsOnly_AllPacked() throws IOException {
564 		Map<String, Ref> tags;
565 		Ref a;
566 
567 		deleteLooseRef(HEAD);
568 		writePackedRef("refs/tags/v1.0", v1_0);
569 
570 		tags = refdir.getRefs(R_TAGS);
571 		assertEquals(1, tags.size());
572 
573 		a = tags.get("v1.0");
574 
575 		assertEquals("refs/tags/v1.0", a.getName());
576 		assertEquals(v1_0, a.getObjectId());
577 	}
578 
579 	@Test
580 	public void testGetRefs_DiscoversNewLoose1() throws IOException {
581 		Map<String, Ref> orig, next;
582 		Ref orig_r, next_r;
583 
584 		writeLooseRef("refs/heads/master", A);
585 		orig = refdir.getRefs(RefDatabase.ALL);
586 
587 		writeLooseRef("refs/heads/next", B);
588 		next = refdir.getRefs(RefDatabase.ALL);
589 
590 		assertEquals(2, orig.size());
591 		assertEquals(3, next.size());
592 
593 		assertFalse(orig.containsKey("refs/heads/next"));
594 		assertTrue(next.containsKey("refs/heads/next"));
595 
596 		orig_r = orig.get("refs/heads/master");
597 		next_r = next.get("refs/heads/master");
598 		assertEquals(A, orig_r.getObjectId());
599 		assertSame("uses cached instance", orig_r, next_r);
600 		assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
601 		assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
602 
603 		next_r = next.get("refs/heads/next");
604 		assertSame(LOOSE, next_r.getStorage());
605 		assertEquals(B, next_r.getObjectId());
606 	}
607 
608 	@Test
609 	public void testGetRefs_DiscoversNewLoose2() throws IOException {
610 		Map<String, Ref> orig, next, news;
611 
612 		writeLooseRef("refs/heads/pu", A);
613 		orig = refdir.getRefs(RefDatabase.ALL);
614 
615 		writeLooseRef("refs/heads/new/B", B);
616 		news = refdir.getRefs("refs/heads/new/");
617 		next = refdir.getRefs(RefDatabase.ALL);
618 
619 		assertEquals(1, orig.size());
620 		assertEquals(2, next.size());
621 		assertEquals(1, news.size());
622 
623 		assertTrue(orig.containsKey("refs/heads/pu"));
624 		assertTrue(next.containsKey("refs/heads/pu"));
625 		assertFalse(news.containsKey("refs/heads/pu"));
626 
627 		assertFalse(orig.containsKey("refs/heads/new/B"));
628 		assertTrue(next.containsKey("refs/heads/new/B"));
629 		assertTrue(news.containsKey("B"));
630 	}
631 
632 	@Test
633 	public void testGetRefs_DiscoversModifiedLoose() throws IOException {
634 		Map<String, Ref> all;
635 
636 		writeLooseRef("refs/heads/master", A);
637 		all = refdir.getRefs(RefDatabase.ALL);
638 		assertEquals(A, all.get(HEAD).getObjectId());
639 
640 		writeLooseRef("refs/heads/master", B);
641 		all = refdir.getRefs(RefDatabase.ALL);
642 		assertEquals(B, all.get(HEAD).getObjectId());
643 	}
644 
645 	@Test
646 	public void testGetRef_DiscoversModifiedLoose() throws IOException {
647 		Map<String, Ref> all;
648 
649 		writeLooseRef("refs/heads/master", A);
650 		all = refdir.getRefs(RefDatabase.ALL);
651 		assertEquals(A, all.get(HEAD).getObjectId());
652 
653 		writeLooseRef("refs/heads/master", B);
654 
655 		Ref master = refdir.getRef("refs/heads/master");
656 		assertEquals(B, master.getObjectId());
657 	}
658 
659 	@Test
660 	public void testGetRefs_DiscoversDeletedLoose1() throws IOException {
661 		Map<String, Ref> orig, next;
662 		Ref orig_r, next_r;
663 
664 		writeLooseRef("refs/heads/B", B);
665 		writeLooseRef("refs/heads/master", A);
666 		orig = refdir.getRefs(RefDatabase.ALL);
667 
668 		deleteLooseRef("refs/heads/B");
669 		next = refdir.getRefs(RefDatabase.ALL);
670 
671 		assertEquals(3, orig.size());
672 		assertEquals(2, next.size());
673 
674 		assertTrue(orig.containsKey("refs/heads/B"));
675 		assertFalse(next.containsKey("refs/heads/B"));
676 
677 		orig_r = orig.get("refs/heads/master");
678 		next_r = next.get("refs/heads/master");
679 		assertEquals(A, orig_r.getObjectId());
680 		assertSame("uses cached instance", orig_r, next_r);
681 		assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
682 		assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
683 
684 		orig_r = orig.get("refs/heads/B");
685 		assertSame(LOOSE, orig_r.getStorage());
686 		assertEquals(B, orig_r.getObjectId());
687 	}
688 
689 	@Test
690 	public void testGetRef_DiscoversDeletedLoose() throws IOException {
691 		Map<String, Ref> all;
692 
693 		writeLooseRef("refs/heads/master", A);
694 		all = refdir.getRefs(RefDatabase.ALL);
695 		assertEquals(A, all.get(HEAD).getObjectId());
696 
697 		deleteLooseRef("refs/heads/master");
698 		assertNull(refdir.getRef("refs/heads/master"));
699 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
700 	}
701 
702 	@Test
703 	public void testGetRefs_DiscoversDeletedLoose2() throws IOException {
704 		Map<String, Ref> orig, next;
705 
706 		writeLooseRef("refs/heads/master", A);
707 		writeLooseRef("refs/heads/pu", B);
708 		orig = refdir.getRefs(RefDatabase.ALL);
709 
710 		deleteLooseRef("refs/heads/pu");
711 		next = refdir.getRefs(RefDatabase.ALL);
712 
713 		assertEquals(3, orig.size());
714 		assertEquals(2, next.size());
715 
716 		assertTrue(orig.containsKey("refs/heads/pu"));
717 		assertFalse(next.containsKey("refs/heads/pu"));
718 	}
719 
720 	@Test
721 	public void testGetRefs_DiscoversDeletedLoose3() throws IOException {
722 		Map<String, Ref> orig, next;
723 
724 		writeLooseRef("refs/heads/master", A);
725 		writeLooseRef("refs/heads/next", B);
726 		writeLooseRef("refs/heads/pu", B);
727 		writeLooseRef("refs/tags/v1.0", v1_0);
728 		orig = refdir.getRefs(RefDatabase.ALL);
729 
730 		deleteLooseRef("refs/heads/pu");
731 		deleteLooseRef("refs/heads/next");
732 		next = refdir.getRefs(RefDatabase.ALL);
733 
734 		assertEquals(5, orig.size());
735 		assertEquals(3, next.size());
736 
737 		assertTrue(orig.containsKey("refs/heads/pu"));
738 		assertTrue(orig.containsKey("refs/heads/next"));
739 		assertFalse(next.containsKey("refs/heads/pu"));
740 		assertFalse(next.containsKey("refs/heads/next"));
741 	}
742 
743 	@Test
744 	public void testGetRefs_DiscoversDeletedLoose4() throws IOException {
745 		Map<String, Ref> orig, next;
746 		Ref orig_r, next_r;
747 
748 		writeLooseRef("refs/heads/B", B);
749 		writeLooseRef("refs/heads/master", A);
750 		orig = refdir.getRefs(RefDatabase.ALL);
751 
752 		deleteLooseRef("refs/heads/master");
753 		next = refdir.getRefs("refs/heads/");
754 
755 		assertEquals(3, orig.size());
756 		assertEquals(1, next.size());
757 
758 		assertTrue(orig.containsKey("refs/heads/B"));
759 		assertTrue(orig.containsKey("refs/heads/master"));
760 		assertTrue(next.containsKey("B"));
761 		assertFalse(next.containsKey("master"));
762 
763 		orig_r = orig.get("refs/heads/B");
764 		next_r = next.get("B");
765 		assertEquals(B, orig_r.getObjectId());
766 		assertSame("uses cached instance", orig_r, next_r);
767 	}
768 
769 	@Test
770 	public void testGetRefs_DiscoversDeletedLoose5() throws IOException {
771 		Map<String, Ref> orig, next;
772 
773 		writeLooseRef("refs/heads/master", A);
774 		writeLooseRef("refs/heads/pu", B);
775 		orig = refdir.getRefs(RefDatabase.ALL);
776 
777 		deleteLooseRef("refs/heads/pu");
778 		writeLooseRef("refs/tags/v1.0", v1_0);
779 		next = refdir.getRefs(RefDatabase.ALL);
780 
781 		assertEquals(3, orig.size());
782 		assertEquals(3, next.size());
783 
784 		assertTrue(orig.containsKey("refs/heads/pu"));
785 		assertFalse(orig.containsKey("refs/tags/v1.0"));
786 		assertFalse(next.containsKey("refs/heads/pu"));
787 		assertTrue(next.containsKey("refs/tags/v1.0"));
788 	}
789 
790 	@Test
791 	public void testGetRefs_SkipsLockFiles() throws IOException {
792 		Map<String, Ref> all;
793 
794 		writeLooseRef("refs/heads/master", A);
795 		writeLooseRef("refs/heads/pu.lock", B);
796 		all = refdir.getRefs(RefDatabase.ALL);
797 
798 		assertEquals(2, all.size());
799 
800 		assertTrue(all.containsKey(HEAD));
801 		assertTrue(all.containsKey("refs/heads/master"));
802 		assertFalse(all.containsKey("refs/heads/pu.lock"));
803 	}
804 
805 	@Test
806 	public void testGetRefs_CycleInSymbolicRef() throws IOException {
807 		Map<String, Ref> all;
808 		Ref r;
809 
810 		writeLooseRef("refs/1", "ref: refs/2\n");
811 		writeLooseRef("refs/2", "ref: refs/3\n");
812 		writeLooseRef("refs/3", "ref: refs/4\n");
813 		writeLooseRef("refs/4", "ref: refs/5\n");
814 		writeLooseRef("refs/5", "ref: refs/end\n");
815 		writeLooseRef("refs/end", A);
816 
817 		all = refdir.getRefs(RefDatabase.ALL);
818 		r = all.get("refs/1");
819 		assertNotNull("has 1", r);
820 
821 		assertEquals("refs/1", r.getName());
822 		assertEquals(A, r.getObjectId());
823 		assertTrue(r.isSymbolic());
824 
825 		r = r.getTarget();
826 		assertEquals("refs/2", r.getName());
827 		assertEquals(A, r.getObjectId());
828 		assertTrue(r.isSymbolic());
829 
830 		r = r.getTarget();
831 		assertEquals("refs/3", r.getName());
832 		assertEquals(A, r.getObjectId());
833 		assertTrue(r.isSymbolic());
834 
835 		r = r.getTarget();
836 		assertEquals("refs/4", r.getName());
837 		assertEquals(A, r.getObjectId());
838 		assertTrue(r.isSymbolic());
839 
840 		r = r.getTarget();
841 		assertEquals("refs/5", r.getName());
842 		assertEquals(A, r.getObjectId());
843 		assertTrue(r.isSymbolic());
844 
845 		r = r.getTarget();
846 		assertEquals("refs/end", r.getName());
847 		assertEquals(A, r.getObjectId());
848 		assertFalse(r.isSymbolic());
849 
850 		writeLooseRef("refs/5", "ref: refs/6\n");
851 		writeLooseRef("refs/6", "ref: refs/end\n");
852 		all = refdir.getRefs(RefDatabase.ALL);
853 		r = all.get("refs/1");
854 		assertNull("mising 1 due to cycle", r);
855 	}
856 
857 	@Test
858 	public void testGetRef_CycleInSymbolicRef() throws IOException {
859 		Ref r;
860 
861 		writeLooseRef("refs/1", "ref: refs/2\n");
862 		writeLooseRef("refs/2", "ref: refs/3\n");
863 		writeLooseRef("refs/3", "ref: refs/4\n");
864 		writeLooseRef("refs/4", "ref: refs/5\n");
865 		writeLooseRef("refs/5", "ref: refs/end\n");
866 		writeLooseRef("refs/end", A);
867 
868 		r = refdir.getRef("1");
869 		assertEquals("refs/1", r.getName());
870 		assertEquals(A, r.getObjectId());
871 		assertTrue(r.isSymbolic());
872 
873 		writeLooseRef("refs/5", "ref: refs/6\n");
874 		writeLooseRef("refs/6", "ref: refs/end\n");
875 
876 		r = refdir.getRef("1");
877 		assertNull("missing 1 due to cycle", r);
878 
879 		writeLooseRef("refs/heads/1", B);
880 
881 		r = refdir.getRef("1");
882 		assertEquals("refs/heads/1", r.getName());
883 		assertEquals(B, r.getObjectId());
884 		assertFalse(r.isSymbolic());
885 	}
886 
887 	@Test
888 	public void testGetRefs_PackedNotPeeled_Sorted() throws IOException {
889 		Map<String, Ref> all;
890 
891 		writePackedRefs("" + //
892 				A.name() + " refs/heads/master\n" + //
893 				B.name() + " refs/heads/other\n" + //
894 				v1_0.name() + " refs/tags/v1.0\n");
895 		all = refdir.getRefs(RefDatabase.ALL);
896 
897 		assertEquals(4, all.size());
898 		final Ref head = all.get(HEAD);
899 		final Ref master = all.get("refs/heads/master");
900 		final Ref other = all.get("refs/heads/other");
901 		final Ref tag = all.get("refs/tags/v1.0");
902 
903 		assertEquals(A, master.getObjectId());
904 		assertFalse(master.isPeeled());
905 		assertNull(master.getPeeledObjectId());
906 
907 		assertEquals(B, other.getObjectId());
908 		assertFalse(other.isPeeled());
909 		assertNull(other.getPeeledObjectId());
910 
911 		assertSame(master, head.getTarget());
912 		assertEquals(A, head.getObjectId());
913 		assertFalse(head.isPeeled());
914 		assertNull(head.getPeeledObjectId());
915 
916 		assertEquals(v1_0, tag.getObjectId());
917 		assertFalse(tag.isPeeled());
918 		assertNull(tag.getPeeledObjectId());
919 	}
920 
921 	@Test
922 	public void testGetRef_PackedNotPeeled_WrongSort() throws IOException {
923 		writePackedRefs("" + //
924 				v1_0.name() + " refs/tags/v1.0\n" + //
925 				B.name() + " refs/heads/other\n" + //
926 				A.name() + " refs/heads/master\n");
927 
928 		final Ref head = refdir.getRef(HEAD);
929 		final Ref master = refdir.getRef("refs/heads/master");
930 		final Ref other = refdir.getRef("refs/heads/other");
931 		final Ref tag = refdir.getRef("refs/tags/v1.0");
932 
933 		assertEquals(A, master.getObjectId());
934 		assertFalse(master.isPeeled());
935 		assertNull(master.getPeeledObjectId());
936 
937 		assertEquals(B, other.getObjectId());
938 		assertFalse(other.isPeeled());
939 		assertNull(other.getPeeledObjectId());
940 
941 		assertSame(master, head.getTarget());
942 		assertEquals(A, head.getObjectId());
943 		assertFalse(head.isPeeled());
944 		assertNull(head.getPeeledObjectId());
945 
946 		assertEquals(v1_0, tag.getObjectId());
947 		assertFalse(tag.isPeeled());
948 		assertNull(tag.getPeeledObjectId());
949 	}
950 
951 	@Test
952 	public void testGetRefs_PackedWithPeeled() throws IOException {
953 		Map<String, Ref> all;
954 
955 		writePackedRefs("# pack-refs with: peeled \n" + //
956 				A.name() + " refs/heads/master\n" + //
957 				B.name() + " refs/heads/other\n" + //
958 				v1_0.name() + " refs/tags/v1.0\n" + //
959 				"^" + v1_0.getObject().name() + "\n");
960 		all = refdir.getRefs(RefDatabase.ALL);
961 
962 		assertEquals(4, all.size());
963 		final Ref head = all.get(HEAD);
964 		final Ref master = all.get("refs/heads/master");
965 		final Ref other = all.get("refs/heads/other");
966 		final Ref tag = all.get("refs/tags/v1.0");
967 
968 		assertEquals(A, master.getObjectId());
969 		assertTrue(master.isPeeled());
970 		assertNull(master.getPeeledObjectId());
971 
972 		assertEquals(B, other.getObjectId());
973 		assertTrue(other.isPeeled());
974 		assertNull(other.getPeeledObjectId());
975 
976 		assertSame(master, head.getTarget());
977 		assertEquals(A, head.getObjectId());
978 		assertTrue(head.isPeeled());
979 		assertNull(head.getPeeledObjectId());
980 
981 		assertEquals(v1_0, tag.getObjectId());
982 		assertTrue(tag.isPeeled());
983 		assertEquals(v1_0.getObject(), tag.getPeeledObjectId());
984 	}
985 
986 	@Test
987 	public void test_repack() throws Exception {
988 		Map<String, Ref> all;
989 
990 		writePackedRefs("# pack-refs with: peeled \n" + //
991 				A.name() + " refs/heads/master\n" + //
992 				B.name() + " refs/heads/other\n" + //
993 				v1_0.name() + " refs/tags/v1.0\n" + //
994 				"^" + v1_0.getObject().name() + "\n");
995 		all = refdir.getRefs(RefDatabase.ALL);
996 
997 		assertEquals(4, all.size());
998 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
999 		assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage());
1000 		assertEquals(A.getId(), all.get("refs/heads/master").getObjectId());
1001 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1002 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1003 
1004 		repo.update("refs/heads/master", B.getId());
1005 		RevTag v0_1 = repo.tag("v0.1", A);
1006 		repo.update("refs/tags/v0.1", v0_1);
1007 
1008 		all = refdir.getRefs(RefDatabase.ALL);
1009 		assertEquals(5, all.size());
1010 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1011 		// Why isn't the next ref LOOSE_PACKED?
1012 		assertEquals(Storage.LOOSE, all.get("refs/heads/master")
1013 				.getStorage());
1014 		assertEquals(B.getId(), all.get("refs/heads/master").getObjectId());
1015 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1016 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1017 		assertEquals(Storage.LOOSE, all.get("refs/tags/v0.1").getStorage());
1018 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
1019 
1020 		all = refdir.getRefs(RefDatabase.ALL);
1021 		refdir.pack(new ArrayList<>(all.keySet()));
1022 
1023 		all = refdir.getRefs(RefDatabase.ALL);
1024 		assertEquals(5, all.size());
1025 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1026 		// Why isn't the next ref LOOSE_PACKED?
1027 		assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage());
1028 		assertEquals(B.getId(), all.get("refs/heads/master").getObjectId());
1029 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1030 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1031 		assertEquals(Storage.PACKED, all.get("refs/tags/v0.1").getStorage());
1032 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
1033 	}
1034 
1035 	@Test
1036 	public void testGetRef_EmptyDatabase() throws IOException {
1037 		Ref r;
1038 
1039 		r = refdir.getRef(HEAD);
1040 		assertTrue(r.isSymbolic());
1041 		assertSame(LOOSE, r.getStorage());
1042 		assertEquals("refs/heads/master", r.getTarget().getName());
1043 		assertSame(NEW, r.getTarget().getStorage());
1044 		assertNull(r.getTarget().getObjectId());
1045 
1046 		assertNull(refdir.getRef("refs/heads/master"));
1047 		assertNull(refdir.getRef("refs/tags/v1.0"));
1048 		assertNull(refdir.getRef("FETCH_HEAD"));
1049 		assertNull(refdir.getRef("NOT.A.REF.NAME"));
1050 		assertNull(refdir.getRef("master"));
1051 		assertNull(refdir.getRef("v1.0"));
1052 	}
1053 
1054 	@Test
1055 	public void testExactRef_EmptyDatabase() throws IOException {
1056 		Ref r;
1057 
1058 		r = refdir.exactRef(HEAD);
1059 		assertTrue(r.isSymbolic());
1060 		assertSame(LOOSE, r.getStorage());
1061 		assertEquals("refs/heads/master", r.getTarget().getName());
1062 		assertSame(NEW, r.getTarget().getStorage());
1063 		assertNull(r.getTarget().getObjectId());
1064 
1065 		assertNull(refdir.exactRef("refs/heads/master"));
1066 		assertNull(refdir.exactRef("refs/tags/v1.0"));
1067 		assertNull(refdir.exactRef("FETCH_HEAD"));
1068 		assertNull(refdir.exactRef("NOT.A.REF.NAME"));
1069 		assertNull(refdir.exactRef("master"));
1070 		assertNull(refdir.exactRef("v1.0"));
1071 	}
1072 
1073 	@Test
1074 	public void testGetRef_FetchHead() throws IOException {
1075 		// This is an odd special case where we need to make sure we read
1076 		// exactly the first 40 bytes of the file and nothing further on
1077 		// that line, or the remainder of the file.
1078 		write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
1079 				+ "\tnot-for-merge"
1080 				+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
1081 
1082 		Ref r = refdir.getRef("FETCH_HEAD");
1083 		assertFalse(r.isSymbolic());
1084 		assertEquals(A, r.getObjectId());
1085 		assertEquals("FETCH_HEAD", r.getName());
1086 		assertFalse(r.isPeeled());
1087 		assertNull(r.getPeeledObjectId());
1088 	}
1089 
1090 	@Test
1091 	public void testExactRef_FetchHead() throws IOException {
1092 		// This is an odd special case where we need to make sure we read
1093 		// exactly the first 40 bytes of the file and nothing further on
1094 		// that line, or the remainder of the file.
1095 		write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
1096 				+ "\tnot-for-merge"
1097 				+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
1098 
1099 		Ref r = refdir.exactRef("FETCH_HEAD");
1100 		assertFalse(r.isSymbolic());
1101 		assertEquals(A, r.getObjectId());
1102 		assertEquals("FETCH_HEAD", r.getName());
1103 		assertFalse(r.isPeeled());
1104 		assertNull(r.getPeeledObjectId());
1105 	}
1106 
1107 	@Test
1108 	public void testGetRef_AnyHeadWithGarbage() throws IOException {
1109 		write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name()
1110 				+ "012345 . this is not a standard reference\n"
1111 				+ "#and even more junk\n");
1112 
1113 		Ref r = refdir.getRef("refs/heads/A");
1114 		assertFalse(r.isSymbolic());
1115 		assertEquals(A, r.getObjectId());
1116 		assertEquals("refs/heads/A", r.getName());
1117 		assertFalse(r.isPeeled());
1118 		assertNull(r.getPeeledObjectId());
1119 	}
1120 
1121 	@Test
1122 	public void testGetRefs_CorruptSymbolicReference() throws IOException {
1123 		String name = "refs/heads/A";
1124 		writeLooseRef(name, "ref: \n");
1125 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
1126 	}
1127 
1128 	@Test
1129 	public void testGetRef_CorruptSymbolicReference() throws IOException {
1130 		String name = "refs/heads/A";
1131 		writeLooseRef(name, "ref: \n");
1132 		try {
1133 			refdir.getRef(name);
1134 			fail("read an invalid reference");
1135 		} catch (IOException err) {
1136 			String msg = err.getMessage();
1137 			assertEquals("Not a ref: " + name + ": ref:", msg);
1138 		}
1139 	}
1140 
1141 	@Test
1142 	public void testGetRefs_CorruptObjectIdReference() throws IOException {
1143 		String name = "refs/heads/A";
1144 		String content = "zoo" + A.name();
1145 		writeLooseRef(name, content + "\n");
1146 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
1147 	}
1148 
1149 	@Test
1150 	public void testGetRef_CorruptObjectIdReference() throws IOException {
1151 		String name = "refs/heads/A";
1152 		String content = "zoo" + A.name();
1153 		writeLooseRef(name, content + "\n");
1154 		try {
1155 			refdir.getRef(name);
1156 			fail("read an invalid reference");
1157 		} catch (IOException err) {
1158 			String msg = err.getMessage();
1159 			assertEquals("Not a ref: " + name + ": " + content, msg);
1160 		}
1161 	}
1162 
1163 	@Test
1164 	public void testIsNameConflicting() throws IOException {
1165 		writeLooseRef("refs/heads/a/b", A);
1166 		writePackedRef("refs/heads/q", B);
1167 
1168 		// new references cannot replace an existing container
1169 		assertTrue(refdir.isNameConflicting("refs"));
1170 		assertTrue(refdir.isNameConflicting("refs/heads"));
1171 		assertTrue(refdir.isNameConflicting("refs/heads/a"));
1172 
1173 		// existing reference is not conflicting
1174 		assertFalse(refdir.isNameConflicting("refs/heads/a/b"));
1175 
1176 		// new references are not conflicting
1177 		assertFalse(refdir.isNameConflicting("refs/heads/a/d"));
1178 		assertFalse(refdir.isNameConflicting("refs/heads/master"));
1179 
1180 		// existing reference must not be used as a container
1181 		assertTrue(refdir.isNameConflicting("refs/heads/a/b/c"));
1182 		assertTrue(refdir.isNameConflicting("refs/heads/q/master"));
1183 	}
1184 
1185 	@Test
1186 	public void testPeelLooseTag() throws IOException {
1187 		writeLooseRef("refs/tags/v1_0", v1_0);
1188 		writeLooseRef("refs/tags/current", "ref: refs/tags/v1_0\n");
1189 
1190 		final Ref tag = refdir.getRef("refs/tags/v1_0");
1191 		final Ref cur = refdir.getRef("refs/tags/current");
1192 
1193 		assertEquals(v1_0, tag.getObjectId());
1194 		assertFalse(tag.isSymbolic());
1195 		assertFalse(tag.isPeeled());
1196 		assertNull(tag.getPeeledObjectId());
1197 
1198 		assertEquals(v1_0, cur.getObjectId());
1199 		assertTrue(cur.isSymbolic());
1200 		assertFalse(cur.isPeeled());
1201 		assertNull(cur.getPeeledObjectId());
1202 
1203 		final Ref tag_p = refdir.peel(tag);
1204 		final Ref cur_p = refdir.peel(cur);
1205 
1206 		assertNotSame(tag, tag_p);
1207 		assertFalse(tag_p.isSymbolic());
1208 		assertTrue(tag_p.isPeeled());
1209 		assertEquals(v1_0, tag_p.getObjectId());
1210 		assertEquals(v1_0.getObject(), tag_p.getPeeledObjectId());
1211 		assertSame(tag_p, refdir.peel(tag_p));
1212 
1213 		assertNotSame(cur, cur_p);
1214 		assertEquals("refs/tags/current", cur_p.getName());
1215 		assertTrue(cur_p.isSymbolic());
1216 		assertEquals("refs/tags/v1_0", cur_p.getTarget().getName());
1217 		assertTrue(cur_p.isPeeled());
1218 		assertEquals(v1_0, cur_p.getObjectId());
1219 		assertEquals(v1_0.getObject(), cur_p.getPeeledObjectId());
1220 
1221 		// reuses cached peeling later, but not immediately due to
1222 		// the implementation so we have to fetch it once.
1223 		final Ref tag_p2 = refdir.getRef("refs/tags/v1_0");
1224 		assertFalse(tag_p2.isSymbolic());
1225 		assertTrue(tag_p2.isPeeled());
1226 		assertEquals(v1_0, tag_p2.getObjectId());
1227 		assertEquals(v1_0.getObject(), tag_p2.getPeeledObjectId());
1228 
1229 		assertSame(tag_p2, refdir.getRef("refs/tags/v1_0"));
1230 		assertSame(tag_p2, refdir.getRef("refs/tags/current").getTarget());
1231 		assertSame(tag_p2, refdir.peel(tag_p2));
1232 	}
1233 
1234 	@Test
1235 	public void testPeelCommit() throws IOException {
1236 		writeLooseRef("refs/heads/master", A);
1237 
1238 		Ref master = refdir.getRef("refs/heads/master");
1239 		assertEquals(A, master.getObjectId());
1240 		assertFalse(master.isPeeled());
1241 		assertNull(master.getPeeledObjectId());
1242 
1243 		Ref master_p = refdir.peel(master);
1244 		assertNotSame(master, master_p);
1245 		assertEquals(A, master_p.getObjectId());
1246 		assertTrue(master_p.isPeeled());
1247 		assertNull(master_p.getPeeledObjectId());
1248 
1249 		// reuses cached peeling later, but not immediately due to
1250 		// the implementation so we have to fetch it once.
1251 		Ref master_p2 = refdir.getRef("refs/heads/master");
1252 		assertNotSame(master, master_p2);
1253 		assertEquals(A, master_p2.getObjectId());
1254 		assertTrue(master_p2.isPeeled());
1255 		assertNull(master_p2.getPeeledObjectId());
1256 		assertSame(master_p2, refdir.peel(master_p2));
1257 	}
1258 
1259 	@Test
1260 	public void testRefsChangedStackOverflow() throws Exception {
1261 		final FileRepository newRepo = createBareRepository();
1262 		final RefDatabase refDb = newRepo.getRefDatabase();
1263 		File packedRefs = new File(newRepo.getDirectory(), "packed-refs");
1264 		assertTrue(packedRefs.createNewFile());
1265 		final AtomicReference<StackOverflowError> error = new AtomicReference<>();
1266 		final AtomicReference<IOException> exception = new AtomicReference<>();
1267 		final AtomicInteger changeCount = new AtomicInteger();
1268 		newRepo.getListenerList().addRefsChangedListener(
1269 				new RefsChangedListener() {
1270 
1271 					@Override
1272 					public void onRefsChanged(RefsChangedEvent event) {
1273 						try {
1274 							refDb.getRefsByPrefix("ref");
1275 							changeCount.incrementAndGet();
1276 						} catch (StackOverflowError soe) {
1277 							error.set(soe);
1278 						} catch (IOException ioe) {
1279 							exception.set(ioe);
1280 						}
1281 					}
1282 				});
1283 		refDb.getRefsByPrefix("ref");
1284 		refDb.getRefsByPrefix("ref");
1285 		assertNull(error.get());
1286 		assertNull(exception.get());
1287 		assertEquals(1, changeCount.get());
1288 	}
1289 
1290 	@Test
1291 	public void testPackedRefsLockFailure() throws Exception {
1292 		writeLooseRef("refs/heads/master", A);
1293 		refdir.setRetrySleepMs(Arrays.asList(0, 0));
1294 		LockFile myLock = refdir.lockPackedRefs();
1295 		try {
1296 			refdir.pack(Arrays.asList("refs/heads/master"));
1297 			fail("expected LockFailedException");
1298 		} catch (LockFailedException e) {
1299 			assertEquals(refdir.packedRefsFile.getPath(), e.getFile().getPath());
1300 		} finally {
1301 			myLock.unlock();
1302 		}
1303 		Ref ref = refdir.getRef("refs/heads/master");
1304 		assertEquals(Storage.LOOSE, ref.getStorage());
1305 	}
1306 
1307 	private void writeLooseRef(String name, AnyObjectId id) throws IOException {
1308 		writeLooseRef(name, id.name() + "\n");
1309 	}
1310 
1311 	private void writeLooseRef(String name, String content) throws IOException {
1312 		write(new File(diskRepo.getDirectory(), name), content);
1313 	}
1314 
1315 	private void writePackedRef(String name, AnyObjectId id) throws IOException {
1316 		writePackedRefs(id.name() + " " + name + "\n");
1317 	}
1318 
1319 	private void writePackedRefs(String content) throws IOException {
1320 		File pr = new File(diskRepo.getDirectory(), "packed-refs");
1321 		write(pr, content);
1322 
1323 		final long now = System.currentTimeMillis();
1324 		final int oneHourAgo = 3600 * 1000;
1325 		pr.setLastModified(now - oneHourAgo);
1326 	}
1327 
1328 	private void deleteLooseRef(String name) {
1329 		File path = new File(diskRepo.getDirectory(), name);
1330 		assertTrue("deleted " + name, path.delete());
1331 	}
1332 }