You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MergedReftableTest.java 20KB

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