1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.reftable;
12
13 import static org.eclipse.jgit.lib.Constants.HEAD;
14 import static org.eclipse.jgit.lib.Constants.MASTER;
15 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
16 import static org.eclipse.jgit.lib.Constants.R_HEADS;
17 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
18 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.eclipse.jgit.internal.storage.io.BlockSource;
35 import org.eclipse.jgit.lib.ObjectId;
36 import org.eclipse.jgit.lib.ObjectIdRef;
37 import org.eclipse.jgit.lib.Ref;
38 import org.eclipse.jgit.lib.RefComparator;
39 import org.eclipse.jgit.lib.SymbolicRef;
40 import org.junit.Test;
41
42 public class MergedReftableTest {
43 @Test
44 public void noTables() throws IOException {
45 MergedReftable mr = merge(new byte[0][]);
46 try (RefCursor rc = mr.allRefs()) {
47 assertFalse(rc.next());
48 }
49 try (RefCursor rc = mr.seekRef(HEAD)) {
50 assertFalse(rc.next());
51 }
52 try (RefCursor rc = mr.seekRefsWithPrefix(R_HEADS)) {
53 assertFalse(rc.next());
54 }
55 }
56
57 @Test
58 public void oneEmptyTable() throws IOException {
59 MergedReftable mr = merge(write());
60 try (RefCursor rc = mr.allRefs()) {
61 assertFalse(rc.next());
62 }
63 try (RefCursor rc = mr.seekRef(HEAD)) {
64 assertFalse(rc.next());
65 }
66 try (RefCursor rc = mr.seekRefsWithPrefix(R_HEADS)) {
67 assertFalse(rc.next());
68 }
69 }
70
71 @Test
72 public void twoEmptyTables() throws IOException {
73 MergedReftable mr = merge(write(), write());
74 try (RefCursor rc = mr.allRefs()) {
75 assertFalse(rc.next());
76 }
77 try (RefCursor rc = mr.seekRef(HEAD)) {
78 assertFalse(rc.next());
79 }
80 try (RefCursor rc = mr.seekRefsWithPrefix(R_HEADS)) {
81 assertFalse(rc.next());
82 }
83 }
84
85 @SuppressWarnings("boxing")
86 @Test
87 public void oneTableScan() throws IOException {
88 List<Ref> refs = new ArrayList<>();
89 for (int i = 1; i <= 567; i++) {
90 refs.add(ref(String.format("refs/heads/%03d", i), i));
91 }
92
93 MergedReftable mr = merge(write(refs));
94 try (RefCursor rc = mr.allRefs()) {
95 for (Ref exp : refs) {
96 assertTrue("has " + exp.getName(), rc.next());
97 Ref act = rc.getRef();
98 assertEquals(exp.getName(), act.getName());
99 assertEquals(exp.getObjectId(), act.getObjectId());
100 assertEquals(1, act.getUpdateIndex());
101 }
102 assertFalse(rc.next());
103 }
104 }
105
106 @Test
107 public void deleteIsHidden() throws IOException {
108 List<Ref> delta1 = Arrays.asList(
109 ref("refs/heads/apple", 1),
110 ref("refs/heads/master", 2));
111 List<Ref> delta2 = Arrays.asList(delete("refs/heads/apple"));
112
113 MergedReftable mr = merge(write(delta1), write(delta2));
114 try (RefCursor rc = mr.allRefs()) {
115 assertTrue(rc.next());
116 assertEquals("refs/heads/master", rc.getRef().getName());
117 assertEquals(id(2), rc.getRef().getObjectId());
118 assertEquals(1, rc.getRef().getUpdateIndex());
119 assertFalse(rc.next());
120 }
121 }
122
123 @Test
124 public void twoTableSeek() throws IOException {
125 List<Ref> delta1 = Arrays.asList(
126 ref("refs/heads/apple", 1),
127 ref("refs/heads/master", 2));
128 List<Ref> delta2 = Arrays.asList(ref("refs/heads/banana", 3));
129
130 MergedReftable mr = merge(write(delta1), write(delta2));
131 try (RefCursor rc = mr.seekRef("refs/heads/master")) {
132 assertTrue(rc.next());
133 assertEquals("refs/heads/master", rc.getRef().getName());
134 assertEquals(id(2), rc.getRef().getObjectId());
135 assertFalse(rc.next());
136 assertEquals(1, rc.getRef().getUpdateIndex());
137 }
138 }
139
140 @Test
141 public void twoTableSeekPastWithRefCursor() throws IOException {
142 List<Ref> delta1 = Arrays.asList(
143 ref("refs/heads/apple", 1),
144 ref("refs/heads/master", 2));
145 List<Ref> delta2 = Arrays.asList(
146 ref("refs/heads/banana", 3),
147 ref("refs/heads/zzlast", 4));
148
149 MergedReftable mr = merge(write(delta1), write(delta2));
150 try (RefCursor rc = mr.seekRefsWithPrefix("")) {
151 assertTrue(rc.next());
152 assertEquals("refs/heads/apple", rc.getRef().getName());
153 assertEquals(id(1), rc.getRef().getObjectId());
154
155 rc.seekPastPrefix("refs/heads/banana/");
156
157 assertTrue(rc.next());
158 assertEquals("refs/heads/master", rc.getRef().getName());
159 assertEquals(id(2), rc.getRef().getObjectId());
160
161 assertTrue(rc.next());
162 assertEquals("refs/heads/zzlast", rc.getRef().getName());
163 assertEquals(id(4), rc.getRef().getObjectId());
164
165 assertEquals(1, rc.getRef().getUpdateIndex());
166 }
167 }
168
169 @Test
170 public void oneTableSeekPastWithRefCursor() throws IOException {
171 List<Ref> delta1 = Arrays.asList(
172 ref("refs/heads/apple", 1),
173 ref("refs/heads/master", 2));
174
175 MergedReftable mr = merge(write(delta1));
176 try (RefCursor rc = mr.seekRefsWithPrefix("")) {
177 rc.seekPastPrefix("refs/heads/apple");
178
179 assertTrue(rc.next());
180 assertEquals("refs/heads/master", rc.getRef().getName());
181 assertEquals(id(2), rc.getRef().getObjectId());
182
183 assertEquals(1, rc.getRef().getUpdateIndex());
184 }
185 }
186
187 @Test
188 public void seekPastToNonExistentPrefixToTheMiddle() throws IOException {
189 List<Ref> delta1 = Arrays.asList(
190 ref("refs/heads/apple", 1),
191 ref("refs/heads/master", 2));
192 List<Ref> delta2 = Arrays.asList(
193 ref("refs/heads/banana", 3),
194 ref("refs/heads/zzlast", 4));
195
196 MergedReftable mr = merge(write(delta1), write(delta2));
197 try (RefCursor rc = mr.seekRefsWithPrefix("")) {
198 rc.seekPastPrefix("refs/heads/x");
199
200 assertTrue(rc.next());
201 assertEquals("refs/heads/zzlast", rc.getRef().getName());
202 assertEquals(id(4), rc.getRef().getObjectId());
203
204 assertEquals(1, rc.getRef().getUpdateIndex());
205 }
206 }
207
208 @Test
209 public void seekPastToNonExistentPrefixToTheEnd() throws IOException {
210 List<Ref> delta1 = Arrays.asList(
211 ref("refs/heads/apple", 1),
212 ref("refs/heads/master", 2));
213 List<Ref> delta2 = Arrays.asList(
214 ref("refs/heads/banana", 3),
215 ref("refs/heads/zzlast", 4));
216
217 MergedReftable mr = merge(write(delta1), write(delta2));
218 try (RefCursor rc = mr.seekRefsWithPrefix("")) {
219 rc.seekPastPrefix("refs/heads/zzz");
220 assertFalse(rc.next());
221 }
222 }
223
224 @Test
225 public void seekPastManyTimes() throws IOException {
226 List<Ref> delta1 = Arrays.asList(
227 ref("refs/heads/apple", 1),
228 ref("refs/heads/master", 2));
229 List<Ref> delta2 = Arrays.asList(
230 ref("refs/heads/banana", 3),
231 ref("refs/heads/zzlast", 4));
232
233 MergedReftable mr = merge(write(delta1), write(delta2));
234 try (RefCursor rc = mr.seekRefsWithPrefix("")) {
235 rc.seekPastPrefix("refs/heads/apple");
236 rc.seekPastPrefix("refs/heads/banana");
237 rc.seekPastPrefix("refs/heads/master");
238 rc.seekPastPrefix("refs/heads/zzlast");
239 assertFalse(rc.next());
240 }
241 }
242
243 @Test
244 public void seekPastOnEmptyTable() throws IOException {
245 MergedReftable mr = merge(write(), write());
246 try (RefCursor rc = mr.seekRefsWithPrefix("")) {
247 rc.seekPastPrefix("refs/");
248 assertFalse(rc.next());
249 }
250 }
251
252 @Test
253 public void twoTableById() throws IOException {
254 List<Ref> delta1 = Arrays.asList(
255 ref("refs/heads/apple", 1),
256 ref("refs/heads/master", 2));
257 List<Ref> delta2 = Arrays.asList(ref("refs/heads/banana", 3));
258
259 MergedReftable mr = merge(write(delta1), write(delta2));
260 try (RefCursor rc = mr.byObjectId(id(2))) {
261 assertTrue(rc.next());
262 assertEquals("refs/heads/master", rc.getRef().getName());
263 assertEquals(id(2), rc.getRef().getObjectId());
264 assertEquals(1, rc.getRef().getUpdateIndex());
265 assertFalse(rc.next());
266 }
267 }
268
269 @Test
270 public void tableByIDDeletion() throws IOException {
271 List<Ref> delta1 = Arrays.asList(
272 ref("refs/heads/apple", 1),
273 ref("refs/heads/master", 2));
274 List<Ref> delta2 = Arrays.asList(ref("refs/heads/master", 3));
275
276 MergedReftable mr = merge(write(delta1), write(delta2));
277 try (RefCursor rc = mr.byObjectId(id(2))) {
278 assertFalse(rc.next());
279 }
280 }
281
282 @SuppressWarnings("boxing")
283 @Test
284 public void fourTableScan() throws IOException {
285 List<Ref> base = new ArrayList<>();
286 for (int i = 1; i <= 567; i++) {
287 base.add(ref(String.format("refs/heads/%03d", i), i));
288 }
289
290 List<Ref> delta1 = Arrays.asList(
291 ref("refs/heads/next", 4),
292 ref(String.format("refs/heads/%03d", 55), 4096));
293 List<Ref> delta2 = Arrays.asList(
294 delete("refs/heads/next"),
295 ref(String.format("refs/heads/%03d", 55), 8192));
296 List<Ref> delta3 = Arrays.asList(
297 ref("refs/heads/master", 4242),
298 ref(String.format("refs/heads/%03d", 42), 5120),
299 ref(String.format("refs/heads/%03d", 98), 6120));
300
301 List<Ref> expected = merge(base, delta1, delta2, delta3);
302 MergedReftable mr = merge(
303 write(base),
304 write(delta1),
305 write(delta2),
306 write(delta3));
307 try (RefCursor rc = mr.allRefs()) {
308 for (Ref exp : expected) {
309 assertTrue("has " + exp.getName(), rc.next());
310 Ref act = rc.getRef();
311 assertEquals(exp.getName(), act.getName());
312 assertEquals(exp.getObjectId(), act.getObjectId());
313 assertEquals(1, rc.getRef().getUpdateIndex());
314 }
315 assertFalse(rc.next());
316 }
317 }
318
319 @Test
320 public void scanIncludeDeletes() throws IOException {
321 List<Ref> delta1 = Arrays.asList(ref("refs/heads/next", 4));
322 List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
323 List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
324
325 MergedReftable mr = merge(write(delta1), write(delta2), write(delta3));
326 mr.setIncludeDeletes(true);
327 try (RefCursor rc = mr.allRefs()) {
328 assertTrue(rc.next());
329 Ref r = rc.getRef();
330 assertEquals("refs/heads/master", r.getName());
331 assertEquals(id(8), r.getObjectId());
332 assertEquals(1, rc.getRef().getUpdateIndex());
333
334 assertTrue(rc.next());
335 r = rc.getRef();
336 assertEquals("refs/heads/next", r.getName());
337 assertEquals(NEW, r.getStorage());
338 assertNull(r.getObjectId());
339 assertEquals(1, rc.getRef().getUpdateIndex());
340
341 assertFalse(rc.next());
342 }
343 }
344
345 @SuppressWarnings("boxing")
346 @Test
347 public void oneTableSeek() throws IOException {
348 List<Ref> refs = new ArrayList<>();
349 for (int i = 1; i <= 567; i++) {
350 refs.add(ref(String.format("refs/heads/%03d", i), i));
351 }
352
353 MergedReftable mr = merge(write(refs));
354 for (Ref exp : refs) {
355 try (RefCursor rc = mr.seekRef(exp.getName())) {
356 assertTrue("has " + exp.getName(), rc.next());
357 Ref act = rc.getRef();
358 assertEquals(exp.getName(), act.getName());
359 assertEquals(exp.getObjectId(), act.getObjectId());
360 assertEquals(1, act.getUpdateIndex());
361 assertFalse(rc.next());
362 }
363 }
364 }
365
366 @Test
367 public void missedUpdate() throws IOException {
368 ByteArrayOutputStream buf = new ByteArrayOutputStream();
369 ReftableWriter writer = new ReftableWriter(buf)
370 .setMinUpdateIndex(1)
371 .setMaxUpdateIndex(3)
372 .begin();
373 writer.writeRef(ref("refs/heads/a", 1), 1);
374 writer.writeRef(ref("refs/heads/c", 3), 3);
375 writer.finish();
376 byte[] base = buf.toByteArray();
377
378 byte[] delta = write(Arrays.asList(
379 ref("refs/heads/b", 2),
380 ref("refs/heads/c", 4)),
381 2);
382 MergedReftable mr = merge(base, delta);
383 try (RefCursor rc = mr.allRefs()) {
384 assertTrue(rc.next());
385 assertEquals("refs/heads/a", rc.getRef().getName());
386 assertEquals(id(1), rc.getRef().getObjectId());
387 assertEquals(1, rc.getRef().getUpdateIndex());
388
389 assertTrue(rc.next());
390 assertEquals("refs/heads/b", rc.getRef().getName());
391 assertEquals(id(2), rc.getRef().getObjectId());
392 assertEquals(2, rc.getRef().getUpdateIndex());
393
394 assertTrue(rc.next());
395 assertEquals("refs/heads/c", rc.getRef().getName());
396 assertEquals(id(3), rc.getRef().getObjectId());
397 assertEquals(3, rc.getRef().getUpdateIndex());
398 }
399 }
400
401 @Test
402 public void nonOverlappedUpdateIndices() throws IOException {
403 ByteArrayOutputStream buf = new ByteArrayOutputStream();
404 ReftableWriter writer = new ReftableWriter(buf)
405 .setMinUpdateIndex(1)
406 .setMaxUpdateIndex(2)
407 .begin();
408 writer.writeRef(ref("refs/heads/a", 1), 1);
409 writer.writeRef(ref("refs/heads/b", 2), 2);
410 writer.finish();
411 byte[] base = buf.toByteArray();
412
413 buf = new ByteArrayOutputStream();
414 writer = new ReftableWriter(buf)
415 .setMinUpdateIndex(3)
416 .setMaxUpdateIndex(4)
417 .begin();
418 writer.writeRef(ref("refs/heads/a", 10), 3);
419 writer.writeRef(ref("refs/heads/b", 20), 4);
420 writer.finish();
421 byte[] delta = buf.toByteArray();
422
423 MergedReftable mr = merge(base, delta);
424 assertEquals(1, mr.minUpdateIndex());
425 assertEquals(4, mr.maxUpdateIndex());
426
427 try (RefCursor rc = mr.allRefs()) {
428 assertTrue(rc.next());
429 assertEquals("refs/heads/a", rc.getRef().getName());
430 assertEquals(id(10), rc.getRef().getObjectId());
431 assertEquals(3, rc.getRef().getUpdateIndex());
432
433 assertTrue(rc.next());
434 assertEquals("refs/heads/b", rc.getRef().getName());
435 assertEquals(id(20), rc.getRef().getObjectId());
436 assertEquals(4, rc.getRef().getUpdateIndex());
437 }
438 }
439
440 @Test
441 public void overlappedUpdateIndices() throws IOException {
442 ByteArrayOutputStream buf = new ByteArrayOutputStream();
443 ReftableWriter writer = new ReftableWriter(buf)
444 .setMinUpdateIndex(2)
445 .setMaxUpdateIndex(4)
446 .begin();
447 writer.writeRef(ref("refs/heads/a", 10), 2);
448 writer.writeRef(ref("refs/heads/b", 20), 4);
449 writer.finish();
450 byte[] base = buf.toByteArray();
451
452 buf = new ByteArrayOutputStream();
453 writer = new ReftableWriter(buf)
454 .setMinUpdateIndex(1)
455 .setMaxUpdateIndex(3)
456 .begin();
457 writer.writeRef(ref("refs/heads/a", 1), 1);
458 writer.writeRef(ref("refs/heads/b", 2), 3);
459 writer.finish();
460 byte[] delta = buf.toByteArray();
461
462 MergedReftable mr = merge(base, delta);
463 assertEquals(1, mr.minUpdateIndex());
464 assertEquals(4, mr.maxUpdateIndex());
465
466 try (RefCursor rc = mr.allRefs()) {
467 assertTrue(rc.next());
468 assertEquals("refs/heads/a", rc.getRef().getName());
469 assertEquals(id(10), rc.getRef().getObjectId());
470 assertEquals(2, rc.getRef().getUpdateIndex());
471
472 assertTrue(rc.next());
473 assertEquals("refs/heads/b", rc.getRef().getName());
474 assertEquals(id(20), rc.getRef().getObjectId());
475 assertEquals(4, rc.getRef().getUpdateIndex());
476 }
477 }
478
479 @Test
480 public void enclosedUpdateIndices() throws IOException {
481 ByteArrayOutputStream buf = new ByteArrayOutputStream();
482 ReftableWriter writer = new ReftableWriter(buf)
483 .setMinUpdateIndex(2)
484 .setMaxUpdateIndex(3)
485 .begin();
486 writer.writeRef(ref("refs/heads/a", 10), 2);
487 writer.writeRef(ref("refs/heads/b", 2), 3);
488 writer.finish();
489 byte[] base = buf.toByteArray();
490
491 buf = new ByteArrayOutputStream();
492 writer = new ReftableWriter(buf)
493 .setMinUpdateIndex(1)
494 .setMaxUpdateIndex(4)
495 .begin();
496 writer.writeRef(ref("refs/heads/a", 1), 1);
497 writer.writeRef(ref("refs/heads/b", 20), 4);
498 writer.finish();
499 byte[] delta = buf.toByteArray();
500
501 MergedReftable mr = merge(base, delta);
502 assertEquals(1, mr.minUpdateIndex());
503 assertEquals(4, mr.maxUpdateIndex());
504
505 try (RefCursor rc = mr.allRefs()) {
506 assertTrue(rc.next());
507 assertEquals("refs/heads/a", rc.getRef().getName());
508 assertEquals(id(10), rc.getRef().getObjectId());
509 assertEquals(2, rc.getRef().getUpdateIndex());
510
511 assertTrue(rc.next());
512 assertEquals("refs/heads/b", rc.getRef().getName());
513 assertEquals(id(20), rc.getRef().getObjectId());
514 assertEquals(4, rc.getRef().getUpdateIndex());
515 }
516 }
517
518 @Test
519 public void compaction() throws IOException {
520 List<Ref> delta1 = Arrays.asList(
521 ref("refs/heads/next", 4),
522 ref("refs/heads/master", 1));
523 List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
524 List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
525
526 ByteArrayOutputStream out = new ByteArrayOutputStream();
527 ReftableCompactor compactor = new ReftableCompactor(out);
528 compactor.addAll(Arrays.asList(
529 read(write(delta1)),
530 read(write(delta2)),
531 read(write(delta3))));
532 compactor.compact();
533 byte[] table = out.toByteArray();
534
535 ReftableReader reader = read(table);
536 try (RefCursor rc = reader.allRefs()) {
537 assertTrue(rc.next());
538 Ref r = rc.getRef();
539 assertEquals("refs/heads/master", r.getName());
540 assertEquals(id(8), r.getObjectId());
541 assertFalse(rc.next());
542 }
543 }
544
545 @Test
546 public void versioningSymbolicReftargetMoves() throws IOException {
547 Ref master = ref(MASTER, 100);
548
549 List<Ref> delta1 = Arrays.asList(master, sym(HEAD, MASTER));
550 List<Ref> delta2 = Arrays.asList(ref(MASTER, 200));
551
552 MergedReftable mr = merge(write(delta1, 1), write(delta2, 2));
553 Ref head = mr.exactRef(HEAD);
554 assertEquals(head.getUpdateIndex(), 1);
555
556 Ref masterRef = mr.exactRef(MASTER);
557 assertEquals(masterRef.getUpdateIndex(), 2);
558 }
559
560 @Test
561 public void versioningSymbolicRefMoves() throws IOException {
562 Ref branchX = ref("refs/heads/branchX", 200);
563
564 List<Ref> delta1 = Arrays.asList(ref(MASTER, 100), branchX,
565 sym(HEAD, MASTER));
566 List<Ref> delta2 = Arrays.asList(sym(HEAD, "refs/heads/branchX"));
567 List<Ref> delta3 = Arrays.asList(sym(HEAD, MASTER));
568
569 MergedReftable mr = merge(write(delta1, 1), write(delta2, 2),
570 write(delta3, 3));
571 Ref head = mr.exactRef(HEAD);
572 assertEquals(head.getUpdateIndex(), 3);
573
574 Ref masterRef = mr.exactRef(MASTER);
575 assertEquals(masterRef.getUpdateIndex(), 1);
576
577 Ref branchRef = mr.exactRef(MASTER);
578 assertEquals(branchRef.getUpdateIndex(), 1);
579 }
580
581 @Test
582 public void versioningResolveRef() throws IOException {
583 List<Ref> delta1 = Arrays.asList(sym(HEAD, "refs/heads/tmp"),
584 sym("refs/heads/tmp", MASTER), ref(MASTER, 100));
585 List<Ref> delta2 = Arrays.asList(ref(MASTER, 200));
586 List<Ref> delta3 = Arrays.asList(ref(MASTER, 300));
587
588 MergedReftable mr = merge(write(delta1, 1), write(delta2, 2),
589 write(delta3, 3));
590 Ref head = mr.exactRef(HEAD);
591 Ref resolvedHead = mr.resolve(head);
592 assertEquals(resolvedHead.getObjectId(), id(300));
593 assertEquals("HEAD has not moved", resolvedHead.getUpdateIndex(), 1);
594
595 Ref master = mr.exactRef(MASTER);
596 Ref resolvedMaster = mr.resolve(master);
597 assertEquals(resolvedMaster.getObjectId(), id(300));
598 assertEquals("master also has update index",
599 resolvedMaster.getUpdateIndex(), 3);
600 }
601
602 private static MergedReftable merge(byte[]... table) {
603 List<ReftableReader> stack = new ArrayList<>(table.length);
604 for (byte[] b : table) {
605 stack.add(read(b));
606 }
607 return new MergedReftable(stack);
608 }
609
610 private static ReftableReader read(byte[] table) {
611 return new ReftableReader(BlockSource.from(table));
612 }
613
614 private static Ref ref(String name, int id) {
615 return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
616 }
617
618 private static Ref sym(String name, String target) {
619 return new SymbolicRef(name, newRef(target));
620 }
621
622 private static Ref newRef(String name) {
623 return new ObjectIdRef.Unpeeled(NEW, name, null);
624 }
625
626 private static Ref delete(String name) {
627 return new ObjectIdRef.Unpeeled(NEW, name, null);
628 }
629
630 private static ObjectId id(int i) {
631 byte[] buf = new byte[OBJECT_ID_LENGTH];
632 buf[0] = (byte) (i & 0xff);
633 buf[1] = (byte) ((i >>> 8) & 0xff);
634 buf[2] = (byte) ((i >>> 16) & 0xff);
635 buf[3] = (byte) (i >>> 24);
636 return ObjectId.fromRaw(buf);
637 }
638
639 private byte[] write(Ref... refs) throws IOException {
640 return write(Arrays.asList(refs));
641 }
642
643 private byte[] write(Collection<Ref> refs) throws IOException {
644 return write(refs, 1);
645 }
646
647 private byte[] write(Collection<Ref> refs, long updateIndex)
648 throws IOException {
649 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
650 new ReftableWriter(buffer)
651 .setMinUpdateIndex(updateIndex)
652 .setMaxUpdateIndex(updateIndex)
653 .begin()
654 .sortAndWriteRefs(refs)
655 .finish();
656 return buffer.toByteArray();
657 }
658
659 @SafeVarargs
660 private static List<Ref> merge(List<Ref>... tables) {
661 Map<String, Ref> expect = new HashMap<>();
662 for (List<Ref> t : tables) {
663 for (Ref r : t) {
664 if (r.getStorage() == NEW && r.getObjectId() == null) {
665 expect.remove(r.getName());
666 } else {
667 expect.put(r.getName(), r);
668 }
669 }
670 }
671
672 List<Ref> expected = new ArrayList<>(expect.values());
673 Collections.sort(expected, RefComparator.INSTANCE);
674 return expected;
675 }
676 }