View Javadoc
1   /*
2    * Copyright (C) 2010, 2013 Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.internal.storage.file;
12  
13  import static org.eclipse.jgit.lib.Constants.HEAD;
14  import static org.eclipse.jgit.lib.Constants.R_HEADS;
15  import static org.eclipse.jgit.lib.Constants.R_TAGS;
16  import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
17  import static org.eclipse.jgit.lib.Ref.Storage.NEW;
18  import static org.junit.Assert.assertArrayEquals;
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertNotNull;
22  import static org.junit.Assert.assertNotSame;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.Assert.assertSame;
25  import static org.junit.Assert.assertTrue;
26  import static org.junit.Assert.fail;
27  
28  import java.io.File;
29  import java.io.IOException;
30  import java.time.Instant;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Set;
37  import java.util.concurrent.atomic.AtomicInteger;
38  import java.util.concurrent.atomic.AtomicReference;
39  
40  import org.eclipse.jgit.errors.LockFailedException;
41  import org.eclipse.jgit.events.ListenerHandle;
42  import org.eclipse.jgit.events.RefsChangedEvent;
43  import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
44  import org.eclipse.jgit.junit.Repeat;
45  import org.eclipse.jgit.junit.TestRepository;
46  import org.eclipse.jgit.lib.AnyObjectId;
47  import org.eclipse.jgit.lib.Ref;
48  import org.eclipse.jgit.lib.Ref.Storage;
49  import org.eclipse.jgit.lib.RefDatabase;
50  import org.eclipse.jgit.lib.Repository;
51  import org.eclipse.jgit.revwalk.RevCommit;
52  import org.eclipse.jgit.revwalk.RevTag;
53  import org.eclipse.jgit.util.FS;
54  import org.junit.Before;
55  import org.junit.Test;
56  
57  @SuppressWarnings("boxing")
58  public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
59  	private Repository diskRepo;
60  
61  	private TestRepository<Repository> repo;
62  
63  	private RefDirectory refdir;
64  
65  	private RevCommit A;
66  
67  	private RevCommit B;
68  
69  	private RevTag v1_0;
70  
71  	@Override
72  	@Before
73  	public void setUp() throws Exception {
74  		super.setUp();
75  
76  		diskRepo = createBareRepository();
77  		refdir = (RefDirectory) diskRepo.getRefDatabase();
78  
79  		repo = new TestRepository<>(diskRepo);
80  		A = repo.commit().create();
81  		B = repo.commit(repo.getRevWalk().parseCommit(A));
82  		v1_0 = repo.tag("v1_0", B);
83  		repo.getRevWalk().parseBody(v1_0);
84  	}
85  
86  	@Test
87  	public void testCreate() throws IOException {
88  		// setUp above created the directory. We just have to test it.
89  		File d = diskRepo.getDirectory();
90  		assertSame(diskRepo, refdir.getRepository());
91  
92  		assertTrue(new File(d, "refs").isDirectory());
93  		assertTrue(new File(d, "logs").isDirectory());
94  		assertTrue(new File(d, "logs/refs").isDirectory());
95  		assertFalse(new File(d, "packed-refs").exists());
96  
97  		assertTrue(new File(d, "refs/heads").isDirectory());
98  		assertTrue(new File(d, "refs/tags").isDirectory());
99  		assertEquals(2, new File(d, "refs").list().length);
100 		assertEquals(0, new File(d, "refs/heads").list().length);
101 		assertEquals(0, new File(d, "refs/tags").list().length);
102 
103 		assertTrue(new File(d, "logs/refs/heads").isDirectory());
104 		assertFalse(new File(d, "logs/HEAD").exists());
105 		assertEquals(0, new File(d, "logs/refs/heads").list().length);
106 
107 		assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
108 	}
109 
110 	@Test(expected = UnsupportedOperationException.class)
111 	public void testVersioningNotImplemented_exactRef() throws IOException {
112 		assertFalse(refdir.hasVersioning());
113 
114 		Ref ref = refdir.exactRef(HEAD);
115 		assertNotNull(ref);
116 		ref.getUpdateIndex(); // Not implemented on FS
117 	}
118 
119 	@Test
120 	public void testVersioningNotImplemented_getRefs() throws Exception {
121 		assertFalse(refdir.hasVersioning());
122 
123 		RevCommit C = repo.commit().parent(B).create();
124 		repo.update("master", C);
125 		List<Ref> refs = refdir.getRefs();
126 
127 		for (Ref ref : refs) {
128 			try {
129 				ref.getUpdateIndex();
130 				fail("FS doesn't implement ref versioning");
131 			} catch (UnsupportedOperationException e) {
132 				// ok
133 			}
134 		}
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 testGetRefs_ExcludingPrefixes() throws IOException {
359 		writeLooseRef("refs/heads/A", A);
360 		writeLooseRef("refs/heads/B", B);
361 		writeLooseRef("refs/tags/tag", A);
362 		writeLooseRef("refs/something/something", B);
363 		writeLooseRef("refs/aaa/aaa", A);
364 
365 		Set<String> toExclude = new HashSet<>();
366 		toExclude.add("refs/aaa/");
367 		toExclude.add("refs/heads/");
368 		List<Ref> refs = refdir.getRefsByPrefixWithExclusions(RefDatabase.ALL, toExclude);
369 
370 		assertEquals(2, refs.size());
371 		assertTrue(refs.contains(refdir.exactRef("refs/tags/tag")));
372 		assertTrue(refs.contains(refdir.exactRef("refs/something/something")));
373 	}
374 
375 	@Test
376 	public void testFirstExactRef_IgnoresGarbageRef() throws IOException {
377 		writeLooseRef("refs/heads/A", A);
378 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
379 
380 		Ref a = refdir.firstExactRef("refs/heads/bad", "refs/heads/A");
381 		assertEquals("refs/heads/A", a.getName());
382 		assertEquals(A, a.getObjectId());
383 	}
384 
385 	@Test
386 	public void testExactRef_IgnoresGarbageRef() throws IOException {
387 		writeLooseRef("refs/heads/A", A);
388 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
389 
390 		Map<String, Ref> refs =
391 				refdir.exactRef("refs/heads/bad", "refs/heads/A");
392 
393 		assertNull("no refs/heads/bad", refs.get("refs/heads/bad"));
394 
395 		Ref a = refs.get("refs/heads/A");
396 		assertEquals("refs/heads/A", a.getName());
397 		assertEquals(A, a.getObjectId());
398 
399 		assertEquals(1, refs.size());
400 	}
401 
402 	@Test
403 	public void testGetRefs_InvalidName() throws IOException {
404 		writeLooseRef("refs/heads/A", A);
405 
406 		assertTrue("empty refs/heads", refdir.getRefs("refs/heads").isEmpty());
407 		assertTrue("empty objects", refdir.getRefs("objects").isEmpty());
408 		assertTrue("empty objects/", refdir.getRefs("objects/").isEmpty());
409 	}
410 
411 	@Test
412 	public void testReadNotExistingBranchConfig() throws IOException {
413 		assertNull("find branch config", refdir.findRef("config"));
414 		assertNull("find branch config", refdir.findRef("refs/heads/config"));
415 	}
416 
417 	@Test
418 	public void testReadBranchConfig() throws IOException {
419 		writeLooseRef("refs/heads/config", A);
420 
421 		assertNotNull("find branch config", refdir.findRef("config"));
422 	}
423 
424 	@Test
425 	public void testGetRefs_HeadsOnly_AllLoose() throws IOException {
426 		Map<String, Ref> heads;
427 		Ref a, b;
428 
429 		writeLooseRef("refs/heads/A", A);
430 		writeLooseRef("refs/heads/B", B);
431 		writeLooseRef("refs/tags/v1.0", v1_0);
432 
433 		heads = refdir.getRefs(R_HEADS);
434 		assertEquals(2, heads.size());
435 
436 		a = heads.get("A");
437 		b = heads.get("B");
438 
439 		assertEquals("refs/heads/A", a.getName());
440 		assertEquals("refs/heads/B", b.getName());
441 
442 		assertEquals(A, a.getObjectId());
443 		assertEquals(B, b.getObjectId());
444 	}
445 
446 	@Test
447 	public void testGetRefs_HeadsOnly_AllPacked1() throws IOException {
448 		Map<String, Ref> heads;
449 		Ref a;
450 
451 		deleteLooseRef(HEAD);
452 		writePackedRef("refs/heads/A", A);
453 
454 		heads = refdir.getRefs(R_HEADS);
455 		assertEquals(1, heads.size());
456 
457 		a = heads.get("A");
458 
459 		assertEquals("refs/heads/A", a.getName());
460 		assertEquals(A, a.getObjectId());
461 	}
462 
463 	@Test
464 	public void testGetRefs_HeadsOnly_SymrefToPacked() throws IOException {
465 		Map<String, Ref> heads;
466 		Ref master, other;
467 
468 		writeLooseRef("refs/heads/other", "ref: refs/heads/master\n");
469 		writePackedRef("refs/heads/master", A);
470 
471 		heads = refdir.getRefs(R_HEADS);
472 		assertEquals(2, heads.size());
473 
474 		master = heads.get("master");
475 		other = heads.get("other");
476 
477 		assertEquals("refs/heads/master", master.getName());
478 		assertEquals(A, master.getObjectId());
479 
480 		assertEquals("refs/heads/other", other.getName());
481 		assertEquals(A, other.getObjectId());
482 		assertSame(master, other.getTarget());
483 	}
484 
485 	@Test
486 	public void testGetRefs_HeadsOnly_Mixed() throws IOException {
487 		Map<String, Ref> heads;
488 		Ref a, b;
489 
490 		writeLooseRef("refs/heads/A", A);
491 		writeLooseRef("refs/heads/B", B);
492 		writePackedRef("refs/tags/v1.0", v1_0);
493 
494 		heads = refdir.getRefs(R_HEADS);
495 		assertEquals(2, heads.size());
496 
497 		a = heads.get("A");
498 		b = heads.get("B");
499 
500 		assertEquals("refs/heads/A", a.getName());
501 		assertEquals("refs/heads/B", b.getName());
502 
503 		assertEquals(A, a.getObjectId());
504 		assertEquals(B, b.getObjectId());
505 	}
506 
507 	@Test
508 	public void testFirstExactRef_Mixed() throws IOException {
509 		writeLooseRef("refs/heads/A", A);
510 		writePackedRef("refs/tags/v1.0", v1_0);
511 
512 		Ref a = refdir.firstExactRef("refs/heads/A", "refs/tags/v1.0");
513 		Ref one = refdir.firstExactRef("refs/tags/v1.0", "refs/heads/A");
514 
515 		assertEquals("refs/heads/A", a.getName());
516 		assertEquals("refs/tags/v1.0", one.getName());
517 
518 		assertEquals(A, a.getObjectId());
519 		assertEquals(v1_0, one.getObjectId());
520 	}
521 
522 	@Test
523 	public void testGetRefs_TagsOnly_AllLoose() throws IOException {
524 		Map<String, Ref> tags;
525 		Ref a;
526 
527 		writeLooseRef("refs/heads/A", A);
528 		writeLooseRef("refs/tags/v1.0", v1_0);
529 
530 		tags = refdir.getRefs(R_TAGS);
531 		assertEquals(1, tags.size());
532 
533 		a = tags.get("v1.0");
534 
535 		assertEquals("refs/tags/v1.0", a.getName());
536 		assertEquals(v1_0, a.getObjectId());
537 	}
538 
539 	@Test
540 	public void testGetRefs_LooseSortedCorrectly() throws IOException {
541 		Map<String, Ref> refs;
542 
543 		writeLooseRef("refs/heads/project1/A", A);
544 		writeLooseRef("refs/heads/project1-B", B);
545 
546 		refs = refdir.getRefs(RefDatabase.ALL);
547 		assertEquals(2, refs.size());
548 		assertEquals(A, refs.get("refs/heads/project1/A").getObjectId());
549 		assertEquals(B, refs.get("refs/heads/project1-B").getObjectId());
550 	}
551 
552 	@Test
553 	public void testGetRefs_LooseSorting_Bug_348834() throws IOException {
554 		Map<String, Ref> refs;
555 
556 		writeLooseRef("refs/heads/my/a+b", A);
557 		writeLooseRef("refs/heads/my/a/b/c", B);
558 
559 		final int[] count = new int[1];
560 
561 		ListenerHandle listener = Repository.getGlobalListenerList()
562 				.addRefsChangedListener((RefsChangedEvent event) -> {
563 					count[0]++;
564 				});
565 
566 		refs = refdir.getRefs(RefDatabase.ALL);
567 		refs = refdir.getRefs(RefDatabase.ALL);
568 		listener.remove();
569 		assertEquals(1, count[0]); // Bug 348834 multiple RefsChangedEvents
570 		assertEquals(2, refs.size());
571 		assertEquals(A, refs.get("refs/heads/my/a+b").getObjectId());
572 		assertEquals(B, refs.get("refs/heads/my/a/b/c").getObjectId());
573 
574 	}
575 
576 	@Test
577 	public void testGetRefs_TagsOnly_AllPacked() throws IOException {
578 		Map<String, Ref> tags;
579 		Ref a;
580 
581 		deleteLooseRef(HEAD);
582 		writePackedRef("refs/tags/v1.0", v1_0);
583 
584 		tags = refdir.getRefs(R_TAGS);
585 		assertEquals(1, tags.size());
586 
587 		a = tags.get("v1.0");
588 
589 		assertEquals("refs/tags/v1.0", a.getName());
590 		assertEquals(v1_0, a.getObjectId());
591 	}
592 
593 	@Test
594 	public void testGetRefs_DiscoversNewLoose1() throws IOException {
595 		Map<String, Ref> orig, next;
596 		Ref orig_r, next_r;
597 
598 		writeLooseRef("refs/heads/master", A);
599 		orig = refdir.getRefs(RefDatabase.ALL);
600 
601 		writeLooseRef("refs/heads/next", B);
602 		next = refdir.getRefs(RefDatabase.ALL);
603 
604 		assertEquals(2, orig.size());
605 		assertEquals(3, next.size());
606 
607 		assertFalse(orig.containsKey("refs/heads/next"));
608 		assertTrue(next.containsKey("refs/heads/next"));
609 
610 		orig_r = orig.get("refs/heads/master");
611 		next_r = next.get("refs/heads/master");
612 		assertEquals(A, orig_r.getObjectId());
613 		assertSame("uses cached instance", orig_r, next_r);
614 		assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
615 		assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
616 
617 		next_r = next.get("refs/heads/next");
618 		assertSame(LOOSE, next_r.getStorage());
619 		assertEquals(B, next_r.getObjectId());
620 	}
621 
622 	@Test
623 	public void testGetRefs_DiscoversNewLoose2() throws IOException {
624 		Map<String, Ref> orig, next, news;
625 
626 		writeLooseRef("refs/heads/pu", A);
627 		orig = refdir.getRefs(RefDatabase.ALL);
628 
629 		writeLooseRef("refs/heads/new/B", B);
630 		news = refdir.getRefs("refs/heads/new/");
631 		next = refdir.getRefs(RefDatabase.ALL);
632 
633 		assertEquals(1, orig.size());
634 		assertEquals(2, next.size());
635 		assertEquals(1, news.size());
636 
637 		assertTrue(orig.containsKey("refs/heads/pu"));
638 		assertTrue(next.containsKey("refs/heads/pu"));
639 		assertFalse(news.containsKey("refs/heads/pu"));
640 
641 		assertFalse(orig.containsKey("refs/heads/new/B"));
642 		assertTrue(next.containsKey("refs/heads/new/B"));
643 		assertTrue(news.containsKey("B"));
644 	}
645 
646 	@Test
647 	public void testGetRefs_DiscoversModifiedLoose() throws IOException {
648 		Map<String, Ref> all;
649 
650 		writeLooseRef("refs/heads/master", A);
651 		all = refdir.getRefs(RefDatabase.ALL);
652 		assertEquals(A, all.get(HEAD).getObjectId());
653 
654 		writeLooseRef("refs/heads/master", B);
655 		all = refdir.getRefs(RefDatabase.ALL);
656 		assertEquals(B, all.get(HEAD).getObjectId());
657 	}
658 
659 	@Repeat(n = 100, abortOnFailure = false)
660 	@Test
661 	public void testFindRef_DiscoversModifiedLoose() throws IOException {
662 		Map<String, Ref> all;
663 
664 		writeLooseRef("refs/heads/master", A);
665 		all = refdir.getRefs(RefDatabase.ALL);
666 		assertEquals(A, all.get(HEAD).getObjectId());
667 
668 		writeLooseRef("refs/heads/master", B);
669 
670 		Ref master = refdir.findRef("refs/heads/master");
671 		assertEquals(B, master.getObjectId());
672 	}
673 
674 	@Test
675 	public void testGetRefs_DiscoversDeletedLoose1() throws IOException {
676 		Map<String, Ref> orig, next;
677 		Ref orig_r, next_r;
678 
679 		writeLooseRef("refs/heads/B", B);
680 		writeLooseRef("refs/heads/master", A);
681 		orig = refdir.getRefs(RefDatabase.ALL);
682 
683 		deleteLooseRef("refs/heads/B");
684 		next = refdir.getRefs(RefDatabase.ALL);
685 
686 		assertEquals(3, orig.size());
687 		assertEquals(2, next.size());
688 
689 		assertTrue(orig.containsKey("refs/heads/B"));
690 		assertFalse(next.containsKey("refs/heads/B"));
691 
692 		orig_r = orig.get("refs/heads/master");
693 		next_r = next.get("refs/heads/master");
694 		assertEquals(A, orig_r.getObjectId());
695 		assertSame("uses cached instance", orig_r, next_r);
696 		assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
697 		assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
698 
699 		orig_r = orig.get("refs/heads/B");
700 		assertSame(LOOSE, orig_r.getStorage());
701 		assertEquals(B, orig_r.getObjectId());
702 	}
703 
704 	@Test
705 	public void testFindRef_DiscoversDeletedLoose() throws IOException {
706 		Map<String, Ref> all;
707 
708 		writeLooseRef("refs/heads/master", A);
709 		all = refdir.getRefs(RefDatabase.ALL);
710 		assertEquals(A, all.get(HEAD).getObjectId());
711 
712 		deleteLooseRef("refs/heads/master");
713 		assertNull(refdir.findRef("refs/heads/master"));
714 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
715 	}
716 
717 	@Test
718 	public void testGetRefs_DiscoversDeletedLoose2() throws IOException {
719 		Map<String, Ref> orig, next;
720 
721 		writeLooseRef("refs/heads/master", A);
722 		writeLooseRef("refs/heads/pu", B);
723 		orig = refdir.getRefs(RefDatabase.ALL);
724 
725 		deleteLooseRef("refs/heads/pu");
726 		next = refdir.getRefs(RefDatabase.ALL);
727 
728 		assertEquals(3, orig.size());
729 		assertEquals(2, next.size());
730 
731 		assertTrue(orig.containsKey("refs/heads/pu"));
732 		assertFalse(next.containsKey("refs/heads/pu"));
733 	}
734 
735 	@Test
736 	public void testGetRefs_DiscoversDeletedLoose3() throws IOException {
737 		Map<String, Ref> orig, next;
738 
739 		writeLooseRef("refs/heads/master", A);
740 		writeLooseRef("refs/heads/next", B);
741 		writeLooseRef("refs/heads/pu", B);
742 		writeLooseRef("refs/tags/v1.0", v1_0);
743 		orig = refdir.getRefs(RefDatabase.ALL);
744 
745 		deleteLooseRef("refs/heads/pu");
746 		deleteLooseRef("refs/heads/next");
747 		next = refdir.getRefs(RefDatabase.ALL);
748 
749 		assertEquals(5, orig.size());
750 		assertEquals(3, next.size());
751 
752 		assertTrue(orig.containsKey("refs/heads/pu"));
753 		assertTrue(orig.containsKey("refs/heads/next"));
754 		assertFalse(next.containsKey("refs/heads/pu"));
755 		assertFalse(next.containsKey("refs/heads/next"));
756 	}
757 
758 	@Test
759 	public void testGetRefs_DiscoversDeletedLoose4() throws IOException {
760 		Map<String, Ref> orig, next;
761 		Ref orig_r, next_r;
762 
763 		writeLooseRef("refs/heads/B", B);
764 		writeLooseRef("refs/heads/master", A);
765 		orig = refdir.getRefs(RefDatabase.ALL);
766 
767 		deleteLooseRef("refs/heads/master");
768 		next = refdir.getRefs("refs/heads/");
769 
770 		assertEquals(3, orig.size());
771 		assertEquals(1, next.size());
772 
773 		assertTrue(orig.containsKey("refs/heads/B"));
774 		assertTrue(orig.containsKey("refs/heads/master"));
775 		assertTrue(next.containsKey("B"));
776 		assertFalse(next.containsKey("master"));
777 
778 		orig_r = orig.get("refs/heads/B");
779 		next_r = next.get("B");
780 		assertEquals(B, orig_r.getObjectId());
781 		assertSame("uses cached instance", orig_r, next_r);
782 	}
783 
784 	@Test
785 	public void testGetRefs_DiscoversDeletedLoose5() throws IOException {
786 		Map<String, Ref> orig, next;
787 
788 		writeLooseRef("refs/heads/master", A);
789 		writeLooseRef("refs/heads/pu", B);
790 		orig = refdir.getRefs(RefDatabase.ALL);
791 
792 		deleteLooseRef("refs/heads/pu");
793 		writeLooseRef("refs/tags/v1.0", v1_0);
794 		next = refdir.getRefs(RefDatabase.ALL);
795 
796 		assertEquals(3, orig.size());
797 		assertEquals(3, next.size());
798 
799 		assertTrue(orig.containsKey("refs/heads/pu"));
800 		assertFalse(orig.containsKey("refs/tags/v1.0"));
801 		assertFalse(next.containsKey("refs/heads/pu"));
802 		assertTrue(next.containsKey("refs/tags/v1.0"));
803 	}
804 
805 	@Test
806 	public void testGetRefs_SkipsLockFiles() throws IOException {
807 		Map<String, Ref> all;
808 
809 		writeLooseRef("refs/heads/master", A);
810 		writeLooseRef("refs/heads/pu.lock", B);
811 		all = refdir.getRefs(RefDatabase.ALL);
812 
813 		assertEquals(2, all.size());
814 
815 		assertTrue(all.containsKey(HEAD));
816 		assertTrue(all.containsKey("refs/heads/master"));
817 		assertFalse(all.containsKey("refs/heads/pu.lock"));
818 	}
819 
820 	@Test
821 	public void testGetRefs_CycleInSymbolicRef() throws IOException {
822 		Map<String, Ref> all;
823 		Ref r;
824 
825 		writeLooseRef("refs/1", "ref: refs/2\n");
826 		writeLooseRef("refs/2", "ref: refs/3\n");
827 		writeLooseRef("refs/3", "ref: refs/4\n");
828 		writeLooseRef("refs/4", "ref: refs/5\n");
829 		writeLooseRef("refs/5", "ref: refs/end\n");
830 		writeLooseRef("refs/end", A);
831 
832 		all = refdir.getRefs(RefDatabase.ALL);
833 		r = all.get("refs/1");
834 		assertNotNull("has 1", r);
835 
836 		assertEquals("refs/1", r.getName());
837 		assertEquals(A, r.getObjectId());
838 		assertTrue(r.isSymbolic());
839 
840 		r = r.getTarget();
841 		assertEquals("refs/2", r.getName());
842 		assertEquals(A, r.getObjectId());
843 		assertTrue(r.isSymbolic());
844 
845 		r = r.getTarget();
846 		assertEquals("refs/3", r.getName());
847 		assertEquals(A, r.getObjectId());
848 		assertTrue(r.isSymbolic());
849 
850 		r = r.getTarget();
851 		assertEquals("refs/4", r.getName());
852 		assertEquals(A, r.getObjectId());
853 		assertTrue(r.isSymbolic());
854 
855 		r = r.getTarget();
856 		assertEquals("refs/5", r.getName());
857 		assertEquals(A, r.getObjectId());
858 		assertTrue(r.isSymbolic());
859 
860 		r = r.getTarget();
861 		assertEquals("refs/end", r.getName());
862 		assertEquals(A, r.getObjectId());
863 		assertFalse(r.isSymbolic());
864 
865 		writeLooseRef("refs/5", "ref: refs/6\n");
866 		writeLooseRef("refs/6", "ref: refs/end\n");
867 		all = refdir.getRefs(RefDatabase.ALL);
868 		r = all.get("refs/1");
869 		assertNull("mising 1 due to cycle", r);
870 	}
871 
872 	@Test
873 	public void testFindRef_CycleInSymbolicRef() throws IOException {
874 		Ref r;
875 
876 		writeLooseRef("refs/1", "ref: refs/2\n");
877 		writeLooseRef("refs/2", "ref: refs/3\n");
878 		writeLooseRef("refs/3", "ref: refs/4\n");
879 		writeLooseRef("refs/4", "ref: refs/5\n");
880 		writeLooseRef("refs/5", "ref: refs/end\n");
881 		writeLooseRef("refs/end", A);
882 
883 		r = refdir.findRef("1");
884 		assertEquals("refs/1", r.getName());
885 		assertEquals(A, r.getObjectId());
886 		assertTrue(r.isSymbolic());
887 
888 		writeLooseRef("refs/5", "ref: refs/6\n");
889 		writeLooseRef("refs/6", "ref: refs/end\n");
890 
891 		r = refdir.findRef("1");
892 		assertNull("missing 1 due to cycle", r);
893 
894 		writeLooseRef("refs/heads/1", B);
895 
896 		r = refdir.findRef("1");
897 		assertEquals("refs/heads/1", r.getName());
898 		assertEquals(B, r.getObjectId());
899 		assertFalse(r.isSymbolic());
900 	}
901 
902 	@Test
903 	public void testGetRefs_PackedNotPeeled_Sorted() throws IOException {
904 		Map<String, Ref> all;
905 
906 		writePackedRefs("" + //
907 				A.name() + " refs/heads/master\n" + //
908 				B.name() + " refs/heads/other\n" + //
909 				v1_0.name() + " refs/tags/v1.0\n");
910 		all = refdir.getRefs(RefDatabase.ALL);
911 
912 		assertEquals(4, all.size());
913 		final Ref head = all.get(HEAD);
914 		final Ref master = all.get("refs/heads/master");
915 		final Ref other = all.get("refs/heads/other");
916 		final Ref tag = all.get("refs/tags/v1.0");
917 
918 		assertEquals(A, master.getObjectId());
919 		assertFalse(master.isPeeled());
920 		assertNull(master.getPeeledObjectId());
921 
922 		assertEquals(B, other.getObjectId());
923 		assertFalse(other.isPeeled());
924 		assertNull(other.getPeeledObjectId());
925 
926 		assertSame(master, head.getTarget());
927 		assertEquals(A, head.getObjectId());
928 		assertFalse(head.isPeeled());
929 		assertNull(head.getPeeledObjectId());
930 
931 		assertEquals(v1_0, tag.getObjectId());
932 		assertFalse(tag.isPeeled());
933 		assertNull(tag.getPeeledObjectId());
934 	}
935 
936 	@Test
937 	public void testFindRef_PackedNotPeeled_WrongSort() throws IOException {
938 		writePackedRefs("" + //
939 				v1_0.name() + " refs/tags/v1.0\n" + //
940 				B.name() + " refs/heads/other\n" + //
941 				A.name() + " refs/heads/master\n");
942 
943 		final Ref head = refdir.findRef(HEAD);
944 		final Ref master = refdir.findRef("refs/heads/master");
945 		final Ref other = refdir.findRef("refs/heads/other");
946 		final Ref tag = refdir.findRef("refs/tags/v1.0");
947 
948 		assertEquals(A, master.getObjectId());
949 		assertFalse(master.isPeeled());
950 		assertNull(master.getPeeledObjectId());
951 
952 		assertEquals(B, other.getObjectId());
953 		assertFalse(other.isPeeled());
954 		assertNull(other.getPeeledObjectId());
955 
956 		assertSame(master, head.getTarget());
957 		assertEquals(A, head.getObjectId());
958 		assertFalse(head.isPeeled());
959 		assertNull(head.getPeeledObjectId());
960 
961 		assertEquals(v1_0, tag.getObjectId());
962 		assertFalse(tag.isPeeled());
963 		assertNull(tag.getPeeledObjectId());
964 	}
965 
966 	@Test
967 	public void testGetRefs_PackedWithPeeled() throws IOException {
968 		Map<String, Ref> all;
969 
970 		writePackedRefs("# pack-refs with: peeled \n" + //
971 				A.name() + " refs/heads/master\n" + //
972 				B.name() + " refs/heads/other\n" + //
973 				v1_0.name() + " refs/tags/v1.0\n" + //
974 				"^" + v1_0.getObject().name() + "\n");
975 		all = refdir.getRefs(RefDatabase.ALL);
976 
977 		assertEquals(4, all.size());
978 		final Ref head = all.get(HEAD);
979 		final Ref master = all.get("refs/heads/master");
980 		final Ref other = all.get("refs/heads/other");
981 		final Ref tag = all.get("refs/tags/v1.0");
982 
983 		assertEquals(A, master.getObjectId());
984 		assertTrue(master.isPeeled());
985 		assertNull(master.getPeeledObjectId());
986 
987 		assertEquals(B, other.getObjectId());
988 		assertTrue(other.isPeeled());
989 		assertNull(other.getPeeledObjectId());
990 
991 		assertSame(master, head.getTarget());
992 		assertEquals(A, head.getObjectId());
993 		assertTrue(head.isPeeled());
994 		assertNull(head.getPeeledObjectId());
995 
996 		assertEquals(v1_0, tag.getObjectId());
997 		assertTrue(tag.isPeeled());
998 		assertEquals(v1_0.getObject(), tag.getPeeledObjectId());
999 	}
1000 
1001 	@Test
1002 	public void test_repack() throws Exception {
1003 		Map<String, Ref> all;
1004 
1005 		writePackedRefs("# pack-refs with: peeled \n" + //
1006 				A.name() + " refs/heads/master\n" + //
1007 				B.name() + " refs/heads/other\n" + //
1008 				v1_0.name() + " refs/tags/v1.0\n" + //
1009 				"^" + v1_0.getObject().name() + "\n");
1010 		all = refdir.getRefs(RefDatabase.ALL);
1011 
1012 		assertEquals(4, all.size());
1013 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1014 		assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage());
1015 		assertEquals(A.getId(), all.get("refs/heads/master").getObjectId());
1016 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1017 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1018 
1019 		repo.update("refs/heads/master", B.getId());
1020 		RevTag v0_1 = repo.tag("v0.1", A);
1021 		repo.update("refs/tags/v0.1", v0_1);
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.LOOSE, all.get("refs/heads/master")
1028 				.getStorage());
1029 		assertEquals(B.getId(), all.get("refs/heads/master").getObjectId());
1030 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1031 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1032 		assertEquals(Storage.LOOSE, all.get("refs/tags/v0.1").getStorage());
1033 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
1034 
1035 		all = refdir.getRefs(RefDatabase.ALL);
1036 		refdir.pack(new ArrayList<>(all.keySet()));
1037 
1038 		all = refdir.getRefs(RefDatabase.ALL);
1039 		assertEquals(5, all.size());
1040 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1041 		// Why isn't the next ref LOOSE_PACKED?
1042 		assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage());
1043 		assertEquals(B.getId(), all.get("refs/heads/master").getObjectId());
1044 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1045 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1046 		assertEquals(Storage.PACKED, all.get("refs/tags/v0.1").getStorage());
1047 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
1048 	}
1049 
1050 	@Test
1051 	public void testFindRef_EmptyDatabase() throws IOException {
1052 		Ref r;
1053 
1054 		r = refdir.findRef(HEAD);
1055 		assertTrue(r.isSymbolic());
1056 		assertSame(LOOSE, r.getStorage());
1057 		assertEquals("refs/heads/master", r.getTarget().getName());
1058 		assertSame(NEW, r.getTarget().getStorage());
1059 		assertNull(r.getTarget().getObjectId());
1060 
1061 		assertNull(refdir.findRef("refs/heads/master"));
1062 		assertNull(refdir.findRef("refs/tags/v1.0"));
1063 		assertNull(refdir.findRef("FETCH_HEAD"));
1064 		assertNull(refdir.findRef("NOT.A.REF.NAME"));
1065 		assertNull(refdir.findRef("master"));
1066 		assertNull(refdir.findRef("v1.0"));
1067 	}
1068 
1069 	@Test
1070 	public void testExactRef_EmptyDatabase() throws IOException {
1071 		Ref r;
1072 
1073 		r = refdir.exactRef(HEAD);
1074 		assertTrue(r.isSymbolic());
1075 		assertSame(LOOSE, r.getStorage());
1076 		assertEquals("refs/heads/master", r.getTarget().getName());
1077 		assertSame(NEW, r.getTarget().getStorage());
1078 		assertNull(r.getTarget().getObjectId());
1079 
1080 		assertNull(refdir.exactRef("refs/heads/master"));
1081 		assertNull(refdir.exactRef("refs/tags/v1.0"));
1082 		assertNull(refdir.exactRef("FETCH_HEAD"));
1083 		assertNull(refdir.exactRef("NOT.A.REF.NAME"));
1084 		assertNull(refdir.exactRef("master"));
1085 		assertNull(refdir.exactRef("v1.0"));
1086 	}
1087 
1088 	@Test
1089 	public void testGetAdditionalRefs_OrigHead() throws IOException {
1090 		writeLooseRef("ORIG_HEAD", A);
1091 
1092 		List<Ref> refs = refdir.getAdditionalRefs();
1093 		assertEquals(1, refs.size());
1094 
1095 		Ref r = refs.get(0);
1096 		assertFalse(r.isSymbolic());
1097 		assertEquals(A, r.getObjectId());
1098 		assertEquals("ORIG_HEAD", r.getName());
1099 		assertFalse(r.isPeeled());
1100 		assertNull(r.getPeeledObjectId());
1101 	}
1102 
1103 	@Test
1104 	public void testGetAdditionalRefs_OrigHeadBranch() throws IOException {
1105 		writeLooseRef("refs/heads/ORIG_HEAD", A);
1106 		List<Ref> refs = refdir.getAdditionalRefs();
1107 		assertArrayEquals(new Ref[0], refs.toArray());
1108 	}
1109 
1110 	@Test
1111 	public void testFindRef_FetchHead() throws IOException {
1112 		// This is an odd special case where we need to make sure we read
1113 		// exactly the first 40 bytes of the file and nothing further on
1114 		// that line, or the remainder of the file.
1115 		write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
1116 				+ "\tnot-for-merge"
1117 				+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
1118 
1119 		Ref r = refdir.findRef("FETCH_HEAD");
1120 		assertFalse(r.isSymbolic());
1121 		assertEquals(A, r.getObjectId());
1122 		assertEquals("FETCH_HEAD", r.getName());
1123 		assertFalse(r.isPeeled());
1124 		assertNull(r.getPeeledObjectId());
1125 	}
1126 
1127 	@Test
1128 	public void testExactRef_FetchHead() throws IOException {
1129 		// This is an odd special case where we need to make sure we read
1130 		// exactly the first 40 bytes of the file and nothing further on
1131 		// that line, or the remainder of the file.
1132 		write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
1133 				+ "\tnot-for-merge"
1134 				+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
1135 
1136 		Ref r = refdir.exactRef("FETCH_HEAD");
1137 		assertFalse(r.isSymbolic());
1138 		assertEquals(A, r.getObjectId());
1139 		assertEquals("FETCH_HEAD", r.getName());
1140 		assertFalse(r.isPeeled());
1141 		assertNull(r.getPeeledObjectId());
1142 	}
1143 
1144 	@Test
1145 	public void testFindRef_AnyHeadWithGarbage() throws IOException {
1146 		write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name()
1147 				+ "012345 . this is not a standard reference\n"
1148 				+ "#and even more junk\n");
1149 
1150 		Ref r = refdir.findRef("refs/heads/A");
1151 		assertFalse(r.isSymbolic());
1152 		assertEquals(A, r.getObjectId());
1153 		assertEquals("refs/heads/A", r.getName());
1154 		assertFalse(r.isPeeled());
1155 		assertNull(r.getPeeledObjectId());
1156 	}
1157 
1158 	@Test
1159 	public void testGetRefs_CorruptSymbolicReference() throws IOException {
1160 		String name = "refs/heads/A";
1161 		writeLooseRef(name, "ref: \n");
1162 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
1163 	}
1164 
1165 	@Test
1166 	public void testFindRef_CorruptSymbolicReference() throws IOException {
1167 		String name = "refs/heads/A";
1168 		writeLooseRef(name, "ref: \n");
1169 		try {
1170 			refdir.findRef(name);
1171 			fail("read an invalid reference");
1172 		} catch (IOException err) {
1173 			String msg = err.getMessage();
1174 			assertEquals("Not a ref: " + name + ": ref:", msg);
1175 		}
1176 	}
1177 
1178 	@Test
1179 	public void testGetRefs_CorruptObjectIdReference() throws IOException {
1180 		String name = "refs/heads/A";
1181 		String content = "zoo" + A.name();
1182 		writeLooseRef(name, content + "\n");
1183 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
1184 	}
1185 
1186 	@Test
1187 	public void testFindRef_CorruptObjectIdReference() throws IOException {
1188 		String name = "refs/heads/A";
1189 		String content = "zoo" + A.name();
1190 		writeLooseRef(name, content + "\n");
1191 		try {
1192 			refdir.findRef(name);
1193 			fail("read an invalid reference");
1194 		} catch (IOException err) {
1195 			String msg = err.getMessage();
1196 			assertEquals("Not a ref: " + name + ": " + content, msg);
1197 		}
1198 	}
1199 
1200 	@Test
1201 	public void testIsNameConflicting() throws IOException {
1202 		writeLooseRef("refs/heads/a/b", A);
1203 		writePackedRef("refs/heads/q", B);
1204 
1205 		// new references cannot replace an existing container
1206 		assertTrue(refdir.isNameConflicting("refs"));
1207 		assertTrue(refdir.isNameConflicting("refs/heads"));
1208 		assertTrue(refdir.isNameConflicting("refs/heads/a"));
1209 
1210 		// existing reference is not conflicting
1211 		assertFalse(refdir.isNameConflicting("refs/heads/a/b"));
1212 
1213 		// new references are not conflicting
1214 		assertFalse(refdir.isNameConflicting("refs/heads/a/d"));
1215 		assertFalse(refdir.isNameConflicting("refs/heads/master"));
1216 
1217 		// existing reference must not be used as a container
1218 		assertTrue(refdir.isNameConflicting("refs/heads/a/b/c"));
1219 		assertTrue(refdir.isNameConflicting("refs/heads/q/master"));
1220 	}
1221 
1222 	@Test
1223 	public void testPeelLooseTag() throws IOException {
1224 		writeLooseRef("refs/tags/v1_0", v1_0);
1225 		writeLooseRef("refs/tags/current", "ref: refs/tags/v1_0\n");
1226 
1227 		final Ref tag = refdir.findRef("refs/tags/v1_0");
1228 		final Ref cur = refdir.findRef("refs/tags/current");
1229 
1230 		assertEquals(v1_0, tag.getObjectId());
1231 		assertFalse(tag.isSymbolic());
1232 		assertFalse(tag.isPeeled());
1233 		assertNull(tag.getPeeledObjectId());
1234 
1235 		assertEquals(v1_0, cur.getObjectId());
1236 		assertTrue(cur.isSymbolic());
1237 		assertFalse(cur.isPeeled());
1238 		assertNull(cur.getPeeledObjectId());
1239 
1240 		final Ref tag_p = refdir.peel(tag);
1241 		final Ref cur_p = refdir.peel(cur);
1242 
1243 		assertNotSame(tag, tag_p);
1244 		assertFalse(tag_p.isSymbolic());
1245 		assertTrue(tag_p.isPeeled());
1246 		assertEquals(v1_0, tag_p.getObjectId());
1247 		assertEquals(v1_0.getObject(), tag_p.getPeeledObjectId());
1248 		assertSame(tag_p, refdir.peel(tag_p));
1249 
1250 		assertNotSame(cur, cur_p);
1251 		assertEquals("refs/tags/current", cur_p.getName());
1252 		assertTrue(cur_p.isSymbolic());
1253 		assertEquals("refs/tags/v1_0", cur_p.getTarget().getName());
1254 		assertTrue(cur_p.isPeeled());
1255 		assertEquals(v1_0, cur_p.getObjectId());
1256 		assertEquals(v1_0.getObject(), cur_p.getPeeledObjectId());
1257 
1258 		// reuses cached peeling later, but not immediately due to
1259 		// the implementation so we have to fetch it once.
1260 		final Ref tag_p2 = refdir.findRef("refs/tags/v1_0");
1261 		assertFalse(tag_p2.isSymbolic());
1262 		assertTrue(tag_p2.isPeeled());
1263 		assertEquals(v1_0, tag_p2.getObjectId());
1264 		assertEquals(v1_0.getObject(), tag_p2.getPeeledObjectId());
1265 
1266 		assertSame(tag_p2, refdir.findRef("refs/tags/v1_0"));
1267 		assertSame(tag_p2, refdir.findRef("refs/tags/current").getTarget());
1268 		assertSame(tag_p2, refdir.peel(tag_p2));
1269 	}
1270 
1271 	@Test
1272 	public void testPeelCommit() throws IOException {
1273 		writeLooseRef("refs/heads/master", A);
1274 
1275 		Ref master = refdir.findRef("refs/heads/master");
1276 		assertEquals(A, master.getObjectId());
1277 		assertFalse(master.isPeeled());
1278 		assertNull(master.getPeeledObjectId());
1279 
1280 		Ref master_p = refdir.peel(master);
1281 		assertNotSame(master, master_p);
1282 		assertEquals(A, master_p.getObjectId());
1283 		assertTrue(master_p.isPeeled());
1284 		assertNull(master_p.getPeeledObjectId());
1285 
1286 		// reuses cached peeling later, but not immediately due to
1287 		// the implementation so we have to fetch it once.
1288 		Ref master_p2 = refdir.findRef("refs/heads/master");
1289 		assertNotSame(master, master_p2);
1290 		assertEquals(A, master_p2.getObjectId());
1291 		assertTrue(master_p2.isPeeled());
1292 		assertNull(master_p2.getPeeledObjectId());
1293 		assertSame(master_p2, refdir.peel(master_p2));
1294 	}
1295 
1296 	@Test
1297 	public void testRefsChangedStackOverflow() throws Exception {
1298 		final FileRepository newRepo = createBareRepository();
1299 		final RefDatabase refDb = newRepo.getRefDatabase();
1300 		File packedRefs = new File(newRepo.getDirectory(), "packed-refs");
1301 		assertTrue(packedRefs.createNewFile());
1302 		final AtomicReference<StackOverflowError> error = new AtomicReference<>();
1303 		final AtomicReference<IOException> exception = new AtomicReference<>();
1304 		final AtomicInteger changeCount = new AtomicInteger();
1305 		newRepo.getListenerList()
1306 				.addRefsChangedListener((RefsChangedEvent event) -> {
1307 					try {
1308 						refDb.getRefsByPrefix("ref");
1309 						changeCount.incrementAndGet();
1310 					} catch (StackOverflowError soe) {
1311 						error.set(soe);
1312 					} catch (IOException ioe) {
1313 						exception.set(ioe);
1314 					}
1315 				});
1316 		refDb.getRefsByPrefix("ref");
1317 		refDb.getRefsByPrefix("ref");
1318 		assertNull(error.get());
1319 		assertNull(exception.get());
1320 		assertEquals(1, changeCount.get());
1321 	}
1322 
1323 	@Test
1324 	public void testPackedRefsLockFailure() throws Exception {
1325 		writeLooseRef("refs/heads/master", A);
1326 		refdir.setRetrySleepMs(Arrays.asList(0, 0));
1327 		LockFile myLock = refdir.lockPackedRefs();
1328 		try {
1329 			refdir.pack(Arrays.asList("refs/heads/master"));
1330 			fail("expected LockFailedException");
1331 		} catch (LockFailedException e) {
1332 			assertEquals(refdir.packedRefsFile.getPath(), e.getFile().getPath());
1333 		} finally {
1334 			myLock.unlock();
1335 		}
1336 		Ref ref = refdir.findRef("refs/heads/master");
1337 		assertEquals(Storage.LOOSE, ref.getStorage());
1338 	}
1339 
1340 	private void writeLooseRef(String name, AnyObjectId id) throws IOException {
1341 		writeLooseRef(name, id.name() + "\n");
1342 	}
1343 
1344 	private void writeLooseRef(String name, String content) throws IOException {
1345 		write(new File(diskRepo.getDirectory(), name), content);
1346 	}
1347 
1348 	private void writePackedRef(String name, AnyObjectId id) throws IOException {
1349 		writePackedRefs(id.name() + " " + name + "\n");
1350 	}
1351 
1352 	private void writePackedRefs(String content) throws IOException {
1353 		File pr = new File(diskRepo.getDirectory(), "packed-refs");
1354 		write(pr, content);
1355 		FS fs = diskRepo.getFS();
1356 		fs.setLastModified(pr.toPath(), Instant.now().minusSeconds(3600));
1357 	}
1358 
1359 	private void deleteLooseRef(String name) {
1360 		File path = new File(diskRepo.getDirectory(), name);
1361 		assertTrue("deleted " + name, path.delete());
1362 	}
1363 }