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