1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.internal.storage.reftree;
45
46 import static org.eclipse.jgit.lib.Constants.HEAD;
47 import static org.eclipse.jgit.lib.Constants.ORIG_HEAD;
48 import static org.eclipse.jgit.lib.Constants.R_HEADS;
49 import static org.eclipse.jgit.lib.Constants.R_TAGS;
50 import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
51 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
52 import static org.eclipse.jgit.lib.RefDatabase.ALL;
53 import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
54 import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
55 import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
56 import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
57 import static org.junit.Assert.assertEquals;
58 import static org.junit.Assert.assertFalse;
59 import static org.junit.Assert.assertNotEquals;
60 import static org.junit.Assert.assertNotNull;
61 import static org.junit.Assert.assertNull;
62 import static org.junit.Assert.assertSame;
63 import static org.junit.Assert.assertTrue;
64 import static org.junit.Assert.fail;
65
66 import java.io.IOException;
67 import java.text.MessageFormat;
68 import java.util.Arrays;
69 import java.util.Collections;
70 import java.util.List;
71 import java.util.Map;
72
73 import org.eclipse.jgit.internal.JGitText;
74 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
75 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
76 import org.eclipse.jgit.junit.TestRepository;
77 import org.eclipse.jgit.lib.AnyObjectId;
78 import org.eclipse.jgit.lib.BatchRefUpdate;
79 import org.eclipse.jgit.lib.CommitBuilder;
80 import org.eclipse.jgit.lib.NullProgressMonitor;
81 import org.eclipse.jgit.lib.ObjectId;
82 import org.eclipse.jgit.lib.ObjectIdRef;
83 import org.eclipse.jgit.lib.ObjectInserter;
84 import org.eclipse.jgit.lib.ObjectReader;
85 import org.eclipse.jgit.lib.Ref;
86 import org.eclipse.jgit.lib.RefDatabase;
87 import org.eclipse.jgit.lib.RefUpdate;
88 import org.eclipse.jgit.lib.SymbolicRef;
89 import org.eclipse.jgit.revwalk.RevCommit;
90 import org.eclipse.jgit.revwalk.RevTag;
91 import org.eclipse.jgit.revwalk.RevWalk;
92 import org.eclipse.jgit.transport.ReceiveCommand;
93 import org.junit.Before;
94 import org.junit.Test;
95
96 public class RefTreeDatabaseTest {
97 private InMemRefTreeRepo repo;
98 private RefTreeDatabase refdb;
99 private RefDatabase bootstrap;
100
101 private TestRepository<InMemRefTreeRepo> testRepo;
102 private RevCommit A;
103 private RevCommit B;
104 private RevTag v1_0;
105
106 @Before
107 public void setUp() throws Exception {
108 repo = new InMemRefTreeRepo(new DfsRepositoryDescription("test"));
109 bootstrap = refdb.getBootstrap();
110
111 testRepo = new TestRepository<>(repo);
112 A = testRepo.commit().create();
113 B = testRepo.commit(testRepo.getRevWalk().parseCommit(A));
114 v1_0 = testRepo.tag("v1_0", B);
115 testRepo.getRevWalk().parseBody(v1_0);
116 }
117
118 @Test
119 public void testSupportsAtomic() {
120 assertTrue(refdb.performsAtomicTransactions());
121 }
122
123 @Test
124 public void testGetRefs_EmptyDatabase() throws IOException {
125 assertTrue("no references", refdb.getRefs(ALL).isEmpty());
126 assertTrue("no references", refdb.getRefs(R_HEADS).isEmpty());
127 assertTrue("no references", refdb.getRefs(R_TAGS).isEmpty());
128 assertTrue("no references", refdb.getAdditionalRefs().isEmpty());
129 }
130
131 @Test
132 public void testGetAdditionalRefs() throws IOException {
133 update("refs/heads/master", A);
134
135 List<Ref> addl = refdb.getAdditionalRefs();
136 assertEquals(1, addl.size());
137 assertEquals("refs/txn/committed", addl.get(0).getName());
138 assertEquals(getTxnCommitted(), addl.get(0).getObjectId());
139 }
140
141 @Test
142 public void testGetRefs_HeadOnOneBranch() throws IOException {
143 symref(HEAD, "refs/heads/master");
144 update("refs/heads/master", A);
145
146 Map<String, Ref> all = refdb.getRefs(ALL);
147 assertEquals(2, all.size());
148 assertTrue("has HEAD", all.containsKey(HEAD));
149 assertTrue("has master", all.containsKey("refs/heads/master"));
150
151 Ref head = all.get(HEAD);
152 Ref master = all.get("refs/heads/master");
153
154 assertEquals(HEAD, head.getName());
155 assertTrue(head.isSymbolic());
156 assertSame(LOOSE, head.getStorage());
157 assertSame("uses same ref as target", master, head.getTarget());
158
159 assertEquals("refs/heads/master", master.getName());
160 assertFalse(master.isSymbolic());
161 assertSame(PACKED, master.getStorage());
162 assertEquals(A, master.getObjectId());
163 }
164
165 @Test
166 public void testGetRefs_DetachedHead() throws IOException {
167 update(HEAD, A);
168
169 Map<String, Ref> all = refdb.getRefs(ALL);
170 assertEquals(1, all.size());
171 assertTrue("has HEAD", all.containsKey(HEAD));
172
173 Ref head = all.get(HEAD);
174 assertEquals(HEAD, head.getName());
175 assertFalse(head.isSymbolic());
176 assertSame(PACKED, head.getStorage());
177 assertEquals(A, head.getObjectId());
178 }
179
180 @Test
181 public void testGetRefs_DeeplyNestedBranch() throws IOException {
182 String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k";
183 update(name, A);
184
185 Map<String, Ref> all = refdb.getRefs(ALL);
186 assertEquals(1, all.size());
187
188 Ref r = all.get(name);
189 assertEquals(name, r.getName());
190 assertFalse(r.isSymbolic());
191 assertSame(PACKED, r.getStorage());
192 assertEquals(A, r.getObjectId());
193 }
194
195 @Test
196 public void testGetRefs_HeadBranchNotBorn() throws IOException {
197 update("refs/heads/A", A);
198 update("refs/heads/B", B);
199
200 Map<String, Ref> all = refdb.getRefs(ALL);
201 assertEquals(2, all.size());
202 assertFalse("no HEAD", all.containsKey(HEAD));
203
204 Ref a = all.get("refs/heads/A");
205 Ref b = all.get("refs/heads/B");
206
207 assertEquals(A, a.getObjectId());
208 assertEquals(B, b.getObjectId());
209
210 assertEquals("refs/heads/A", a.getName());
211 assertEquals("refs/heads/B", b.getName());
212 }
213
214 @Test
215 public void testGetRefs_HeadsOnly() throws IOException {
216 update("refs/heads/A", A);
217 update("refs/heads/B", B);
218 update("refs/tags/v1.0", v1_0);
219
220 Map<String, Ref> heads = refdb.getRefs(R_HEADS);
221 assertEquals(2, heads.size());
222
223 Ref a = heads.get("A");
224 Ref b = heads.get("B");
225
226 assertEquals("refs/heads/A", a.getName());
227 assertEquals("refs/heads/B", b.getName());
228
229 assertEquals(A, a.getObjectId());
230 assertEquals(B, b.getObjectId());
231 }
232
233 @Test
234 public void testGetRefs_TagsOnly() throws IOException {
235 update("refs/heads/A", A);
236 update("refs/heads/B", B);
237 update("refs/tags/v1.0", v1_0);
238
239 Map<String, Ref> tags = refdb.getRefs(R_TAGS);
240 assertEquals(1, tags.size());
241
242 Ref a = tags.get("v1.0");
243 assertEquals("refs/tags/v1.0", a.getName());
244 assertEquals(v1_0, a.getObjectId());
245 assertTrue(a.isPeeled());
246 assertEquals(v1_0.getObject(), a.getPeeledObjectId());
247 }
248
249 @Test
250 public void testGetRefs_HeadsSymref() throws IOException {
251 symref("refs/heads/other", "refs/heads/master");
252 update("refs/heads/master", A);
253
254 Map<String, Ref> heads = refdb.getRefs(R_HEADS);
255 assertEquals(2, heads.size());
256
257 Ref master = heads.get("master");
258 Ref other = heads.get("other");
259
260 assertEquals("refs/heads/master", master.getName());
261 assertEquals(A, master.getObjectId());
262
263 assertEquals("refs/heads/other", other.getName());
264 assertEquals(A, other.getObjectId());
265 assertSame(master, other.getTarget());
266 }
267
268 @Test
269 public void testGetRefs_InvalidPrefixes() throws IOException {
270 update("refs/heads/A", A);
271
272 assertTrue("empty refs/heads", refdb.getRefs("refs/heads").isEmpty());
273 assertTrue("empty objects", refdb.getRefs("objects").isEmpty());
274 assertTrue("empty objects/", refdb.getRefs("objects/").isEmpty());
275 }
276
277 @Test
278 public void testGetRefs_DiscoversNew() throws IOException {
279 update("refs/heads/master", A);
280 Map<String, Ref> orig = refdb.getRefs(ALL);
281
282 update("refs/heads/next", B);
283 Map<String, Ref> next = refdb.getRefs(ALL);
284
285 assertEquals(1, orig.size());
286 assertEquals(2, next.size());
287
288 assertFalse(orig.containsKey("refs/heads/next"));
289 assertTrue(next.containsKey("refs/heads/next"));
290
291 assertEquals(A, next.get("refs/heads/master").getObjectId());
292 assertEquals(B, next.get("refs/heads/next").getObjectId());
293 }
294
295 @Test
296 public void testGetRefs_DiscoversModified() throws IOException {
297 symref(HEAD, "refs/heads/master");
298 update("refs/heads/master", A);
299
300 Map<String, Ref> all = refdb.getRefs(ALL);
301 assertEquals(A, all.get(HEAD).getObjectId());
302
303 update("refs/heads/master", B);
304 all = refdb.getRefs(ALL);
305 assertEquals(B, all.get(HEAD).getObjectId());
306 assertEquals(B, refdb.exactRef(HEAD).getObjectId());
307 }
308
309 @Test
310 public void testGetRefs_CycleInSymbolicRef() throws IOException {
311 symref("refs/1", "refs/2");
312 symref("refs/2", "refs/3");
313 symref("refs/3", "refs/4");
314 symref("refs/4", "refs/5");
315 symref("refs/5", "refs/end");
316 update("refs/end", A);
317
318 Map<String, Ref> all = refdb.getRefs(ALL);
319 Ref r = all.get("refs/1");
320 assertNotNull("has 1", r);
321
322 assertEquals("refs/1", r.getName());
323 assertEquals(A, r.getObjectId());
324 assertTrue(r.isSymbolic());
325
326 r = r.getTarget();
327 assertEquals("refs/2", r.getName());
328 assertEquals(A, r.getObjectId());
329 assertTrue(r.isSymbolic());
330
331 r = r.getTarget();
332 assertEquals("refs/3", r.getName());
333 assertEquals(A, r.getObjectId());
334 assertTrue(r.isSymbolic());
335
336 r = r.getTarget();
337 assertEquals("refs/4", r.getName());
338 assertEquals(A, r.getObjectId());
339 assertTrue(r.isSymbolic());
340
341 r = r.getTarget();
342 assertEquals("refs/5", r.getName());
343 assertEquals(A, r.getObjectId());
344 assertTrue(r.isSymbolic());
345
346 r = r.getTarget();
347 assertEquals("refs/end", r.getName());
348 assertEquals(A, r.getObjectId());
349 assertFalse(r.isSymbolic());
350
351 symref("refs/5", "refs/6");
352 symref("refs/6", "refs/end");
353 all = refdb.getRefs(ALL);
354 assertNull("mising 1 due to cycle", all.get("refs/1"));
355 assertEquals(A, all.get("refs/2").getObjectId());
356 assertEquals(A, all.get("refs/3").getObjectId());
357 assertEquals(A, all.get("refs/4").getObjectId());
358 assertEquals(A, all.get("refs/5").getObjectId());
359 assertEquals(A, all.get("refs/6").getObjectId());
360 assertEquals(A, all.get("refs/end").getObjectId());
361 }
362
363 @Test
364 public void testGetRef_NonExistingBranchConfig() throws IOException {
365 assertNull("find branch config", refdb.findRef("config"));
366 assertNull("find branch config", refdb.findRef("refs/heads/config"));
367 }
368
369 @Test
370 public void testGetRef_FindBranchConfig() throws IOException {
371 update("refs/heads/config", A);
372
373 for (String t : new String[] { "config", "refs/heads/config" }) {
374 Ref r = refdb.findRef(t);
375 assertNotNull("find branch config (" + t + ")", r);
376 assertEquals("for " + t, "refs/heads/config", r.getName());
377 assertEquals("for " + t, A, r.getObjectId());
378 }
379 }
380
381 @Test
382 public void testFirstExactRef() throws IOException {
383 update("refs/heads/A", A);
384 update("refs/tags/v1.0", v1_0);
385
386 Ref a = refdb.firstExactRef("refs/heads/A", "refs/tags/v1.0");
387 Ref one = refdb.firstExactRef("refs/tags/v1.0", "refs/heads/A");
388
389 assertEquals("refs/heads/A", a.getName());
390 assertEquals("refs/tags/v1.0", one.getName());
391
392 assertEquals(A, a.getObjectId());
393 assertEquals(v1_0, one.getObjectId());
394 }
395
396 @Test
397 public void testExactRef_DiscoversModified() throws IOException {
398 symref(HEAD, "refs/heads/master");
399 update("refs/heads/master", A);
400 assertEquals(A, refdb.exactRef(HEAD).getObjectId());
401
402 update("refs/heads/master", B);
403 assertEquals(B, refdb.exactRef(HEAD).getObjectId());
404 }
405
406 @Test
407 public void testIsNameConflicting() throws IOException {
408 update("refs/heads/a/b", A);
409 update("refs/heads/q", B);
410
411
412 assertTrue(refdb.isNameConflicting("refs"));
413 assertTrue(refdb.isNameConflicting("refs/heads"));
414 assertTrue(refdb.isNameConflicting("refs/heads/a"));
415
416
417 assertFalse(refdb.isNameConflicting("refs/heads/a/b"));
418
419
420 assertFalse(refdb.isNameConflicting("refs/heads/a/d"));
421 assertFalse(refdb.isNameConflicting("refs/heads/master"));
422
423
424 assertTrue(refdb.isNameConflicting("refs/heads/a/b/c"));
425 assertTrue(refdb.isNameConflicting("refs/heads/q/master"));
426
427
428 assertTrue(refdb.isNameConflicting(refdb.getTxnCommitted()));
429 assertTrue(refdb.isNameConflicting("refs/txn/foo"));
430 }
431
432 @Test
433 public void testUpdate_RefusesRefsTxnNamespace() throws IOException {
434 ObjectId txnId = getTxnCommitted();
435
436 RefUpdate u = refdb.newUpdate("refs/txn/tmp", false);
437 u.setNewObjectId(B);
438 assertEquals(RefUpdate.Result.LOCK_FAILURE, u.update());
439 assertEquals(txnId, getTxnCommitted());
440
441 ReceiveCommand cmd = command(null, B, "refs/txn/tmp");
442 BatchRefUpdate batch = refdb.newBatchUpdate();
443 batch.addCommand(cmd);
444 batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
445
446 assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
447 assertEquals(MessageFormat.format(JGitText.get().invalidRefName,
448 "refs/txn/tmp"), cmd.getMessage());
449 assertEquals(txnId, getTxnCommitted());
450 }
451
452 @Test
453 public void testUpdate_RefusesDotLockInRefName() throws IOException {
454 ObjectId txnId = getTxnCommitted();
455
456 RefUpdate u = refdb.newUpdate("refs/heads/pu.lock", false);
457 u.setNewObjectId(B);
458 assertEquals(RefUpdate.Result.REJECTED, u.update());
459 assertEquals(txnId, getTxnCommitted());
460
461 ReceiveCommand cmd = command(null, B, "refs/heads/pu.lock");
462 BatchRefUpdate batch = refdb.newBatchUpdate();
463 batch.addCommand(cmd);
464 batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
465
466 assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
467 assertEquals(JGitText.get().funnyRefname, cmd.getMessage());
468 assertEquals(txnId, getTxnCommitted());
469 }
470
471 @Test
472 public void testUpdate_RefusesOrigHeadOnBare() throws IOException {
473 assertTrue(refdb.getRepository().isBare());
474 ObjectId txnId = getTxnCommitted();
475
476 RefUpdate orig = refdb.newUpdate(ORIG_HEAD, true);
477 orig.setNewObjectId(B);
478 assertEquals(RefUpdate.Result.LOCK_FAILURE, orig.update());
479 assertEquals(txnId, getTxnCommitted());
480
481 ReceiveCommand cmd = command(null, B, ORIG_HEAD);
482 BatchRefUpdate batch = refdb.newBatchUpdate();
483 batch.addCommand(cmd);
484 batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
485 assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
486 assertEquals(
487 MessageFormat.format(JGitText.get().invalidRefName, ORIG_HEAD),
488 cmd.getMessage());
489 assertEquals(txnId, getTxnCommitted());
490 }
491
492 @Test
493 public void testBatchRefUpdate_NonFastForwardAborts() throws IOException {
494 update("refs/heads/master", A);
495 update("refs/heads/masters", B);
496 ObjectId txnId = getTxnCommitted();
497
498 List<ReceiveCommand> commands = Arrays.asList(
499 command(A, B, "refs/heads/master"),
500 command(B, A, "refs/heads/masters"));
501 BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
502 batchUpdate.addCommand(commands);
503 batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
504 assertEquals(txnId, getTxnCommitted());
505
506 assertEquals(REJECTED_NONFASTFORWARD,
507 commands.get(1).getResult());
508 assertEquals(REJECTED_OTHER_REASON,
509 commands.get(0).getResult());
510 assertEquals(JGitText.get().transactionAborted,
511 commands.get(0).getMessage());
512 }
513
514 @Test
515 public void testBatchRefUpdate_ForceUpdate() throws IOException {
516 update("refs/heads/master", A);
517 update("refs/heads/masters", B);
518 ObjectId txnId = getTxnCommitted();
519
520 List<ReceiveCommand> commands = Arrays.asList(
521 command(A, B, "refs/heads/master"),
522 command(B, A, "refs/heads/masters"));
523 BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
524 batchUpdate.setAllowNonFastForwards(true);
525 batchUpdate.addCommand(commands);
526 batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
527 assertNotEquals(txnId, getTxnCommitted());
528
529 Map<String, Ref> refs = refdb.getRefs(ALL);
530 assertEquals(OK, commands.get(0).getResult());
531 assertEquals(OK, commands.get(1).getResult());
532 assertEquals(
533 "[refs/heads/master, refs/heads/masters]",
534 refs.keySet().toString());
535 assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
536 assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId());
537 }
538
539 @Test
540 public void testBatchRefUpdate_NonFastForwardDoesNotDoExpensiveMergeCheck()
541 throws IOException {
542 update("refs/heads/master", B);
543 ObjectId txnId = getTxnCommitted();
544
545 List<ReceiveCommand> commands = Arrays.asList(
546 command(B, A, "refs/heads/master"));
547 BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
548 batchUpdate.setAllowNonFastForwards(true);
549 batchUpdate.addCommand(commands);
550 batchUpdate.execute(new RevWalk(repo) {
551 @Override
552 public boolean isMergedInto(RevCommit base, RevCommit tip) {
553 fail("isMergedInto() should not be called");
554 return false;
555 }
556 }, NullProgressMonitor.INSTANCE);
557 assertNotEquals(txnId, getTxnCommitted());
558
559 Map<String, Ref> refs = refdb.getRefs(ALL);
560 assertEquals(OK, commands.get(0).getResult());
561 assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId());
562 }
563
564 @Test
565 public void testBatchRefUpdate_ConflictCausesAbort() throws IOException {
566 update("refs/heads/master", A);
567 update("refs/heads/masters", B);
568 ObjectId txnId = getTxnCommitted();
569
570 List<ReceiveCommand> commands = Arrays.asList(
571 command(A, B, "refs/heads/master"),
572 command(null, A, "refs/heads/master/x"),
573 command(null, A, "refs/heads"));
574 BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
575 batchUpdate.setAllowNonFastForwards(true);
576 batchUpdate.addCommand(commands);
577 batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
578 assertEquals(txnId, getTxnCommitted());
579
580 assertEquals(LOCK_FAILURE, commands.get(0).getResult());
581
582 assertEquals(REJECTED_OTHER_REASON, commands.get(1).getResult());
583 assertEquals(JGitText.get().transactionAborted,
584 commands.get(1).getMessage());
585
586 assertEquals(REJECTED_OTHER_REASON, commands.get(2).getResult());
587 assertEquals(JGitText.get().transactionAborted,
588 commands.get(2).getMessage());
589 }
590
591 @Test
592 public void testBatchRefUpdate_NoConflictIfDeleted() throws IOException {
593 update("refs/heads/master", A);
594 update("refs/heads/masters", B);
595 ObjectId txnId = getTxnCommitted();
596
597 List<ReceiveCommand> commands = Arrays.asList(
598 command(A, B, "refs/heads/master"),
599 command(null, A, "refs/heads/masters/x"),
600 command(B, null, "refs/heads/masters"));
601 BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
602 batchUpdate.setAllowNonFastForwards(true);
603 batchUpdate.addCommand(commands);
604 batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
605 assertNotEquals(txnId, getTxnCommitted());
606
607 assertEquals(OK, commands.get(0).getResult());
608 assertEquals(OK, commands.get(1).getResult());
609 assertEquals(OK, commands.get(2).getResult());
610
611 Map<String, Ref> refs = refdb.getRefs(ALL);
612 assertEquals(
613 "[refs/heads/master, refs/heads/masters/x]",
614 refs.keySet().toString());
615 assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId());
616 }
617
618 private ObjectId getTxnCommitted() throws IOException {
619 Ref r = bootstrap.exactRef(refdb.getTxnCommitted());
620 if (r != null && r.getObjectId() != null) {
621 return r.getObjectId();
622 }
623 return ObjectId.zeroId();
624 }
625
626 private static ReceiveCommand command(AnyObjectId a, AnyObjectId b,
627 String name) {
628 return new ReceiveCommand(
629 a != null ? a.copy() : ObjectId.zeroId(),
630 b != null ? b.copy() : ObjectId.zeroId(),
631 name);
632 }
633
634 private void symref(String name, String dst)
635 throws IOException {
636 commit(new Function() {
637 @Override
638 public boolean apply(ObjectReader reader, RefTree tree)
639 throws IOException {
640 Ref old = tree.exactRef(reader, name);
641 Command n = new Command(
642 old,
643 new SymbolicRef(
644 name,
645 new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null)));
646 return tree.apply(Collections.singleton(n));
647 }
648 });
649 }
650
651 private void update(String name, ObjectId id)
652 throws IOException {
653 commit(new Function() {
654 @Override
655 public boolean apply(ObjectReader reader, RefTree tree)
656 throws IOException {
657 Ref old = tree.exactRef(reader, name);
658 Command n;
659 try (RevWalk rw = new RevWalk(repo)) {
660 n = new Command(old,
661 Command.toRef(rw, id, null, name, true));
662 }
663 return tree.apply(Collections.singleton(n));
664 }
665 });
666 }
667
668 interface Function {
669 boolean apply(ObjectReader reader, RefTree tree) throws IOException;
670 }
671
672 private void commit(Function fun) throws IOException {
673 try (ObjectReader reader = repo.newObjectReader();
674 ObjectInserter inserter = repo.newObjectInserter();
675 RevWalk rw = new RevWalk(reader)) {
676 RefUpdate u = bootstrap.newUpdate(refdb.getTxnCommitted(), false);
677 CommitBuilder cb = new CommitBuilder();
678 testRepo.setAuthorAndCommitter(cb);
679
680 Ref ref = bootstrap.exactRef(refdb.getTxnCommitted());
681 RefTree tree;
682 if (ref != null && ref.getObjectId() != null) {
683 tree = RefTree.read(reader, rw.parseTree(ref.getObjectId()));
684 cb.setParentId(ref.getObjectId());
685 u.setExpectedOldObjectId(ref.getObjectId());
686 } else {
687 tree = RefTree.newEmptyTree();
688 u.setExpectedOldObjectId(ObjectId.zeroId());
689 }
690
691 assertTrue(fun.apply(reader, tree));
692 cb.setTreeId(tree.writeTree(inserter));
693 u.setNewObjectId(inserter.insert(cb));
694 inserter.flush();
695 switch (u.update(rw)) {
696 case NEW:
697 case FAST_FORWARD:
698 break;
699 default:
700 fail("Expected " + u.getName() + " to update");
701 }
702 }
703 }
704
705 private class InMemRefTreeRepo extends InMemoryRepository {
706 private final RefTreeDatabase refs;
707
708 InMemRefTreeRepo(DfsRepositoryDescription repoDesc) {
709 super(repoDesc);
710 refs = new RefTreeDatabase(this, super.getRefDatabase(),
711 "refs/txn/committed");
712 RefTreeDatabaseTest.this.refdb = refs;
713 }
714
715 @Override
716 public RefDatabase getRefDatabase() {
717 return refs;
718 }
719 }
720 }