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.reftable;
45
46 import static org.eclipse.jgit.lib.Constants.HEAD;
47 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
48 import static org.eclipse.jgit.lib.Constants.R_HEADS;
49 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
50 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
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.assertNull;
55 import static org.junit.Assert.assertSame;
56 import static org.junit.Assert.assertTrue;
57 import static org.junit.Assert.fail;
58
59 import java.io.ByteArrayOutputStream;
60 import java.io.IOException;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collection;
64 import java.util.Collections;
65 import java.util.List;
66
67 import org.eclipse.jgit.internal.JGitText;
68 import org.eclipse.jgit.internal.storage.io.BlockSource;
69 import org.eclipse.jgit.internal.storage.reftable.ReftableWriter.Stats;
70 import org.eclipse.jgit.lib.ObjectId;
71 import org.eclipse.jgit.lib.ObjectIdRef;
72 import org.eclipse.jgit.lib.PersonIdent;
73 import org.eclipse.jgit.lib.Ref;
74 import org.eclipse.jgit.lib.ReflogEntry;
75 import org.eclipse.jgit.lib.SymbolicRef;
76 import org.junit.Test;
77
78 public class ReftableTest {
79 private static final String MASTER = "refs/heads/master";
80 private static final String NEXT = "refs/heads/next";
81 private static final String V1_0 = "refs/tags/v1.0";
82
83 private Stats stats;
84
85 @Test
86 public void emptyTable() throws IOException {
87 byte[] table = write();
88 assertEquals(92 , table.length);
89 assertEquals('R', table[0]);
90 assertEquals('E', table[1]);
91 assertEquals('F', table[2]);
92 assertEquals('T', table[3]);
93 assertEquals(0x01, table[4]);
94 assertTrue(ReftableConstants.isFileHeaderMagic(table, 0, 8));
95 assertTrue(ReftableConstants.isFileHeaderMagic(table, 24, 92));
96
97 Reftable t = read(table);
98 try (RefCursor rc = t.allRefs()) {
99 assertFalse(rc.next());
100 }
101 try (RefCursor rc = t.seekRef(HEAD)) {
102 assertFalse(rc.next());
103 }
104 try (RefCursor rc = t.seekRefsWithPrefix(R_HEADS)) {
105 assertFalse(rc.next());
106 }
107 try (LogCursor rc = t.allLogs()) {
108 assertFalse(rc.next());
109 }
110 }
111
112 @Test
113 public void emptyVirtualTableFromRefs() throws IOException {
114 Reftable t = Reftable.from(Collections.emptyList());
115 try (RefCursor rc = t.allRefs()) {
116 assertFalse(rc.next());
117 }
118 try (RefCursor rc = t.seekRef(HEAD)) {
119 assertFalse(rc.next());
120 }
121 try (LogCursor rc = t.allLogs()) {
122 assertFalse(rc.next());
123 }
124 }
125
126 @Test
127 public void estimateCurrentBytesOneRef() throws IOException {
128 Ref exp = ref(MASTER, 1);
129 int expBytes = 24 + 4 + 5 + 4 + MASTER.length() + 20 + 68;
130
131 byte[] table;
132 ReftableConfig cfg = new ReftableConfig();
133 cfg.setIndexObjects(false);
134 ReftableWriter writer = new ReftableWriter().setConfig(cfg);
135 try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
136 writer.begin(buf);
137 assertEquals(92, writer.estimateTotalBytes());
138 writer.writeRef(exp);
139 assertEquals(expBytes, writer.estimateTotalBytes());
140 writer.finish();
141 table = buf.toByteArray();
142 }
143 assertEquals(expBytes, table.length);
144 }
145
146 @SuppressWarnings("boxing")
147 @Test
148 public void estimateCurrentBytesWithIndex() throws IOException {
149 List<Ref> refs = new ArrayList<>();
150 for (int i = 1; i <= 5670; i++) {
151 refs.add(ref(String.format("refs/heads/%04d", i), i));
152 }
153
154 ReftableConfig cfg = new ReftableConfig();
155 cfg.setIndexObjects(false);
156 cfg.setMaxIndexLevels(1);
157
158 int expBytes = 147860;
159 byte[] table;
160 ReftableWriter writer = new ReftableWriter().setConfig(cfg);
161 try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
162 writer.begin(buf);
163 writer.sortAndWriteRefs(refs);
164 assertEquals(expBytes, writer.estimateTotalBytes());
165 writer.finish();
166 stats = writer.getStats();
167 table = buf.toByteArray();
168 }
169 assertEquals(1, stats.refIndexLevels());
170 assertEquals(expBytes, table.length);
171 }
172
173 @Test
174 public void oneIdRef() throws IOException {
175 Ref exp = ref(MASTER, 1);
176 byte[] table = write(exp);
177 assertEquals(24 + 4 + 5 + 4 + MASTER.length() + 20 + 68, table.length);
178
179 ReftableReader t = read(table);
180 try (RefCursor rc = t.allRefs()) {
181 assertTrue(rc.next());
182 Ref act = rc.getRef();
183 assertNotNull(act);
184 assertEquals(PACKED, act.getStorage());
185 assertTrue(act.isPeeled());
186 assertFalse(act.isSymbolic());
187 assertEquals(exp.getName(), act.getName());
188 assertEquals(exp.getObjectId(), act.getObjectId());
189 assertNull(act.getPeeledObjectId());
190 assertFalse(rc.wasDeleted());
191 assertFalse(rc.next());
192 }
193 try (RefCursor rc = t.seekRef(MASTER)) {
194 assertTrue(rc.next());
195 Ref act = rc.getRef();
196 assertNotNull(act);
197 assertEquals(exp.getName(), act.getName());
198 assertFalse(rc.next());
199 }
200 }
201
202 @Test
203 public void oneTagRef() throws IOException {
204 Ref exp = tag(V1_0, 1, 2);
205 byte[] table = write(exp);
206 assertEquals(24 + 4 + 5 + 3 + V1_0.length() + 40 + 68, table.length);
207
208 ReftableReader t = read(table);
209 try (RefCursor rc = t.allRefs()) {
210 assertTrue(rc.next());
211 Ref act = rc.getRef();
212 assertNotNull(act);
213 assertEquals(PACKED, act.getStorage());
214 assertTrue(act.isPeeled());
215 assertFalse(act.isSymbolic());
216 assertEquals(exp.getName(), act.getName());
217 assertEquals(exp.getObjectId(), act.getObjectId());
218 assertEquals(exp.getPeeledObjectId(), act.getPeeledObjectId());
219 }
220 }
221
222 @Test
223 public void oneSymbolicRef() throws IOException {
224 Ref exp = sym(HEAD, MASTER);
225 byte[] table = write(exp);
226 assertEquals(
227 24 + 4 + 5 + 2 + HEAD.length() + 2 + MASTER.length() + 68,
228 table.length);
229
230 ReftableReader t = read(table);
231 try (RefCursor rc = t.allRefs()) {
232 assertTrue(rc.next());
233 Ref act = rc.getRef();
234 assertNotNull(act);
235 assertTrue(act.isSymbolic());
236 assertEquals(exp.getName(), act.getName());
237 assertNotNull(act.getLeaf());
238 assertEquals(MASTER, act.getTarget().getName());
239 assertNull(act.getObjectId());
240 }
241 }
242
243 @Test
244 public void resolveSymbolicRef() throws IOException {
245 Reftable t = read(write(
246 sym(HEAD, "refs/heads/tmp"),
247 sym("refs/heads/tmp", MASTER),
248 ref(MASTER, 1)));
249
250 Ref head = t.exactRef(HEAD);
251 assertNull(head.getObjectId());
252 assertEquals("refs/heads/tmp", head.getTarget().getName());
253
254 head = t.resolve(head);
255 assertNotNull(head);
256 assertEquals(id(1), head.getObjectId());
257
258 Ref master = t.exactRef(MASTER);
259 assertNotNull(master);
260 assertSame(master, t.resolve(master));
261 }
262
263 @Test
264 public void failDeepChainOfSymbolicRef() throws IOException {
265 Reftable t = read(write(
266 sym(HEAD, "refs/heads/1"),
267 sym("refs/heads/1", "refs/heads/2"),
268 sym("refs/heads/2", "refs/heads/3"),
269 sym("refs/heads/3", "refs/heads/4"),
270 sym("refs/heads/4", "refs/heads/5"),
271 sym("refs/heads/5", MASTER),
272 ref(MASTER, 1)));
273
274 Ref head = t.exactRef(HEAD);
275 assertNull(head.getObjectId());
276 assertNull(t.resolve(head));
277 }
278
279 @Test
280 public void oneDeletedRef() throws IOException {
281 String name = "refs/heads/gone";
282 Ref exp = newRef(name);
283 byte[] table = write(exp);
284 assertEquals(24 + 4 + 5 + 3 + name.length() + 68, table.length);
285
286 ReftableReader t = read(table);
287 try (RefCursor rc = t.allRefs()) {
288 assertFalse(rc.next());
289 }
290
291 t.setIncludeDeletes(true);
292 try (RefCursor rc = t.allRefs()) {
293 assertTrue(rc.next());
294 Ref act = rc.getRef();
295 assertNotNull(act);
296 assertFalse(act.isSymbolic());
297 assertEquals(name, act.getName());
298 assertEquals(NEW, act.getStorage());
299 assertNull(act.getObjectId());
300 assertTrue(rc.wasDeleted());
301 }
302 }
303
304 @Test
305 public void seekNotFound() throws IOException {
306 Ref exp = ref(MASTER, 1);
307 ReftableReader t = read(write(exp));
308 try (RefCursor rc = t.seekRef("refs/heads/a")) {
309 assertFalse(rc.next());
310 }
311 try (RefCursor rc = t.seekRef("refs/heads/n")) {
312 assertFalse(rc.next());
313 }
314 }
315
316 @Test
317 public void namespaceNotFound() throws IOException {
318 Ref exp = ref(MASTER, 1);
319 ReftableReader t = read(write(exp));
320 try (RefCursor rc = t.seekRefsWithPrefix("refs/changes/")) {
321 assertFalse(rc.next());
322 }
323 try (RefCursor rc = t.seekRefsWithPrefix("refs/tags/")) {
324 assertFalse(rc.next());
325 }
326 }
327
328 @Test
329 public void namespaceHeads() throws IOException {
330 Ref master = ref(MASTER, 1);
331 Ref next = ref(NEXT, 2);
332 Ref v1 = tag(V1_0, 3, 4);
333
334 ReftableReader t = read(write(master, next, v1));
335 try (RefCursor rc = t.seekRefsWithPrefix("refs/tags/")) {
336 assertTrue(rc.next());
337 assertEquals(V1_0, rc.getRef().getName());
338 assertFalse(rc.next());
339 }
340 try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
341 assertTrue(rc.next());
342 assertEquals(MASTER, rc.getRef().getName());
343
344 assertTrue(rc.next());
345 assertEquals(NEXT, rc.getRef().getName());
346
347 assertFalse(rc.next());
348 }
349 }
350
351 @SuppressWarnings("boxing")
352 @Test
353 public void indexScan() throws IOException {
354 List<Ref> refs = new ArrayList<>();
355 for (int i = 1; i <= 5670; i++) {
356 refs.add(ref(String.format("refs/heads/%04d", i), i));
357 }
358
359 byte[] table = write(refs);
360 assertTrue(stats.refIndexLevels() > 0);
361 assertTrue(stats.refIndexSize() > 0);
362 assertScan(refs, read(table));
363 }
364
365 @SuppressWarnings("boxing")
366 @Test
367 public void indexSeek() throws IOException {
368 List<Ref> refs = new ArrayList<>();
369 for (int i = 1; i <= 5670; i++) {
370 refs.add(ref(String.format("refs/heads/%04d", i), i));
371 }
372
373 byte[] table = write(refs);
374 assertTrue(stats.refIndexLevels() > 0);
375 assertTrue(stats.refIndexSize() > 0);
376 assertSeek(refs, read(table));
377 }
378
379 @SuppressWarnings("boxing")
380 @Test
381 public void noIndexScan() throws IOException {
382 List<Ref> refs = new ArrayList<>();
383 for (int i = 1; i <= 567; i++) {
384 refs.add(ref(String.format("refs/heads/%03d", i), i));
385 }
386
387 byte[] table = write(refs);
388 assertEquals(0, stats.refIndexLevels());
389 assertEquals(0, stats.refIndexSize());
390 assertEquals(table.length, stats.totalBytes());
391 assertScan(refs, read(table));
392 }
393
394 @SuppressWarnings("boxing")
395 @Test
396 public void noIndexSeek() throws IOException {
397 List<Ref> refs = new ArrayList<>();
398 for (int i = 1; i <= 567; i++) {
399 refs.add(ref(String.format("refs/heads/%03d", i), i));
400 }
401
402 byte[] table = write(refs);
403 assertEquals(0, stats.refIndexLevels());
404 assertSeek(refs, read(table));
405 }
406
407 @Test
408 public void withReflog() throws IOException {
409 Ref master = ref(MASTER, 1);
410 Ref next = ref(NEXT, 2);
411 PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
412 String msg = "test";
413
414 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
415 ReftableWriter writer = new ReftableWriter()
416 .setMinUpdateIndex(1)
417 .setMaxUpdateIndex(1)
418 .begin(buffer);
419
420 writer.writeRef(master);
421 writer.writeRef(next);
422
423 writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
424 writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msg);
425
426 writer.finish();
427 byte[] table = buffer.toByteArray();
428 assertEquals(247, table.length);
429
430 ReftableReader t = read(table);
431 try (RefCursor rc = t.allRefs()) {
432 assertTrue(rc.next());
433 assertEquals(MASTER, rc.getRef().getName());
434 assertEquals(id(1), rc.getRef().getObjectId());
435 assertEquals(1, rc.getUpdateIndex());
436
437 assertTrue(rc.next());
438 assertEquals(NEXT, rc.getRef().getName());
439 assertEquals(id(2), rc.getRef().getObjectId());
440 assertFalse(rc.next());
441 }
442 try (LogCursor lc = t.allLogs()) {
443 assertTrue(lc.next());
444 assertEquals(MASTER, lc.getRefName());
445 assertEquals(1, lc.getUpdateIndex());
446 assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
447 assertEquals(id(1), lc.getReflogEntry().getNewId());
448 assertEquals(who, lc.getReflogEntry().getWho());
449 assertEquals(msg, lc.getReflogEntry().getComment());
450
451 assertTrue(lc.next());
452 assertEquals(NEXT, lc.getRefName());
453 assertEquals(1, lc.getUpdateIndex());
454 assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
455 assertEquals(id(2), lc.getReflogEntry().getNewId());
456 assertEquals(who, lc.getReflogEntry().getWho());
457 assertEquals(msg, lc.getReflogEntry().getComment());
458
459 assertFalse(lc.next());
460 }
461 }
462
463 @Test
464 public void onlyReflog() throws IOException {
465 PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
466 String msg = "test";
467
468 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
469 ReftableWriter writer = new ReftableWriter()
470 .setMinUpdateIndex(1)
471 .setMaxUpdateIndex(1)
472 .begin(buffer);
473 writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
474 writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msg);
475 writer.finish();
476 byte[] table = buffer.toByteArray();
477 stats = writer.getStats();
478 assertEquals(170, table.length);
479 assertEquals(0, stats.refCount());
480 assertEquals(0, stats.refBytes());
481 assertEquals(0, stats.refIndexLevels());
482
483 ReftableReader t = read(table);
484 try (RefCursor rc = t.allRefs()) {
485 assertFalse(rc.next());
486 }
487 try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
488 assertFalse(rc.next());
489 }
490 try (LogCursor lc = t.allLogs()) {
491 assertTrue(lc.next());
492 assertEquals(MASTER, lc.getRefName());
493 assertEquals(1, lc.getUpdateIndex());
494 assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
495 assertEquals(id(1), lc.getReflogEntry().getNewId());
496 assertEquals(who, lc.getReflogEntry().getWho());
497 assertEquals(msg, lc.getReflogEntry().getComment());
498
499 assertTrue(lc.next());
500 assertEquals(NEXT, lc.getRefName());
501 assertEquals(1, lc.getUpdateIndex());
502 assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
503 assertEquals(id(2), lc.getReflogEntry().getNewId());
504 assertEquals(who, lc.getReflogEntry().getWho());
505 assertEquals(msg, lc.getReflogEntry().getComment());
506
507 assertFalse(lc.next());
508 }
509 }
510
511 @SuppressWarnings("boxing")
512 @Test
513 public void logScan() throws IOException {
514 ReftableConfig cfg = new ReftableConfig();
515 cfg.setRefBlockSize(256);
516 cfg.setLogBlockSize(2048);
517
518 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
519 ReftableWriter writer = new ReftableWriter(cfg);
520 writer.setMinUpdateIndex(1).setMaxUpdateIndex(1).begin(buffer);
521
522 List<Ref> refs = new ArrayList<>();
523 for (int i = 1; i <= 5670; i++) {
524 Ref ref = ref(String.format("refs/heads/%03d", i), i);
525 refs.add(ref);
526 writer.writeRef(ref);
527 }
528
529 PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
530 for (Ref ref : refs) {
531 writer.writeLog(ref.getName(), 1, who,
532 ObjectId.zeroId(), ref.getObjectId(),
533 "create " + ref.getName());
534 }
535 writer.finish();
536 stats = writer.getStats();
537 assertTrue(stats.logBytes() > 4096);
538 byte[] table = buffer.toByteArray();
539
540 ReftableReader t = read(table);
541 try (LogCursor lc = t.allLogs()) {
542 for (Ref exp : refs) {
543 assertTrue("has " + exp.getName(), lc.next());
544 assertEquals(exp.getName(), lc.getRefName());
545 ReflogEntry entry = lc.getReflogEntry();
546 assertNotNull(entry);
547 assertEquals(who, entry.getWho());
548 assertEquals(ObjectId.zeroId(), entry.getOldId());
549 assertEquals(exp.getObjectId(), entry.getNewId());
550 assertEquals("create " + exp.getName(), entry.getComment());
551 }
552 assertFalse(lc.next());
553 }
554 }
555
556 @SuppressWarnings("boxing")
557 @Test
558 public void byObjectIdOneRefNoIndex() throws IOException {
559 List<Ref> refs = new ArrayList<>();
560 for (int i = 1; i <= 200; i++) {
561 refs.add(ref(String.format("refs/heads/%02d", i), i));
562 }
563 refs.add(ref("refs/heads/master", 100));
564
565 ReftableReader t = read(write(refs));
566 assertEquals(0, stats.objIndexSize());
567
568 try (RefCursor rc = t.byObjectId(id(42))) {
569 assertTrue("has 42", rc.next());
570 assertEquals("refs/heads/42", rc.getRef().getName());
571 assertEquals(id(42), rc.getRef().getObjectId());
572 assertFalse(rc.next());
573 }
574 try (RefCursor rc = t.byObjectId(id(100))) {
575 assertTrue("has 100", rc.next());
576 assertEquals("refs/heads/100", rc.getRef().getName());
577 assertEquals(id(100), rc.getRef().getObjectId());
578
579 assertTrue("has master", rc.next());
580 assertEquals("refs/heads/master", rc.getRef().getName());
581 assertEquals(id(100), rc.getRef().getObjectId());
582
583 assertFalse(rc.next());
584 }
585 }
586
587 @SuppressWarnings("boxing")
588 @Test
589 public void byObjectIdOneRefWithIndex() throws IOException {
590 List<Ref> refs = new ArrayList<>();
591 for (int i = 1; i <= 5200; i++) {
592 refs.add(ref(String.format("refs/heads/%02d", i), i));
593 }
594 refs.add(ref("refs/heads/master", 100));
595
596 ReftableReader t = read(write(refs));
597 assertTrue(stats.objIndexSize() > 0);
598
599 try (RefCursor rc = t.byObjectId(id(42))) {
600 assertTrue("has 42", rc.next());
601 assertEquals("refs/heads/42", rc.getRef().getName());
602 assertEquals(id(42), rc.getRef().getObjectId());
603 assertFalse(rc.next());
604 }
605 try (RefCursor rc = t.byObjectId(id(100))) {
606 assertTrue("has 100", rc.next());
607 assertEquals("refs/heads/100", rc.getRef().getName());
608 assertEquals(id(100), rc.getRef().getObjectId());
609
610 assertTrue("has master", rc.next());
611 assertEquals("refs/heads/master", rc.getRef().getName());
612 assertEquals(id(100), rc.getRef().getObjectId());
613
614 assertFalse(rc.next());
615 }
616 }
617
618 @Test
619 public void unpeeledDoesNotWrite() {
620 try {
621 write(new ObjectIdRef.Unpeeled(PACKED, MASTER, id(1)));
622 fail("expected IOException");
623 } catch (IOException e) {
624 assertEquals(JGitText.get().peeledRefIsRequired, e.getMessage());
625 }
626 }
627
628 @Test
629 public void nameTooLongDoesNotWrite() throws IOException {
630 try {
631 ReftableConfig cfg = new ReftableConfig();
632 cfg.setRefBlockSize(64);
633
634 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
635 ReftableWriter writer = new ReftableWriter(cfg).begin(buffer);
636 writer.writeRef(ref("refs/heads/i-am-not-a-teapot", 1));
637 writer.finish();
638 fail("expected BlockSizeTooSmallException");
639 } catch (BlockSizeTooSmallException e) {
640 assertEquals(85, e.getMinimumBlockSize());
641 }
642 }
643
644 @Test
645 public void badCrc32() throws IOException {
646 byte[] table = write();
647 table[table.length - 1] = 0x42;
648
649 try {
650 read(table).seekRef(HEAD);
651 fail("expected IOException");
652 } catch (IOException e) {
653 assertEquals(JGitText.get().invalidReftableCRC, e.getMessage());
654 }
655 }
656
657
658 private static void assertScan(List<Ref> refs, Reftable t)
659 throws IOException {
660 try (RefCursor rc = t.allRefs()) {
661 for (Ref exp : refs) {
662 assertTrue("has " + exp.getName(), rc.next());
663 Ref act = rc.getRef();
664 assertEquals(exp.getName(), act.getName());
665 assertEquals(exp.getObjectId(), act.getObjectId());
666 }
667 assertFalse(rc.next());
668 }
669 }
670
671 private static void assertSeek(List<Ref> refs, Reftable t)
672 throws IOException {
673 for (Ref exp : refs) {
674 try (RefCursor rc = t.seekRef(exp.getName())) {
675 assertTrue("has " + exp.getName(), rc.next());
676 Ref act = rc.getRef();
677 assertEquals(exp.getName(), act.getName());
678 assertEquals(exp.getObjectId(), act.getObjectId());
679 assertFalse(rc.next());
680 }
681 }
682 }
683
684 private static Ref ref(String name, int id) {
685 return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
686 }
687
688 private static Ref tag(String name, int id1, int id2) {
689 return new ObjectIdRef.PeeledTag(PACKED, name, id(id1), id(id2));
690 }
691
692 private static Ref sym(String name, String target) {
693 return new SymbolicRef(name, newRef(target));
694 }
695
696 private static Ref newRef(String name) {
697 return new ObjectIdRef.Unpeeled(NEW, name, null);
698 }
699
700 private static ObjectId id(int i) {
701 byte[] buf = new byte[OBJECT_ID_LENGTH];
702 buf[0] = (byte) (i & 0xff);
703 buf[1] = (byte) ((i >>> 8) & 0xff);
704 buf[2] = (byte) ((i >>> 16) & 0xff);
705 buf[3] = (byte) (i >>> 24);
706 return ObjectId.fromRaw(buf);
707 }
708
709 private static ReftableReader read(byte[] table) {
710 return new ReftableReader(BlockSource.from(table));
711 }
712
713 private byte[] write(Ref... refs) throws IOException {
714 return write(Arrays.asList(refs));
715 }
716
717 private byte[] write(Collection<Ref> refs) throws IOException {
718 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
719 stats = new ReftableWriter()
720 .begin(buffer)
721 .sortAndWriteRefs(refs)
722 .finish()
723 .getStats();
724 return buffer.toByteArray();
725 }
726 }