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.List;
65  import java.util.Map;
66  import java.util.concurrent.atomic.AtomicInteger;
67  import java.util.concurrent.atomic.AtomicReference;
68  
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.BatchRefUpdate;
76  import org.eclipse.jgit.lib.NullProgressMonitor;
77  import org.eclipse.jgit.lib.ProgressMonitor;
78  import org.eclipse.jgit.lib.Ref;
79  import org.eclipse.jgit.lib.Ref.Storage;
80  import org.eclipse.jgit.lib.RefDatabase;
81  import org.eclipse.jgit.lib.Repository;
82  import org.eclipse.jgit.revwalk.RevCommit;
83  import org.eclipse.jgit.revwalk.RevTag;
84  import org.eclipse.jgit.revwalk.RevWalk;
85  import org.eclipse.jgit.transport.ReceiveCommand;
86  import org.eclipse.jgit.transport.ReceiveCommand.Type;
87  import org.junit.Before;
88  import org.junit.Test;
89  
90  public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
91  	private Repository diskRepo;
92  
93  	private TestRepository<Repository> repo;
94  
95  	private RefDirectory refdir;
96  
97  	private RevCommit A;
98  
99  	private RevCommit B;
100 
101 	private RevTag v1_0;
102 
103 	@Override
104 	@Before
105 	public void setUp() throws Exception {
106 		super.setUp();
107 
108 		diskRepo = createBareRepository();
109 		refdir = (RefDirectory) diskRepo.getRefDatabase();
110 
111 		repo = new TestRepository<>(diskRepo);
112 		A = repo.commit().create();
113 		B = repo.commit(repo.getRevWalk().parseCommit(A));
114 		v1_0 = repo.tag("v1_0", B);
115 		repo.getRevWalk().parseBody(v1_0);
116 	}
117 
118 	@Test
119 	public void testCreate() throws IOException {
120 		// setUp above created the directory. We just have to test it.
121 		File d = diskRepo.getDirectory();
122 		assertSame(diskRepo, refdir.getRepository());
123 
124 		assertTrue(new File(d, "refs").isDirectory());
125 		assertTrue(new File(d, "logs").isDirectory());
126 		assertTrue(new File(d, "logs/refs").isDirectory());
127 		assertFalse(new File(d, "packed-refs").exists());
128 
129 		assertTrue(new File(d, "refs/heads").isDirectory());
130 		assertTrue(new File(d, "refs/tags").isDirectory());
131 		assertEquals(2, new File(d, "refs").list().length);
132 		assertEquals(0, new File(d, "refs/heads").list().length);
133 		assertEquals(0, new File(d, "refs/tags").list().length);
134 
135 		assertTrue(new File(d, "logs/refs/heads").isDirectory());
136 		assertFalse(new File(d, "logs/HEAD").exists());
137 		assertEquals(0, new File(d, "logs/refs/heads").list().length);
138 
139 		assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
140 	}
141 
142 	@Test
143 	public void testGetRefs_EmptyDatabase() throws IOException {
144 		Map<String, Ref> all;
145 
146 		all = refdir.getRefs(RefDatabase.ALL);
147 		assertTrue("no references", all.isEmpty());
148 
149 		all = refdir.getRefs(R_HEADS);
150 		assertTrue("no references", all.isEmpty());
151 
152 		all = refdir.getRefs(R_TAGS);
153 		assertTrue("no references", all.isEmpty());
154 	}
155 
156 	@Test
157 	public void testGetRefs_HeadOnOneBranch() throws IOException {
158 		Map<String, Ref> all;
159 		Ref head, master;
160 
161 		writeLooseRef("refs/heads/master", A);
162 
163 		all = refdir.getRefs(RefDatabase.ALL);
164 		assertEquals(2, all.size());
165 		assertTrue("has HEAD", all.containsKey(HEAD));
166 		assertTrue("has master", all.containsKey("refs/heads/master"));
167 
168 		head = all.get(HEAD);
169 		master = all.get("refs/heads/master");
170 
171 		assertEquals(HEAD, head.getName());
172 		assertTrue(head.isSymbolic());
173 		assertSame(LOOSE, head.getStorage());
174 		assertSame("uses same ref as target", master, head.getTarget());
175 
176 		assertEquals("refs/heads/master", master.getName());
177 		assertFalse(master.isSymbolic());
178 		assertSame(LOOSE, master.getStorage());
179 		assertEquals(A, master.getObjectId());
180 	}
181 
182 	@Test
183 	public void testGetRefs_DeatchedHead1() throws IOException {
184 		Map<String, Ref> all;
185 		Ref head;
186 
187 		writeLooseRef(HEAD, A);
188 
189 		all = refdir.getRefs(RefDatabase.ALL);
190 		assertEquals(1, all.size());
191 		assertTrue("has HEAD", all.containsKey(HEAD));
192 
193 		head = all.get(HEAD);
194 
195 		assertEquals(HEAD, head.getName());
196 		assertFalse(head.isSymbolic());
197 		assertSame(LOOSE, head.getStorage());
198 		assertEquals(A, head.getObjectId());
199 	}
200 
201 	@Test
202 	public void testGetRefs_DeatchedHead2() throws IOException {
203 		Map<String, Ref> all;
204 		Ref head, master;
205 
206 		writeLooseRef(HEAD, A);
207 		writeLooseRef("refs/heads/master", B);
208 
209 		all = refdir.getRefs(RefDatabase.ALL);
210 		assertEquals(2, all.size());
211 
212 		head = all.get(HEAD);
213 		master = all.get("refs/heads/master");
214 
215 		assertEquals(HEAD, head.getName());
216 		assertFalse(head.isSymbolic());
217 		assertSame(LOOSE, head.getStorage());
218 		assertEquals(A, head.getObjectId());
219 
220 		assertEquals("refs/heads/master", master.getName());
221 		assertFalse(master.isSymbolic());
222 		assertSame(LOOSE, master.getStorage());
223 		assertEquals(B, master.getObjectId());
224 	}
225 
226 	@Test
227 	public void testGetRefs_DeeplyNestedBranch() throws IOException {
228 		String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k";
229 		Map<String, Ref> all;
230 		Ref r;
231 
232 		writeLooseRef(name, A);
233 
234 		all = refdir.getRefs(RefDatabase.ALL);
235 		assertEquals(1, all.size());
236 
237 		r = all.get(name);
238 		assertEquals(name, r.getName());
239 		assertFalse(r.isSymbolic());
240 		assertSame(LOOSE, r.getStorage());
241 		assertEquals(A, r.getObjectId());
242 	}
243 
244 	@Test
245 	public void testGetRefs_HeadBranchNotBorn() throws IOException {
246 		Map<String, Ref> all;
247 		Ref a, b;
248 
249 		writeLooseRef("refs/heads/A", A);
250 		writeLooseRef("refs/heads/B", B);
251 
252 		all = refdir.getRefs(RefDatabase.ALL);
253 		assertEquals(2, all.size());
254 		assertFalse("no HEAD", all.containsKey(HEAD));
255 
256 		a = all.get("refs/heads/A");
257 		b = all.get("refs/heads/B");
258 
259 		assertEquals(A, a.getObjectId());
260 		assertEquals(B, b.getObjectId());
261 
262 		assertEquals("refs/heads/A", a.getName());
263 		assertEquals("refs/heads/B", b.getName());
264 	}
265 
266 	@Test
267 	public void testGetRefs_LooseOverridesPacked() throws IOException {
268 		Map<String, Ref> heads;
269 		Ref a;
270 
271 		writeLooseRef("refs/heads/master", B);
272 		writePackedRef("refs/heads/master", A);
273 
274 		heads = refdir.getRefs(R_HEADS);
275 		assertEquals(1, heads.size());
276 
277 		a = heads.get("master");
278 		assertEquals("refs/heads/master", a.getName());
279 		assertEquals(B, a.getObjectId());
280 	}
281 
282 	@Test
283 	public void testGetRefs_IgnoresGarbageRef1() throws IOException {
284 		Map<String, Ref> heads;
285 		Ref a;
286 
287 		writeLooseRef("refs/heads/A", A);
288 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
289 
290 		heads = refdir.getRefs(RefDatabase.ALL);
291 		assertEquals(1, heads.size());
292 
293 		a = heads.get("refs/heads/A");
294 		assertEquals("refs/heads/A", a.getName());
295 		assertEquals(A, a.getObjectId());
296 	}
297 
298 	@Test
299 	public void testGetRefs_IgnoresGarbageRef2() throws IOException {
300 		Map<String, Ref> heads;
301 		Ref a;
302 
303 		writeLooseRef("refs/heads/A", A);
304 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "");
305 
306 		heads = refdir.getRefs(RefDatabase.ALL);
307 		assertEquals(1, heads.size());
308 
309 		a = heads.get("refs/heads/A");
310 		assertEquals("refs/heads/A", a.getName());
311 		assertEquals(A, a.getObjectId());
312 	}
313 
314 	@Test
315 	public void testGetRefs_IgnoresGarbageRef3() throws IOException {
316 		Map<String, Ref> heads;
317 		Ref a;
318 
319 		writeLooseRef("refs/heads/A", A);
320 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "\n");
321 
322 		heads = refdir.getRefs(RefDatabase.ALL);
323 		assertEquals(1, heads.size());
324 
325 		a = heads.get("refs/heads/A");
326 		assertEquals("refs/heads/A", a.getName());
327 		assertEquals(A, a.getObjectId());
328 	}
329 
330 	@Test
331 	public void testGetRefs_IgnoresGarbageRef4() throws IOException {
332 		Map<String, Ref> heads;
333 		Ref a, b, c;
334 
335 		writeLooseRef("refs/heads/A", A);
336 		writeLooseRef("refs/heads/B", B);
337 		writeLooseRef("refs/heads/C", A);
338 		heads = refdir.getRefs(RefDatabase.ALL);
339 		assertEquals(3, heads.size());
340 		assertTrue(heads.containsKey("refs/heads/A"));
341 		assertTrue(heads.containsKey("refs/heads/B"));
342 		assertTrue(heads.containsKey("refs/heads/C"));
343 
344 		writeLooseRef("refs/heads/B", "FAIL\n");
345 
346 		heads = refdir.getRefs(RefDatabase.ALL);
347 		assertEquals(2, heads.size());
348 
349 		a = heads.get("refs/heads/A");
350 		b = heads.get("refs/heads/B");
351 		c = heads.get("refs/heads/C");
352 
353 		assertEquals("refs/heads/A", a.getName());
354 		assertEquals(A, a.getObjectId());
355 
356 		assertNull("no refs/heads/B", b);
357 
358 		assertEquals("refs/heads/C", c.getName());
359 		assertEquals(A, c.getObjectId());
360 	}
361 
362 	@Test
363 	public void testFirstExactRef_IgnoresGarbageRef() throws IOException {
364 		writeLooseRef("refs/heads/A", A);
365 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
366 
367 		Ref a = refdir.firstExactRef("refs/heads/bad", "refs/heads/A");
368 		assertEquals("refs/heads/A", a.getName());
369 		assertEquals(A, a.getObjectId());
370 	}
371 
372 	@Test
373 	public void testExactRef_IgnoresGarbageRef() throws IOException {
374 		writeLooseRef("refs/heads/A", A);
375 		write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
376 
377 		Map<String, Ref> refs =
378 				refdir.exactRef("refs/heads/bad", "refs/heads/A");
379 
380 		assertNull("no refs/heads/bad", refs.get("refs/heads/bad"));
381 
382 		Ref a = refs.get("refs/heads/A");
383 		assertEquals("refs/heads/A", a.getName());
384 		assertEquals(A, a.getObjectId());
385 
386 		assertEquals(1, refs.size());
387 	}
388 
389 	@Test
390 	public void testGetRefs_InvalidName() throws IOException {
391 		writeLooseRef("refs/heads/A", A);
392 
393 		assertTrue("empty refs/heads", refdir.getRefs("refs/heads").isEmpty());
394 		assertTrue("empty objects", refdir.getRefs("objects").isEmpty());
395 		assertTrue("empty objects/", refdir.getRefs("objects/").isEmpty());
396 	}
397 
398 	@Test
399 	public void testReadNotExistingBranchConfig() throws IOException {
400 		assertNull("find branch config", refdir.getRef("config"));
401 		assertNull("find branch config", refdir.getRef("refs/heads/config"));
402 	}
403 
404 	@Test
405 	public void testReadBranchConfig() throws IOException {
406 		writeLooseRef("refs/heads/config", A);
407 
408 		assertNotNull("find branch config", refdir.getRef("config"));
409 	}
410 
411 	@Test
412 	public void testGetRefs_HeadsOnly_AllLoose() throws IOException {
413 		Map<String, Ref> heads;
414 		Ref a, b;
415 
416 		writeLooseRef("refs/heads/A", A);
417 		writeLooseRef("refs/heads/B", B);
418 		writeLooseRef("refs/tags/v1.0", v1_0);
419 
420 		heads = refdir.getRefs(R_HEADS);
421 		assertEquals(2, heads.size());
422 
423 		a = heads.get("A");
424 		b = heads.get("B");
425 
426 		assertEquals("refs/heads/A", a.getName());
427 		assertEquals("refs/heads/B", b.getName());
428 
429 		assertEquals(A, a.getObjectId());
430 		assertEquals(B, b.getObjectId());
431 	}
432 
433 	@Test
434 	public void testGetRefs_HeadsOnly_AllPacked1() throws IOException {
435 		Map<String, Ref> heads;
436 		Ref a;
437 
438 		deleteLooseRef(HEAD);
439 		writePackedRef("refs/heads/A", A);
440 
441 		heads = refdir.getRefs(R_HEADS);
442 		assertEquals(1, heads.size());
443 
444 		a = heads.get("A");
445 
446 		assertEquals("refs/heads/A", a.getName());
447 		assertEquals(A, a.getObjectId());
448 	}
449 
450 	@Test
451 	public void testGetRefs_HeadsOnly_SymrefToPacked() throws IOException {
452 		Map<String, Ref> heads;
453 		Ref master, other;
454 
455 		writeLooseRef("refs/heads/other", "ref: refs/heads/master\n");
456 		writePackedRef("refs/heads/master", A);
457 
458 		heads = refdir.getRefs(R_HEADS);
459 		assertEquals(2, heads.size());
460 
461 		master = heads.get("master");
462 		other = heads.get("other");
463 
464 		assertEquals("refs/heads/master", master.getName());
465 		assertEquals(A, master.getObjectId());
466 
467 		assertEquals("refs/heads/other", other.getName());
468 		assertEquals(A, other.getObjectId());
469 		assertSame(master, other.getTarget());
470 	}
471 
472 	@Test
473 	public void testGetRefs_HeadsOnly_Mixed() throws IOException {
474 		Map<String, Ref> heads;
475 		Ref a, b;
476 
477 		writeLooseRef("refs/heads/A", A);
478 		writeLooseRef("refs/heads/B", B);
479 		writePackedRef("refs/tags/v1.0", v1_0);
480 
481 		heads = refdir.getRefs(R_HEADS);
482 		assertEquals(2, heads.size());
483 
484 		a = heads.get("A");
485 		b = heads.get("B");
486 
487 		assertEquals("refs/heads/A", a.getName());
488 		assertEquals("refs/heads/B", b.getName());
489 
490 		assertEquals(A, a.getObjectId());
491 		assertEquals(B, b.getObjectId());
492 	}
493 
494 	@Test
495 	public void testFirstExactRef_Mixed() throws IOException {
496 		writeLooseRef("refs/heads/A", A);
497 		writePackedRef("refs/tags/v1.0", v1_0);
498 
499 		Ref a = refdir.firstExactRef("refs/heads/A", "refs/tags/v1.0");
500 		Ref one = refdir.firstExactRef("refs/tags/v1.0", "refs/heads/A");
501 
502 		assertEquals("refs/heads/A", a.getName());
503 		assertEquals("refs/tags/v1.0", one.getName());
504 
505 		assertEquals(A, a.getObjectId());
506 		assertEquals(v1_0, one.getObjectId());
507 	}
508 
509 	@Test
510 	public void testGetRefs_TagsOnly_AllLoose() throws IOException {
511 		Map<String, Ref> tags;
512 		Ref a;
513 
514 		writeLooseRef("refs/heads/A", A);
515 		writeLooseRef("refs/tags/v1.0", v1_0);
516 
517 		tags = refdir.getRefs(R_TAGS);
518 		assertEquals(1, tags.size());
519 
520 		a = tags.get("v1.0");
521 
522 		assertEquals("refs/tags/v1.0", a.getName());
523 		assertEquals(v1_0, a.getObjectId());
524 	}
525 
526 	@Test
527 	public void testGetRefs_LooseSortedCorrectly() throws IOException {
528 		Map<String, Ref> refs;
529 
530 		writeLooseRef("refs/heads/project1/A", A);
531 		writeLooseRef("refs/heads/project1-B", B);
532 
533 		refs = refdir.getRefs(RefDatabase.ALL);
534 		assertEquals(2, refs.size());
535 		assertEquals(A, refs.get("refs/heads/project1/A").getObjectId());
536 		assertEquals(B, refs.get("refs/heads/project1-B").getObjectId());
537 	}
538 
539 	@Test
540 	public void testGetRefs_LooseSorting_Bug_348834() throws IOException {
541 		Map<String, Ref> refs;
542 
543 		writeLooseRef("refs/heads/my/a+b", A);
544 		writeLooseRef("refs/heads/my/a/b/c", B);
545 
546 		final int[] count = new int[1];
547 
548 		ListenerHandle listener = Repository.getGlobalListenerList()
549 				.addRefsChangedListener(new RefsChangedListener() {
550 
551 					@Override
552 					public void onRefsChanged(RefsChangedEvent event) {
553 						count[0]++;
554 					}
555 				});
556 
557 		refs = refdir.getRefs(RefDatabase.ALL);
558 		refs = refdir.getRefs(RefDatabase.ALL);
559 		listener.remove();
560 		assertEquals(1, count[0]); // Bug 348834 multiple RefsChangedEvents
561 		assertEquals(2, refs.size());
562 		assertEquals(A, refs.get("refs/heads/my/a+b").getObjectId());
563 		assertEquals(B, refs.get("refs/heads/my/a/b/c").getObjectId());
564 
565 	}
566 
567 	@Test
568 	public void testGetRefs_TagsOnly_AllPacked() throws IOException {
569 		Map<String, Ref> tags;
570 		Ref a;
571 
572 		deleteLooseRef(HEAD);
573 		writePackedRef("refs/tags/v1.0", v1_0);
574 
575 		tags = refdir.getRefs(R_TAGS);
576 		assertEquals(1, tags.size());
577 
578 		a = tags.get("v1.0");
579 
580 		assertEquals("refs/tags/v1.0", a.getName());
581 		assertEquals(v1_0, a.getObjectId());
582 	}
583 
584 	@Test
585 	public void testGetRefs_DiscoversNewLoose1() throws IOException {
586 		Map<String, Ref> orig, next;
587 		Ref orig_r, next_r;
588 
589 		writeLooseRef("refs/heads/master", A);
590 		orig = refdir.getRefs(RefDatabase.ALL);
591 
592 		writeLooseRef("refs/heads/next", B);
593 		next = refdir.getRefs(RefDatabase.ALL);
594 
595 		assertEquals(2, orig.size());
596 		assertEquals(3, next.size());
597 
598 		assertFalse(orig.containsKey("refs/heads/next"));
599 		assertTrue(next.containsKey("refs/heads/next"));
600 
601 		orig_r = orig.get("refs/heads/master");
602 		next_r = next.get("refs/heads/master");
603 		assertEquals(A, orig_r.getObjectId());
604 		assertSame("uses cached instance", orig_r, next_r);
605 		assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
606 		assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
607 
608 		next_r = next.get("refs/heads/next");
609 		assertSame(LOOSE, next_r.getStorage());
610 		assertEquals(B, next_r.getObjectId());
611 	}
612 
613 	@Test
614 	public void testGetRefs_DiscoversNewLoose2() throws IOException {
615 		Map<String, Ref> orig, next, news;
616 
617 		writeLooseRef("refs/heads/pu", A);
618 		orig = refdir.getRefs(RefDatabase.ALL);
619 
620 		writeLooseRef("refs/heads/new/B", B);
621 		news = refdir.getRefs("refs/heads/new/");
622 		next = refdir.getRefs(RefDatabase.ALL);
623 
624 		assertEquals(1, orig.size());
625 		assertEquals(2, next.size());
626 		assertEquals(1, news.size());
627 
628 		assertTrue(orig.containsKey("refs/heads/pu"));
629 		assertTrue(next.containsKey("refs/heads/pu"));
630 		assertFalse(news.containsKey("refs/heads/pu"));
631 
632 		assertFalse(orig.containsKey("refs/heads/new/B"));
633 		assertTrue(next.containsKey("refs/heads/new/B"));
634 		assertTrue(news.containsKey("B"));
635 	}
636 
637 	@Test
638 	public void testGetRefs_DiscoversModifiedLoose() throws IOException {
639 		Map<String, Ref> all;
640 
641 		writeLooseRef("refs/heads/master", A);
642 		all = refdir.getRefs(RefDatabase.ALL);
643 		assertEquals(A, all.get(HEAD).getObjectId());
644 
645 		writeLooseRef("refs/heads/master", B);
646 		all = refdir.getRefs(RefDatabase.ALL);
647 		assertEquals(B, all.get(HEAD).getObjectId());
648 	}
649 
650 	@Test
651 	public void testGetRef_DiscoversModifiedLoose() throws IOException {
652 		Map<String, Ref> all;
653 
654 		writeLooseRef("refs/heads/master", A);
655 		all = refdir.getRefs(RefDatabase.ALL);
656 		assertEquals(A, all.get(HEAD).getObjectId());
657 
658 		writeLooseRef("refs/heads/master", B);
659 
660 		Ref master = refdir.getRef("refs/heads/master");
661 		assertEquals(B, master.getObjectId());
662 	}
663 
664 	@Test
665 	public void testGetRefs_DiscoversDeletedLoose1() throws IOException {
666 		Map<String, Ref> orig, next;
667 		Ref orig_r, next_r;
668 
669 		writeLooseRef("refs/heads/B", B);
670 		writeLooseRef("refs/heads/master", A);
671 		orig = refdir.getRefs(RefDatabase.ALL);
672 
673 		deleteLooseRef("refs/heads/B");
674 		next = refdir.getRefs(RefDatabase.ALL);
675 
676 		assertEquals(3, orig.size());
677 		assertEquals(2, next.size());
678 
679 		assertTrue(orig.containsKey("refs/heads/B"));
680 		assertFalse(next.containsKey("refs/heads/B"));
681 
682 		orig_r = orig.get("refs/heads/master");
683 		next_r = next.get("refs/heads/master");
684 		assertEquals(A, orig_r.getObjectId());
685 		assertSame("uses cached instance", orig_r, next_r);
686 		assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget());
687 		assertSame("same HEAD", orig_r, next.get(HEAD).getTarget());
688 
689 		orig_r = orig.get("refs/heads/B");
690 		assertSame(LOOSE, orig_r.getStorage());
691 		assertEquals(B, orig_r.getObjectId());
692 	}
693 
694 	@Test
695 	public void testGetRef_DiscoversDeletedLoose() throws IOException {
696 		Map<String, Ref> all;
697 
698 		writeLooseRef("refs/heads/master", A);
699 		all = refdir.getRefs(RefDatabase.ALL);
700 		assertEquals(A, all.get(HEAD).getObjectId());
701 
702 		deleteLooseRef("refs/heads/master");
703 		assertNull(refdir.getRef("refs/heads/master"));
704 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
705 	}
706 
707 	@Test
708 	public void testGetRefs_DiscoversDeletedLoose2() throws IOException {
709 		Map<String, Ref> orig, next;
710 
711 		writeLooseRef("refs/heads/master", A);
712 		writeLooseRef("refs/heads/pu", B);
713 		orig = refdir.getRefs(RefDatabase.ALL);
714 
715 		deleteLooseRef("refs/heads/pu");
716 		next = refdir.getRefs(RefDatabase.ALL);
717 
718 		assertEquals(3, orig.size());
719 		assertEquals(2, next.size());
720 
721 		assertTrue(orig.containsKey("refs/heads/pu"));
722 		assertFalse(next.containsKey("refs/heads/pu"));
723 	}
724 
725 	@Test
726 	public void testGetRefs_DiscoversDeletedLoose3() throws IOException {
727 		Map<String, Ref> orig, next;
728 
729 		writeLooseRef("refs/heads/master", A);
730 		writeLooseRef("refs/heads/next", B);
731 		writeLooseRef("refs/heads/pu", B);
732 		writeLooseRef("refs/tags/v1.0", v1_0);
733 		orig = refdir.getRefs(RefDatabase.ALL);
734 
735 		deleteLooseRef("refs/heads/pu");
736 		deleteLooseRef("refs/heads/next");
737 		next = refdir.getRefs(RefDatabase.ALL);
738 
739 		assertEquals(5, orig.size());
740 		assertEquals(3, next.size());
741 
742 		assertTrue(orig.containsKey("refs/heads/pu"));
743 		assertTrue(orig.containsKey("refs/heads/next"));
744 		assertFalse(next.containsKey("refs/heads/pu"));
745 		assertFalse(next.containsKey("refs/heads/next"));
746 	}
747 
748 	@Test
749 	public void testGetRefs_DiscoversDeletedLoose4() throws IOException {
750 		Map<String, Ref> orig, next;
751 		Ref orig_r, next_r;
752 
753 		writeLooseRef("refs/heads/B", B);
754 		writeLooseRef("refs/heads/master", A);
755 		orig = refdir.getRefs(RefDatabase.ALL);
756 
757 		deleteLooseRef("refs/heads/master");
758 		next = refdir.getRefs("refs/heads/");
759 
760 		assertEquals(3, orig.size());
761 		assertEquals(1, next.size());
762 
763 		assertTrue(orig.containsKey("refs/heads/B"));
764 		assertTrue(orig.containsKey("refs/heads/master"));
765 		assertTrue(next.containsKey("B"));
766 		assertFalse(next.containsKey("master"));
767 
768 		orig_r = orig.get("refs/heads/B");
769 		next_r = next.get("B");
770 		assertEquals(B, orig_r.getObjectId());
771 		assertSame("uses cached instance", orig_r, next_r);
772 	}
773 
774 	@Test
775 	public void testGetRefs_DiscoversDeletedLoose5() throws IOException {
776 		Map<String, Ref> orig, next;
777 
778 		writeLooseRef("refs/heads/master", A);
779 		writeLooseRef("refs/heads/pu", B);
780 		orig = refdir.getRefs(RefDatabase.ALL);
781 
782 		deleteLooseRef("refs/heads/pu");
783 		writeLooseRef("refs/tags/v1.0", v1_0);
784 		next = refdir.getRefs(RefDatabase.ALL);
785 
786 		assertEquals(3, orig.size());
787 		assertEquals(3, next.size());
788 
789 		assertTrue(orig.containsKey("refs/heads/pu"));
790 		assertFalse(orig.containsKey("refs/tags/v1.0"));
791 		assertFalse(next.containsKey("refs/heads/pu"));
792 		assertTrue(next.containsKey("refs/tags/v1.0"));
793 	}
794 
795 	@Test
796 	public void testGetRefs_SkipsLockFiles() throws IOException {
797 		Map<String, Ref> all;
798 
799 		writeLooseRef("refs/heads/master", A);
800 		writeLooseRef("refs/heads/pu.lock", B);
801 		all = refdir.getRefs(RefDatabase.ALL);
802 
803 		assertEquals(2, all.size());
804 
805 		assertTrue(all.containsKey(HEAD));
806 		assertTrue(all.containsKey("refs/heads/master"));
807 		assertFalse(all.containsKey("refs/heads/pu.lock"));
808 	}
809 
810 	@Test
811 	public void testGetRefs_CycleInSymbolicRef() throws IOException {
812 		Map<String, Ref> all;
813 		Ref r;
814 
815 		writeLooseRef("refs/1", "ref: refs/2\n");
816 		writeLooseRef("refs/2", "ref: refs/3\n");
817 		writeLooseRef("refs/3", "ref: refs/4\n");
818 		writeLooseRef("refs/4", "ref: refs/5\n");
819 		writeLooseRef("refs/5", "ref: refs/end\n");
820 		writeLooseRef("refs/end", A);
821 
822 		all = refdir.getRefs(RefDatabase.ALL);
823 		r = all.get("refs/1");
824 		assertNotNull("has 1", r);
825 
826 		assertEquals("refs/1", r.getName());
827 		assertEquals(A, r.getObjectId());
828 		assertTrue(r.isSymbolic());
829 
830 		r = r.getTarget();
831 		assertEquals("refs/2", r.getName());
832 		assertEquals(A, r.getObjectId());
833 		assertTrue(r.isSymbolic());
834 
835 		r = r.getTarget();
836 		assertEquals("refs/3", r.getName());
837 		assertEquals(A, r.getObjectId());
838 		assertTrue(r.isSymbolic());
839 
840 		r = r.getTarget();
841 		assertEquals("refs/4", r.getName());
842 		assertEquals(A, r.getObjectId());
843 		assertTrue(r.isSymbolic());
844 
845 		r = r.getTarget();
846 		assertEquals("refs/5", r.getName());
847 		assertEquals(A, r.getObjectId());
848 		assertTrue(r.isSymbolic());
849 
850 		r = r.getTarget();
851 		assertEquals("refs/end", r.getName());
852 		assertEquals(A, r.getObjectId());
853 		assertFalse(r.isSymbolic());
854 
855 		writeLooseRef("refs/5", "ref: refs/6\n");
856 		writeLooseRef("refs/6", "ref: refs/end\n");
857 		all = refdir.getRefs(RefDatabase.ALL);
858 		r = all.get("refs/1");
859 		assertNull("mising 1 due to cycle", r);
860 	}
861 
862 	@Test
863 	public void testGetRef_CycleInSymbolicRef() throws IOException {
864 		Ref r;
865 
866 		writeLooseRef("refs/1", "ref: refs/2\n");
867 		writeLooseRef("refs/2", "ref: refs/3\n");
868 		writeLooseRef("refs/3", "ref: refs/4\n");
869 		writeLooseRef("refs/4", "ref: refs/5\n");
870 		writeLooseRef("refs/5", "ref: refs/end\n");
871 		writeLooseRef("refs/end", A);
872 
873 		r = refdir.getRef("1");
874 		assertEquals("refs/1", r.getName());
875 		assertEquals(A, r.getObjectId());
876 		assertTrue(r.isSymbolic());
877 
878 		writeLooseRef("refs/5", "ref: refs/6\n");
879 		writeLooseRef("refs/6", "ref: refs/end\n");
880 
881 		r = refdir.getRef("1");
882 		assertNull("missing 1 due to cycle", r);
883 
884 		writeLooseRef("refs/heads/1", B);
885 
886 		r = refdir.getRef("1");
887 		assertEquals("refs/heads/1", r.getName());
888 		assertEquals(B, r.getObjectId());
889 		assertFalse(r.isSymbolic());
890 	}
891 
892 	@Test
893 	public void testGetRefs_PackedNotPeeled_Sorted() throws IOException {
894 		Map<String, Ref> all;
895 
896 		writePackedRefs("" + //
897 				A.name() + " refs/heads/master\n" + //
898 				B.name() + " refs/heads/other\n" + //
899 				v1_0.name() + " refs/tags/v1.0\n");
900 		all = refdir.getRefs(RefDatabase.ALL);
901 
902 		assertEquals(4, all.size());
903 		final Ref head = all.get(HEAD);
904 		final Ref master = all.get("refs/heads/master");
905 		final Ref other = all.get("refs/heads/other");
906 		final Ref tag = all.get("refs/tags/v1.0");
907 
908 		assertEquals(A, master.getObjectId());
909 		assertFalse(master.isPeeled());
910 		assertNull(master.getPeeledObjectId());
911 
912 		assertEquals(B, other.getObjectId());
913 		assertFalse(other.isPeeled());
914 		assertNull(other.getPeeledObjectId());
915 
916 		assertSame(master, head.getTarget());
917 		assertEquals(A, head.getObjectId());
918 		assertFalse(head.isPeeled());
919 		assertNull(head.getPeeledObjectId());
920 
921 		assertEquals(v1_0, tag.getObjectId());
922 		assertFalse(tag.isPeeled());
923 		assertNull(tag.getPeeledObjectId());
924 	}
925 
926 	@Test
927 	public void testGetRef_PackedNotPeeled_WrongSort() throws IOException {
928 		writePackedRefs("" + //
929 				v1_0.name() + " refs/tags/v1.0\n" + //
930 				B.name() + " refs/heads/other\n" + //
931 				A.name() + " refs/heads/master\n");
932 
933 		final Ref head = refdir.getRef(HEAD);
934 		final Ref master = refdir.getRef("refs/heads/master");
935 		final Ref other = refdir.getRef("refs/heads/other");
936 		final Ref tag = refdir.getRef("refs/tags/v1.0");
937 
938 		assertEquals(A, master.getObjectId());
939 		assertFalse(master.isPeeled());
940 		assertNull(master.getPeeledObjectId());
941 
942 		assertEquals(B, other.getObjectId());
943 		assertFalse(other.isPeeled());
944 		assertNull(other.getPeeledObjectId());
945 
946 		assertSame(master, head.getTarget());
947 		assertEquals(A, head.getObjectId());
948 		assertFalse(head.isPeeled());
949 		assertNull(head.getPeeledObjectId());
950 
951 		assertEquals(v1_0, tag.getObjectId());
952 		assertFalse(tag.isPeeled());
953 		assertNull(tag.getPeeledObjectId());
954 	}
955 
956 	@Test
957 	public void testGetRefs_PackedWithPeeled() throws IOException {
958 		Map<String, Ref> all;
959 
960 		writePackedRefs("# pack-refs with: peeled \n" + //
961 				A.name() + " refs/heads/master\n" + //
962 				B.name() + " refs/heads/other\n" + //
963 				v1_0.name() + " refs/tags/v1.0\n" + //
964 				"^" + v1_0.getObject().name() + "\n");
965 		all = refdir.getRefs(RefDatabase.ALL);
966 
967 		assertEquals(4, all.size());
968 		final Ref head = all.get(HEAD);
969 		final Ref master = all.get("refs/heads/master");
970 		final Ref other = all.get("refs/heads/other");
971 		final Ref tag = all.get("refs/tags/v1.0");
972 
973 		assertEquals(A, master.getObjectId());
974 		assertTrue(master.isPeeled());
975 		assertNull(master.getPeeledObjectId());
976 
977 		assertEquals(B, other.getObjectId());
978 		assertTrue(other.isPeeled());
979 		assertNull(other.getPeeledObjectId());
980 
981 		assertSame(master, head.getTarget());
982 		assertEquals(A, head.getObjectId());
983 		assertTrue(head.isPeeled());
984 		assertNull(head.getPeeledObjectId());
985 
986 		assertEquals(v1_0, tag.getObjectId());
987 		assertTrue(tag.isPeeled());
988 		assertEquals(v1_0.getObject(), tag.getPeeledObjectId());
989 	}
990 
991 	@Test
992 	public void test_repack() throws Exception {
993 		Map<String, Ref> all;
994 
995 		writePackedRefs("# pack-refs with: peeled \n" + //
996 				A.name() + " refs/heads/master\n" + //
997 				B.name() + " refs/heads/other\n" + //
998 				v1_0.name() + " refs/tags/v1.0\n" + //
999 				"^" + v1_0.getObject().name() + "\n");
1000 		all = refdir.getRefs(RefDatabase.ALL);
1001 
1002 		assertEquals(4, all.size());
1003 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1004 		assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage());
1005 		assertEquals(A.getId(), all.get("refs/heads/master").getObjectId());
1006 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1007 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1008 
1009 		repo.update("refs/heads/master", B.getId());
1010 		RevTag v0_1 = repo.tag("v0.1", A);
1011 		repo.update("refs/tags/v0.1", v0_1);
1012 
1013 		all = refdir.getRefs(RefDatabase.ALL);
1014 		assertEquals(5, all.size());
1015 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1016 		// Why isn't the next ref LOOSE_PACKED?
1017 		assertEquals(Storage.LOOSE, all.get("refs/heads/master")
1018 				.getStorage());
1019 		assertEquals(B.getId(), all.get("refs/heads/master").getObjectId());
1020 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1021 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1022 		assertEquals(Storage.LOOSE, all.get("refs/tags/v0.1").getStorage());
1023 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
1024 
1025 		all = refdir.getRefs(RefDatabase.ALL);
1026 		refdir.pack(new ArrayList<>(all.keySet()));
1027 
1028 		all = refdir.getRefs(RefDatabase.ALL);
1029 		assertEquals(5, all.size());
1030 		assertEquals(Storage.LOOSE, all.get(HEAD).getStorage());
1031 		// Why isn't the next ref LOOSE_PACKED?
1032 		assertEquals(Storage.PACKED, all.get("refs/heads/master").getStorage());
1033 		assertEquals(B.getId(), all.get("refs/heads/master").getObjectId());
1034 		assertEquals(Storage.PACKED, all.get("refs/heads/other").getStorage());
1035 		assertEquals(Storage.PACKED, all.get("refs/tags/v1.0").getStorage());
1036 		assertEquals(Storage.PACKED, all.get("refs/tags/v0.1").getStorage());
1037 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
1038 	}
1039 
1040 	@Test
1041 	public void testGetRef_EmptyDatabase() throws IOException {
1042 		Ref r;
1043 
1044 		r = refdir.getRef(HEAD);
1045 		assertTrue(r.isSymbolic());
1046 		assertSame(LOOSE, r.getStorage());
1047 		assertEquals("refs/heads/master", r.getTarget().getName());
1048 		assertSame(NEW, r.getTarget().getStorage());
1049 		assertNull(r.getTarget().getObjectId());
1050 
1051 		assertNull(refdir.getRef("refs/heads/master"));
1052 		assertNull(refdir.getRef("refs/tags/v1.0"));
1053 		assertNull(refdir.getRef("FETCH_HEAD"));
1054 		assertNull(refdir.getRef("NOT.A.REF.NAME"));
1055 		assertNull(refdir.getRef("master"));
1056 		assertNull(refdir.getRef("v1.0"));
1057 	}
1058 
1059 	@Test
1060 	public void testExactRef_EmptyDatabase() throws IOException {
1061 		Ref r;
1062 
1063 		r = refdir.exactRef(HEAD);
1064 		assertTrue(r.isSymbolic());
1065 		assertSame(LOOSE, r.getStorage());
1066 		assertEquals("refs/heads/master", r.getTarget().getName());
1067 		assertSame(NEW, r.getTarget().getStorage());
1068 		assertNull(r.getTarget().getObjectId());
1069 
1070 		assertNull(refdir.exactRef("refs/heads/master"));
1071 		assertNull(refdir.exactRef("refs/tags/v1.0"));
1072 		assertNull(refdir.exactRef("FETCH_HEAD"));
1073 		assertNull(refdir.exactRef("NOT.A.REF.NAME"));
1074 		assertNull(refdir.exactRef("master"));
1075 		assertNull(refdir.exactRef("v1.0"));
1076 	}
1077 
1078 	@Test
1079 	public void testGetRef_FetchHead() throws IOException {
1080 		// This is an odd special case where we need to make sure we read
1081 		// exactly the first 40 bytes of the file and nothing further on
1082 		// that line, or the remainder of the file.
1083 		write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
1084 				+ "\tnot-for-merge"
1085 				+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
1086 
1087 		Ref r = refdir.getRef("FETCH_HEAD");
1088 		assertFalse(r.isSymbolic());
1089 		assertEquals(A, r.getObjectId());
1090 		assertEquals("FETCH_HEAD", r.getName());
1091 		assertFalse(r.isPeeled());
1092 		assertNull(r.getPeeledObjectId());
1093 	}
1094 
1095 	@Test
1096 	public void testExactRef_FetchHead() throws IOException {
1097 		// This is an odd special case where we need to make sure we read
1098 		// exactly the first 40 bytes of the file and nothing further on
1099 		// that line, or the remainder of the file.
1100 		write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
1101 				+ "\tnot-for-merge"
1102 				+ "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
1103 
1104 		Ref r = refdir.exactRef("FETCH_HEAD");
1105 		assertFalse(r.isSymbolic());
1106 		assertEquals(A, r.getObjectId());
1107 		assertEquals("FETCH_HEAD", r.getName());
1108 		assertFalse(r.isPeeled());
1109 		assertNull(r.getPeeledObjectId());
1110 	}
1111 
1112 	@Test
1113 	public void testGetRef_AnyHeadWithGarbage() throws IOException {
1114 		write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name()
1115 				+ "012345 . this is not a standard reference\n"
1116 				+ "#and even more junk\n");
1117 
1118 		Ref r = refdir.getRef("refs/heads/A");
1119 		assertFalse(r.isSymbolic());
1120 		assertEquals(A, r.getObjectId());
1121 		assertEquals("refs/heads/A", r.getName());
1122 		assertFalse(r.isPeeled());
1123 		assertNull(r.getPeeledObjectId());
1124 	}
1125 
1126 	@Test
1127 	public void testGetRefs_CorruptSymbolicReference() throws IOException {
1128 		String name = "refs/heads/A";
1129 		writeLooseRef(name, "ref: \n");
1130 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
1131 	}
1132 
1133 	@Test
1134 	public void testGetRef_CorruptSymbolicReference() throws IOException {
1135 		String name = "refs/heads/A";
1136 		writeLooseRef(name, "ref: \n");
1137 		try {
1138 			refdir.getRef(name);
1139 			fail("read an invalid reference");
1140 		} catch (IOException err) {
1141 			String msg = err.getMessage();
1142 			assertEquals("Not a ref: " + name + ": ref:", msg);
1143 		}
1144 	}
1145 
1146 	@Test
1147 	public void testGetRefs_CorruptObjectIdReference() throws IOException {
1148 		String name = "refs/heads/A";
1149 		String content = "zoo" + A.name();
1150 		writeLooseRef(name, content + "\n");
1151 		assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty());
1152 	}
1153 
1154 	@Test
1155 	public void testGetRef_CorruptObjectIdReference() throws IOException {
1156 		String name = "refs/heads/A";
1157 		String content = "zoo" + A.name();
1158 		writeLooseRef(name, content + "\n");
1159 		try {
1160 			refdir.getRef(name);
1161 			fail("read an invalid reference");
1162 		} catch (IOException err) {
1163 			String msg = err.getMessage();
1164 			assertEquals("Not a ref: " + name + ": " + content, msg);
1165 		}
1166 	}
1167 
1168 	@Test
1169 	public void testIsNameConflicting() throws IOException {
1170 		writeLooseRef("refs/heads/a/b", A);
1171 		writePackedRef("refs/heads/q", B);
1172 
1173 		// new references cannot replace an existing container
1174 		assertTrue(refdir.isNameConflicting("refs"));
1175 		assertTrue(refdir.isNameConflicting("refs/heads"));
1176 		assertTrue(refdir.isNameConflicting("refs/heads/a"));
1177 
1178 		// existing reference is not conflicting
1179 		assertFalse(refdir.isNameConflicting("refs/heads/a/b"));
1180 
1181 		// new references are not conflicting
1182 		assertFalse(refdir.isNameConflicting("refs/heads/a/d"));
1183 		assertFalse(refdir.isNameConflicting("refs/heads/master"));
1184 
1185 		// existing reference must not be used as a container
1186 		assertTrue(refdir.isNameConflicting("refs/heads/a/b/c"));
1187 		assertTrue(refdir.isNameConflicting("refs/heads/q/master"));
1188 	}
1189 
1190 	@Test
1191 	public void testPeelLooseTag() throws IOException {
1192 		writeLooseRef("refs/tags/v1_0", v1_0);
1193 		writeLooseRef("refs/tags/current", "ref: refs/tags/v1_0\n");
1194 
1195 		final Ref tag = refdir.getRef("refs/tags/v1_0");
1196 		final Ref cur = refdir.getRef("refs/tags/current");
1197 
1198 		assertEquals(v1_0, tag.getObjectId());
1199 		assertFalse(tag.isSymbolic());
1200 		assertFalse(tag.isPeeled());
1201 		assertNull(tag.getPeeledObjectId());
1202 
1203 		assertEquals(v1_0, cur.getObjectId());
1204 		assertTrue(cur.isSymbolic());
1205 		assertFalse(cur.isPeeled());
1206 		assertNull(cur.getPeeledObjectId());
1207 
1208 		final Ref tag_p = refdir.peel(tag);
1209 		final Ref cur_p = refdir.peel(cur);
1210 
1211 		assertNotSame(tag, tag_p);
1212 		assertFalse(tag_p.isSymbolic());
1213 		assertTrue(tag_p.isPeeled());
1214 		assertEquals(v1_0, tag_p.getObjectId());
1215 		assertEquals(v1_0.getObject(), tag_p.getPeeledObjectId());
1216 		assertSame(tag_p, refdir.peel(tag_p));
1217 
1218 		assertNotSame(cur, cur_p);
1219 		assertEquals("refs/tags/current", cur_p.getName());
1220 		assertTrue(cur_p.isSymbolic());
1221 		assertEquals("refs/tags/v1_0", cur_p.getTarget().getName());
1222 		assertTrue(cur_p.isPeeled());
1223 		assertEquals(v1_0, cur_p.getObjectId());
1224 		assertEquals(v1_0.getObject(), cur_p.getPeeledObjectId());
1225 
1226 		// reuses cached peeling later, but not immediately due to
1227 		// the implementation so we have to fetch it once.
1228 		final Ref tag_p2 = refdir.getRef("refs/tags/v1_0");
1229 		assertFalse(tag_p2.isSymbolic());
1230 		assertTrue(tag_p2.isPeeled());
1231 		assertEquals(v1_0, tag_p2.getObjectId());
1232 		assertEquals(v1_0.getObject(), tag_p2.getPeeledObjectId());
1233 
1234 		assertSame(tag_p2, refdir.getRef("refs/tags/v1_0"));
1235 		assertSame(tag_p2, refdir.getRef("refs/tags/current").getTarget());
1236 		assertSame(tag_p2, refdir.peel(tag_p2));
1237 	}
1238 
1239 	@Test
1240 	public void testPeelCommit() throws IOException {
1241 		writeLooseRef("refs/heads/master", A);
1242 
1243 		Ref master = refdir.getRef("refs/heads/master");
1244 		assertEquals(A, master.getObjectId());
1245 		assertFalse(master.isPeeled());
1246 		assertNull(master.getPeeledObjectId());
1247 
1248 		Ref master_p = refdir.peel(master);
1249 		assertNotSame(master, master_p);
1250 		assertEquals(A, master_p.getObjectId());
1251 		assertTrue(master_p.isPeeled());
1252 		assertNull(master_p.getPeeledObjectId());
1253 
1254 		// reuses cached peeling later, but not immediately due to
1255 		// the implementation so we have to fetch it once.
1256 		Ref master_p2 = refdir.getRef("refs/heads/master");
1257 		assertNotSame(master, master_p2);
1258 		assertEquals(A, master_p2.getObjectId());
1259 		assertTrue(master_p2.isPeeled());
1260 		assertNull(master_p2.getPeeledObjectId());
1261 		assertSame(master_p2, refdir.peel(master_p2));
1262 	}
1263 
1264 	@Test
1265 	public void testRefsChangedStackOverflow() throws Exception {
1266 		final FileRepository newRepo = createBareRepository();
1267 		final RefDatabase refDb = newRepo.getRefDatabase();
1268 		File packedRefs = new File(newRepo.getDirectory(), "packed-refs");
1269 		assertTrue(packedRefs.createNewFile());
1270 		final AtomicReference<StackOverflowError> error = new AtomicReference<>();
1271 		final AtomicReference<IOException> exception = new AtomicReference<>();
1272 		final AtomicInteger changeCount = new AtomicInteger();
1273 		newRepo.getListenerList().addRefsChangedListener(
1274 				new RefsChangedListener() {
1275 
1276 					@Override
1277 					public void onRefsChanged(RefsChangedEvent event) {
1278 						try {
1279 							refDb.getRefs("ref");
1280 							changeCount.incrementAndGet();
1281 						} catch (StackOverflowError soe) {
1282 							error.set(soe);
1283 						} catch (IOException ioe) {
1284 							exception.set(ioe);
1285 						}
1286 					}
1287 				});
1288 		refDb.getRefs("ref");
1289 		refDb.getRefs("ref");
1290 		assertNull(error.get());
1291 		assertNull(exception.get());
1292 		assertEquals(1, changeCount.get());
1293 	}
1294 
1295 	@Test
1296 	public void testBatchRefUpdateSimpleNoForce() throws IOException {
1297 		writeLooseRef("refs/heads/master", A);
1298 		writeLooseRef("refs/heads/masters", B);
1299 		List<ReceiveCommand> commands = Arrays.asList(
1300 				newCommand(A, B, "refs/heads/master",
1301 						ReceiveCommand.Type.UPDATE),
1302 				newCommand(B, A, "refs/heads/masters",
1303 						ReceiveCommand.Type.UPDATE_NONFASTFORWARD));
1304 		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
1305 		batchUpdate.addCommand(commands);
1306 		batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor());
1307 		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
1308 		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
1309 		assertEquals(ReceiveCommand.Result.REJECTED_NONFASTFORWARD, commands
1310 				.get(1).getResult());
1311 		assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs
1312 				.keySet().toString());
1313 		assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
1314 		assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId());
1315 	}
1316 
1317 	@Test
1318 	public void testBatchRefUpdateSimpleForce() throws IOException {
1319 		writeLooseRef("refs/heads/master", A);
1320 		writeLooseRef("refs/heads/masters", B);
1321 		List<ReceiveCommand> commands = Arrays.asList(
1322 				newCommand(A, B, "refs/heads/master",
1323 						ReceiveCommand.Type.UPDATE),
1324 				newCommand(B, A, "refs/heads/masters",
1325 						ReceiveCommand.Type.UPDATE_NONFASTFORWARD));
1326 		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
1327 		batchUpdate.setAllowNonFastForwards(true);
1328 		batchUpdate.addCommand(commands);
1329 		batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor());
1330 		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
1331 		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
1332 		assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult());
1333 		assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs
1334 				.keySet().toString());
1335 		assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
1336 		assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId());
1337 	}
1338 
1339 	@Test
1340 	public void testBatchRefUpdateNonFastForwardDoesNotDoExpensiveMergeCheck()
1341 			throws IOException {
1342 		writeLooseRef("refs/heads/master", B);
1343 		List<ReceiveCommand> commands = Arrays.asList(
1344 				newCommand(B, A, "refs/heads/master",
1345 						ReceiveCommand.Type.UPDATE_NONFASTFORWARD));
1346 		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
1347 		batchUpdate.setAllowNonFastForwards(true);
1348 		batchUpdate.addCommand(commands);
1349 		batchUpdate.execute(new RevWalk(diskRepo) {
1350 			@Override
1351 			public boolean isMergedInto(RevCommit base, RevCommit tip) {
1352 				throw new AssertionError("isMergedInto() should not be called");
1353 			}
1354 		}, new StrictWorkMonitor());
1355 		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
1356 		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
1357 		assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId());
1358 	}
1359 
1360 	@Test
1361 	public void testBatchRefUpdateConflict() throws IOException {
1362 		writeLooseRef("refs/heads/master", A);
1363 		writeLooseRef("refs/heads/masters", B);
1364 		List<ReceiveCommand> commands = Arrays.asList(
1365 				newCommand(A, B, "refs/heads/master",
1366 						ReceiveCommand.Type.UPDATE),
1367 				newCommand(null, A, "refs/heads/master/x",
1368 						ReceiveCommand.Type.CREATE),
1369 				newCommand(null, A, "refs/heads", ReceiveCommand.Type.CREATE));
1370 		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
1371 		batchUpdate.setAllowNonFastForwards(true);
1372 		batchUpdate.addCommand(commands);
1373 		batchUpdate
1374 				.execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE);
1375 		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
1376 		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
1377 		assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(1)
1378 				.getResult());
1379 		assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(2)
1380 				.getResult());
1381 		assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs
1382 				.keySet().toString());
1383 		assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
1384 		assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId());
1385 	}
1386 
1387 	@Test
1388 	public void testBatchRefUpdateConflictThanksToDelete() throws IOException {
1389 		writeLooseRef("refs/heads/master", A);
1390 		writeLooseRef("refs/heads/masters", B);
1391 		List<ReceiveCommand> commands = Arrays.asList(
1392 				newCommand(A, B, "refs/heads/master",
1393 						ReceiveCommand.Type.UPDATE),
1394 				newCommand(null, A, "refs/heads/masters/x",
1395 						ReceiveCommand.Type.CREATE),
1396 				newCommand(B, null, "refs/heads/masters",
1397 						ReceiveCommand.Type.DELETE));
1398 		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
1399 		batchUpdate.setAllowNonFastForwards(true);
1400 		batchUpdate.addCommand(commands);
1401 		batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor());
1402 		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
1403 		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
1404 		assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult());
1405 		assertEquals(ReceiveCommand.Result.OK, commands.get(2).getResult());
1406 		assertEquals("[HEAD, refs/heads/master, refs/heads/masters/x]", refs
1407 				.keySet().toString());
1408 		assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId());
1409 	}
1410 
1411 	private static ReceiveCommand newCommand(RevCommit a, RevCommit b,
1412 			String string, Type update) {
1413 		return new ReceiveCommand(a != null ? a.getId() : null,
1414 				b != null ? b.getId() : null, string, update);
1415 	}
1416 
1417 	private void writeLooseRef(String name, AnyObjectId id) throws IOException {
1418 		writeLooseRef(name, id.name() + "\n");
1419 	}
1420 
1421 	private void writeLooseRef(String name, String content) throws IOException {
1422 		write(new File(diskRepo.getDirectory(), name), content);
1423 	}
1424 
1425 	private void writePackedRef(String name, AnyObjectId id) throws IOException {
1426 		writePackedRefs(id.name() + " " + name + "\n");
1427 	}
1428 
1429 	private void writePackedRefs(String content) throws IOException {
1430 		File pr = new File(diskRepo.getDirectory(), "packed-refs");
1431 		write(pr, content);
1432 
1433 		final long now = System.currentTimeMillis();
1434 		final int oneHourAgo = 3600 * 1000;
1435 		pr.setLastModified(now - oneHourAgo);
1436 	}
1437 
1438 	private void deleteLooseRef(String name) {
1439 		File path = new File(diskRepo.getDirectory(), name);
1440 		assertTrue("deleted " + name, path.delete());
1441 	}
1442 
1443 	private static final class StrictWorkMonitor implements ProgressMonitor {
1444 		private int lastWork, totalWork;
1445 
1446 		@Override
1447 		public void start(int totalTasks) {
1448 			// empty
1449 		}
1450 
1451 		@Override
1452 		public void beginTask(String title, int total) {
1453 			this.totalWork = total;
1454 			lastWork = 0;
1455 		}
1456 
1457 		@Override
1458 		public void update(int completed) {
1459 			lastWork += completed;
1460 		}
1461 
1462 		@Override
1463 		public void endTask() {
1464 			assertEquals("Units of work recorded", totalWork, lastWork);
1465 		}
1466 
1467 		@Override
1468 		public boolean isCancelled() {
1469 			return false;
1470 		}
1471 	}
1472 }