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.

BatchRefUpdateTest.java 36KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114
  1. /*
  2. * Copyright (C) 2017 Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.internal.storage.file;
  44. import static java.nio.charset.StandardCharsets.UTF_8;
  45. import static java.util.concurrent.TimeUnit.NANOSECONDS;
  46. import static java.util.concurrent.TimeUnit.SECONDS;
  47. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.LOCK_FAILURE;
  48. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.OK;
  49. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.REJECTED_MISSING_OBJECT;
  50. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.REJECTED_NONFASTFORWARD;
  51. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.TRANSACTION_ABORTED;
  52. import static org.eclipse.jgit.lib.ObjectId.zeroId;
  53. import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE;
  54. import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
  55. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
  56. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
  57. import static org.junit.Assert.assertEquals;
  58. import static org.junit.Assert.assertFalse;
  59. import static org.junit.Assert.assertNotNull;
  60. import static org.junit.Assert.assertNull;
  61. import static org.junit.Assert.assertTrue;
  62. import static org.junit.Assume.assumeFalse;
  63. import static org.junit.Assume.assumeTrue;
  64. import java.io.File;
  65. import java.io.IOException;
  66. import java.nio.file.Files;
  67. import java.util.Arrays;
  68. import java.util.Collection;
  69. import java.util.Collections;
  70. import java.util.LinkedHashMap;
  71. import java.util.List;
  72. import java.util.Map;
  73. import java.util.concurrent.locks.ReentrantLock;
  74. import java.util.function.Predicate;
  75. import org.eclipse.jgit.events.ListenerHandle;
  76. import org.eclipse.jgit.events.RefsChangedListener;
  77. import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
  78. import org.eclipse.jgit.junit.StrictWorkMonitor;
  79. import org.eclipse.jgit.junit.TestRepository;
  80. import org.eclipse.jgit.lib.AnyObjectId;
  81. import org.eclipse.jgit.lib.BatchRefUpdate;
  82. import org.eclipse.jgit.lib.CheckoutEntry;
  83. import org.eclipse.jgit.lib.ConfigConstants;
  84. import org.eclipse.jgit.lib.Constants;
  85. import org.eclipse.jgit.lib.NullProgressMonitor;
  86. import org.eclipse.jgit.lib.ObjectId;
  87. import org.eclipse.jgit.lib.PersonIdent;
  88. import org.eclipse.jgit.lib.Ref;
  89. import org.eclipse.jgit.lib.RefDatabase;
  90. import org.eclipse.jgit.lib.RefUpdate;
  91. import org.eclipse.jgit.lib.ReflogEntry;
  92. import org.eclipse.jgit.lib.ReflogReader;
  93. import org.eclipse.jgit.lib.Repository;
  94. import org.eclipse.jgit.lib.StoredConfig;
  95. import org.eclipse.jgit.revwalk.RevCommit;
  96. import org.eclipse.jgit.revwalk.RevWalk;
  97. import org.eclipse.jgit.transport.ReceiveCommand;
  98. import org.junit.After;
  99. import org.junit.Before;
  100. import org.junit.Test;
  101. import org.junit.runner.RunWith;
  102. import org.junit.runners.Parameterized;
  103. import org.junit.runners.Parameterized.Parameter;
  104. import org.junit.runners.Parameterized.Parameters;
  105. @SuppressWarnings("boxing")
  106. @RunWith(Parameterized.class)
  107. public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
  108. @Parameter(0)
  109. public boolean atomic;
  110. @Parameter(1)
  111. public boolean useReftable;
  112. @Parameters(name = "atomic={0} reftable={1}")
  113. public static Collection<Object[]> data() {
  114. return Arrays.asList(new Object[][] { { Boolean.FALSE, Boolean.FALSE },
  115. { Boolean.TRUE, Boolean.FALSE },
  116. { Boolean.FALSE, Boolean.TRUE },
  117. { Boolean.TRUE, Boolean.TRUE }, });
  118. }
  119. private Repository diskRepo;
  120. private TestRepository<Repository> repo;
  121. private RefDirectory refdir;
  122. private RevCommit A;
  123. private RevCommit B; // B descends from A.
  124. /**
  125. * When asserting the number of RefsChangedEvents you must account for one
  126. * additional event due to the initial ref setup via a number of calls to
  127. * {@link #writeLooseRef(String, AnyObjectId)} (will be fired in execute()
  128. * when it is detected that the on-disk loose refs have changed), or for one
  129. * additional event per {@link #writeRef(String, AnyObjectId)}.
  130. */
  131. private int refsChangedEvents;
  132. private ListenerHandle handle;
  133. private RefsChangedListener refsChangedListener = event -> {
  134. refsChangedEvents++;
  135. };
  136. @Override
  137. @Before
  138. public void setUp() throws Exception {
  139. super.setUp();
  140. FileRepository fileRepo = createBareRepository();
  141. if (useReftable) {
  142. fileRepo.convertToReftable(false, false);
  143. }
  144. diskRepo = fileRepo;
  145. setLogAllRefUpdates(true);
  146. if (!useReftable) {
  147. refdir = (RefDirectory) diskRepo.getRefDatabase();
  148. refdir.setRetrySleepMs(Arrays.asList(0, 0));
  149. }
  150. repo = new TestRepository<>(diskRepo);
  151. A = repo.commit().create();
  152. B = repo.commit(repo.getRevWalk().parseCommit(A));
  153. refsChangedEvents = 0;
  154. handle = diskRepo.getListenerList()
  155. .addRefsChangedListener(refsChangedListener);
  156. }
  157. @After
  158. public void removeListener() {
  159. handle.remove();
  160. refsChangedEvents = 0;
  161. }
  162. @Test
  163. public void packedRefsFileIsSorted() throws IOException {
  164. assumeTrue(atomic);
  165. assumeFalse(useReftable);
  166. for (int i = 0; i < 2; i++) {
  167. BatchRefUpdate bu = diskRepo.getRefDatabase().newBatchUpdate();
  168. String b1 = String.format("refs/heads/a%d", i);
  169. String b2 = String.format("refs/heads/b%d", i);
  170. bu.setAtomic(atomic);
  171. ReceiveCommand c1 = new ReceiveCommand(ObjectId.zeroId(), A, b1);
  172. ReceiveCommand c2 = new ReceiveCommand(ObjectId.zeroId(), B, b2);
  173. bu.addCommand(c1, c2);
  174. try (RevWalk rw = new RevWalk(diskRepo)) {
  175. bu.execute(rw, NullProgressMonitor.INSTANCE);
  176. }
  177. assertEquals(c1.getResult(), ReceiveCommand.Result.OK);
  178. assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
  179. }
  180. File packed = new File(diskRepo.getDirectory(), "packed-refs");
  181. String packedStr = new String(Files.readAllBytes(packed.toPath()),
  182. UTF_8);
  183. int a2 = packedStr.indexOf("refs/heads/a1");
  184. int b1 = packedStr.indexOf("refs/heads/b0");
  185. assertTrue(a2 < b1);
  186. }
  187. @Test
  188. public void simpleNoForce() throws IOException {
  189. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  190. List<ReceiveCommand> cmds = Arrays.asList(
  191. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  192. new ReceiveCommand(B, A, "refs/heads/masters",
  193. UPDATE_NONFASTFORWARD));
  194. execute(newBatchUpdate(cmds));
  195. if (atomic) {
  196. assertResults(cmds, TRANSACTION_ABORTED, REJECTED_NONFASTFORWARD);
  197. assertRefs("refs/heads/master", A, "refs/heads/masters", B);
  198. assertEquals(1, refsChangedEvents);
  199. } else {
  200. assertResults(cmds, OK, REJECTED_NONFASTFORWARD);
  201. assertRefs("refs/heads/master", B, "refs/heads/masters", B);
  202. assertEquals(2, refsChangedEvents);
  203. }
  204. }
  205. @Test
  206. public void simpleForce() throws IOException {
  207. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  208. List<ReceiveCommand> cmds = Arrays.asList(
  209. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  210. new ReceiveCommand(B, A, "refs/heads/masters",
  211. UPDATE_NONFASTFORWARD));
  212. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  213. assertResults(cmds, OK, OK);
  214. assertRefs("refs/heads/master", B, "refs/heads/masters", A);
  215. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  216. }
  217. @Test
  218. public void nonFastForwardDoesNotDoExpensiveMergeCheck()
  219. throws IOException {
  220. writeLooseRef("refs/heads/master", B);
  221. List<ReceiveCommand> cmds = Arrays.asList(new ReceiveCommand(B, A,
  222. "refs/heads/master", UPDATE_NONFASTFORWARD));
  223. try (RevWalk rw = new RevWalk(diskRepo) {
  224. @Override
  225. public boolean isMergedInto(RevCommit base, RevCommit tip) {
  226. throw new AssertionError("isMergedInto() should not be called");
  227. }
  228. }) {
  229. newBatchUpdate(cmds).setAllowNonFastForwards(true).execute(rw,
  230. new StrictWorkMonitor());
  231. }
  232. assertResults(cmds, OK);
  233. assertRefs("refs/heads/master", A);
  234. assertEquals(2, refsChangedEvents);
  235. }
  236. @Test
  237. public void fileDirectoryConflict() throws IOException {
  238. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  239. List<ReceiveCommand> cmds = Arrays.asList(
  240. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  241. new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE),
  242. new ReceiveCommand(zeroId(), A, "refs/heads", CREATE));
  243. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  244. if (atomic) {
  245. // Atomic update sees that master and master/x are conflicting, then
  246. // marks the first one in the list as LOCK_FAILURE and aborts the rest.
  247. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED,
  248. TRANSACTION_ABORTED);
  249. assertRefs("refs/heads/master", A, "refs/heads/masters", B);
  250. assertEquals(1, refsChangedEvents);
  251. } else {
  252. // Non-atomic updates are applied in order: master succeeds, then
  253. // master/x fails due to conflict.
  254. assertResults(cmds, OK, LOCK_FAILURE, LOCK_FAILURE);
  255. assertRefs("refs/heads/master", B, "refs/heads/masters", B);
  256. assertEquals(2, refsChangedEvents);
  257. }
  258. }
  259. @Test
  260. public void conflictThanksToDelete() throws IOException {
  261. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  262. List<ReceiveCommand> cmds = Arrays.asList(
  263. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  264. new ReceiveCommand(zeroId(), A, "refs/heads/masters/x", CREATE),
  265. new ReceiveCommand(B, zeroId(), "refs/heads/masters", DELETE));
  266. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  267. assertResults(cmds, OK, OK, OK);
  268. assertRefs("refs/heads/master", B, "refs/heads/masters/x", A);
  269. if (atomic) {
  270. assertEquals(2, refsChangedEvents);
  271. } else if (!useReftable) {
  272. // The non-atomic case actually produces 5 events, but that's an
  273. // implementation detail. We expect at least 4 events, one for the
  274. // initial read due to writeLooseRef(), and then one for each
  275. // successful ref update.
  276. assertTrue(refsChangedEvents >= 4);
  277. }
  278. }
  279. @Test
  280. public void updateToMissingObject() throws IOException {
  281. writeLooseRef("refs/heads/master", A);
  282. ObjectId bad = ObjectId
  283. .fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  284. List<ReceiveCommand> cmds = Arrays.asList(
  285. new ReceiveCommand(A, bad, "refs/heads/master", UPDATE),
  286. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  287. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  288. if (atomic) {
  289. assertResults(cmds, REJECTED_MISSING_OBJECT, TRANSACTION_ABORTED);
  290. assertRefs("refs/heads/master", A);
  291. assertEquals(1, refsChangedEvents);
  292. } else {
  293. assertResults(cmds, REJECTED_MISSING_OBJECT, OK);
  294. assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
  295. assertEquals(2, refsChangedEvents);
  296. }
  297. }
  298. @Test
  299. public void addMissingObject() throws IOException {
  300. writeLooseRef("refs/heads/master", A);
  301. ObjectId bad = ObjectId
  302. .fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  303. List<ReceiveCommand> cmds = Arrays.asList(
  304. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  305. new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", CREATE));
  306. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  307. if (atomic) {
  308. assertResults(cmds, TRANSACTION_ABORTED, REJECTED_MISSING_OBJECT);
  309. assertRefs("refs/heads/master", A);
  310. assertEquals(1, refsChangedEvents);
  311. } else {
  312. assertResults(cmds, OK, REJECTED_MISSING_OBJECT);
  313. assertRefs("refs/heads/master", B);
  314. assertEquals(2, refsChangedEvents);
  315. }
  316. }
  317. @Test
  318. public void oneNonExistentRef() throws IOException {
  319. List<ReceiveCommand> cmds = Arrays.asList(
  320. new ReceiveCommand(A, B, "refs/heads/foo1", UPDATE),
  321. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  322. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  323. if (atomic) {
  324. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
  325. assertRefs();
  326. assertEquals(0, refsChangedEvents);
  327. } else {
  328. assertResults(cmds, LOCK_FAILURE, OK);
  329. assertRefs("refs/heads/foo2", B);
  330. assertEquals(1, refsChangedEvents);
  331. }
  332. }
  333. @Test
  334. public void oneRefWrongOldValue() throws IOException {
  335. writeLooseRef("refs/heads/master", A);
  336. List<ReceiveCommand> cmds = Arrays.asList(
  337. new ReceiveCommand(B, B, "refs/heads/master", UPDATE),
  338. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  339. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  340. if (atomic) {
  341. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
  342. assertRefs("refs/heads/master", A);
  343. assertEquals(1, refsChangedEvents);
  344. } else {
  345. assertResults(cmds, LOCK_FAILURE, OK);
  346. assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
  347. assertEquals(2, refsChangedEvents);
  348. }
  349. }
  350. @Test
  351. public void nonExistentRef() throws IOException {
  352. writeLooseRef("refs/heads/master", A);
  353. List<ReceiveCommand> cmds = Arrays.asList(
  354. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  355. new ReceiveCommand(A, zeroId(), "refs/heads/foo2", DELETE));
  356. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  357. if (atomic) {
  358. assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
  359. assertRefs("refs/heads/master", A);
  360. assertEquals(1, refsChangedEvents);
  361. } else {
  362. assertResults(cmds, OK, LOCK_FAILURE);
  363. assertRefs("refs/heads/master", B);
  364. assertEquals(2, refsChangedEvents);
  365. }
  366. }
  367. @Test
  368. public void noRefLog() throws IOException {
  369. writeRef("refs/heads/master", A);
  370. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  371. "refs/heads/branch");
  372. assertEquals(Collections.singleton("refs/heads/master"),
  373. oldLogs.keySet());
  374. List<ReceiveCommand> cmds = Arrays.asList(
  375. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  376. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  377. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  378. assertResults(cmds, OK, OK);
  379. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  380. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  381. assertReflogUnchanged(oldLogs, "refs/heads/master");
  382. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  383. }
  384. @Test
  385. public void reflogDefaultIdent() throws IOException {
  386. writeRef("refs/heads/master", A);
  387. writeRef("refs/heads/branch2", A);
  388. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  389. "refs/heads/branch1", "refs/heads/branch2");
  390. List<ReceiveCommand> cmds = Arrays.asList(
  391. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  392. new ReceiveCommand(zeroId(), B, "refs/heads/branch1", CREATE));
  393. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)
  394. .setRefLogMessage("a reflog", false));
  395. assertResults(cmds, OK, OK);
  396. assertRefs("refs/heads/master", B, "refs/heads/branch1", B,
  397. "refs/heads/branch2", A);
  398. assertEquals(batchesRefUpdates() ? 3 : 4, refsChangedEvents);
  399. assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  400. getLastReflog("refs/heads/master"));
  401. assertReflogEquals(
  402. reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
  403. getLastReflog("refs/heads/branch1"));
  404. assertReflogUnchanged(oldLogs, "refs/heads/branch2");
  405. }
  406. @Test
  407. public void reflogAppendStatusNoMessage() throws IOException {
  408. writeRef("refs/heads/master", A);
  409. writeRef("refs/heads/branch1", B);
  410. List<ReceiveCommand> cmds = Arrays.asList(
  411. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  412. new ReceiveCommand(B, A, "refs/heads/branch1",
  413. UPDATE_NONFASTFORWARD),
  414. new ReceiveCommand(zeroId(), A, "refs/heads/branch2", CREATE));
  415. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)
  416. .setRefLogMessage(null, true));
  417. assertResults(cmds, OK, OK, OK);
  418. assertRefs("refs/heads/master", B, "refs/heads/branch1", A,
  419. "refs/heads/branch2", A);
  420. assertEquals(batchesRefUpdates() ? 3 : 5, refsChangedEvents);
  421. assertReflogEquals(
  422. // Always forced; setAllowNonFastForwards(true) bypasses the
  423. // check.
  424. reflog(A, B, new PersonIdent(diskRepo), "forced-update"),
  425. getLastReflog("refs/heads/master"));
  426. assertReflogEquals(
  427. reflog(B, A, new PersonIdent(diskRepo), "forced-update"),
  428. getLastReflog("refs/heads/branch1"));
  429. assertReflogEquals(
  430. reflog(zeroId(), A, new PersonIdent(diskRepo), "created"),
  431. getLastReflog("refs/heads/branch2"));
  432. }
  433. @Test
  434. public void reflogAppendStatusFastForward() throws IOException {
  435. writeRef("refs/heads/master", A);
  436. List<ReceiveCommand> cmds = Arrays
  437. .asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  438. execute(newBatchUpdate(cmds).setRefLogMessage(null, true));
  439. assertResults(cmds, OK);
  440. assertRefs("refs/heads/master", B);
  441. assertEquals(2, refsChangedEvents);
  442. assertReflogEquals(
  443. reflog(A, B, new PersonIdent(diskRepo), "fast-forward"),
  444. getLastReflog("refs/heads/master"));
  445. }
  446. @Test
  447. public void reflogAppendStatusWithMessage() throws IOException {
  448. writeRef("refs/heads/master", A);
  449. List<ReceiveCommand> cmds = Arrays.asList(
  450. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  451. new ReceiveCommand(zeroId(), A, "refs/heads/branch", CREATE));
  452. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
  453. assertResults(cmds, OK, OK);
  454. assertRefs("refs/heads/master", B, "refs/heads/branch", A);
  455. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  456. assertReflogEquals(
  457. reflog(A, B, new PersonIdent(diskRepo),
  458. "a reflog: fast-forward"),
  459. getLastReflog("refs/heads/master"));
  460. assertReflogEquals(
  461. reflog(zeroId(), A, new PersonIdent(diskRepo),
  462. "a reflog: created"),
  463. getLastReflog("refs/heads/branch"));
  464. }
  465. @Test
  466. public void reflogCustomIdent() throws IOException {
  467. writeRef("refs/heads/master", A);
  468. List<ReceiveCommand> cmds = Arrays.asList(
  469. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  470. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  471. PersonIdent ident = new PersonIdent("A Reflog User",
  472. "reflog@example.com");
  473. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false)
  474. .setRefLogIdent(ident));
  475. assertResults(cmds, OK, OK);
  476. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  477. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  478. assertReflogEquals(reflog(A, B, ident, "a reflog"),
  479. getLastReflog("refs/heads/master"), true);
  480. assertReflogEquals(reflog(zeroId(), B, ident, "a reflog"),
  481. getLastReflog("refs/heads/branch"), true);
  482. }
  483. @Test
  484. public void reflogDelete() throws IOException {
  485. writeRef("refs/heads/master", A);
  486. writeRef("refs/heads/branch", A);
  487. assertEquals(2, getLastReflogs("refs/heads/master", "refs/heads/branch")
  488. .size());
  489. List<ReceiveCommand> cmds = Arrays.asList(
  490. new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE),
  491. new ReceiveCommand(A, B, "refs/heads/branch", UPDATE));
  492. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  493. assertResults(cmds, OK, OK);
  494. assertRefs("refs/heads/branch", B);
  495. assertEquals(batchesRefUpdates() ? 3 : 4, refsChangedEvents);
  496. if (useReftable) {
  497. // reftable retains reflog entries for deleted branches.
  498. assertReflogEquals(
  499. reflog(A, zeroId(), new PersonIdent(diskRepo), "a reflog"),
  500. getLastReflog("refs/heads/master"));
  501. } else {
  502. assertNull(getLastReflog("refs/heads/master"));
  503. }
  504. assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  505. getLastReflog("refs/heads/branch"));
  506. }
  507. @Test
  508. public void reflogFileDirectoryConflict() throws IOException {
  509. writeRef("refs/heads/master", A);
  510. List<ReceiveCommand> cmds = Arrays.asList(
  511. new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE),
  512. new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE));
  513. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  514. assertResults(cmds, OK, OK);
  515. assertRefs("refs/heads/master/x", A);
  516. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  517. if (!useReftable) {
  518. // reftable retains reflog entries for deleted branches.
  519. assertNull(getLastReflog("refs/heads/master"));
  520. }
  521. assertReflogEquals(
  522. reflog(zeroId(), A, new PersonIdent(diskRepo), "a reflog"),
  523. getLastReflog("refs/heads/master/x"));
  524. }
  525. @Test
  526. public void reflogOnLockFailure() throws IOException {
  527. writeRef("refs/heads/master", A);
  528. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  529. "refs/heads/branch");
  530. List<ReceiveCommand> cmds = Arrays.asList(
  531. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  532. new ReceiveCommand(A, B, "refs/heads/branch", UPDATE));
  533. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  534. if (atomic) {
  535. assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
  536. assertEquals(1, refsChangedEvents);
  537. assertReflogUnchanged(oldLogs, "refs/heads/master");
  538. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  539. } else {
  540. assertResults(cmds, OK, LOCK_FAILURE);
  541. assertEquals(2, refsChangedEvents);
  542. assertReflogEquals(
  543. reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  544. getLastReflog("refs/heads/master"));
  545. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  546. }
  547. }
  548. @Test
  549. public void overrideRefLogMessage() throws Exception {
  550. writeRef("refs/heads/master", A);
  551. List<ReceiveCommand> cmds = Arrays.asList(
  552. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  553. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  554. cmds.get(0).setRefLogMessage("custom log", false);
  555. PersonIdent ident = new PersonIdent(diskRepo);
  556. execute(newBatchUpdate(cmds).setRefLogIdent(ident)
  557. .setRefLogMessage("a reflog", true));
  558. assertResults(cmds, OK, OK);
  559. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  560. assertReflogEquals(reflog(A, B, ident, "custom log"),
  561. getLastReflog("refs/heads/master"), true);
  562. assertReflogEquals(reflog(zeroId(), B, ident, "a reflog: created"),
  563. getLastReflog("refs/heads/branch"), true);
  564. }
  565. @Test
  566. public void overrideDisableRefLog() throws Exception {
  567. writeRef("refs/heads/master", A);
  568. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  569. "refs/heads/branch");
  570. List<ReceiveCommand> cmds = Arrays.asList(
  571. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  572. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  573. cmds.get(0).disableRefLog();
  574. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
  575. assertResults(cmds, OK, OK);
  576. assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents);
  577. assertReflogUnchanged(oldLogs, "refs/heads/master");
  578. assertReflogEquals(
  579. reflog(zeroId(), B, new PersonIdent(diskRepo),
  580. "a reflog: created"),
  581. getLastReflog("refs/heads/branch"));
  582. }
  583. @Test
  584. public void refLogNotWrittenWithoutConfigOption() throws Exception {
  585. assumeFalse(useReftable);
  586. setLogAllRefUpdates(false);
  587. writeRef("refs/heads/master", A);
  588. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  589. "refs/heads/branch");
  590. assertTrue(oldLogs.isEmpty());
  591. List<ReceiveCommand> cmds = Arrays.asList(
  592. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  593. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  594. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  595. assertResults(cmds, OK, OK);
  596. assertReflogUnchanged(oldLogs, "refs/heads/master");
  597. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  598. }
  599. @Test
  600. public void forceRefLogInUpdate() throws Exception {
  601. assumeFalse(useReftable);
  602. setLogAllRefUpdates(false);
  603. writeRef("refs/heads/master", A);
  604. assertTrue(getLastReflogs("refs/heads/master", "refs/heads/branch")
  605. .isEmpty());
  606. List<ReceiveCommand> cmds = Arrays.asList(
  607. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  608. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  609. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false)
  610. .setForceRefLog(true));
  611. assertResults(cmds, OK, OK);
  612. assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  613. getLastReflog("refs/heads/master"));
  614. assertReflogEquals(
  615. reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
  616. getLastReflog("refs/heads/branch"));
  617. }
  618. @Test
  619. public void forceRefLogInCommand() throws Exception {
  620. assumeFalse(useReftable);
  621. setLogAllRefUpdates(false);
  622. writeRef("refs/heads/master", A);
  623. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  624. "refs/heads/branch");
  625. assertTrue(oldLogs.isEmpty());
  626. List<ReceiveCommand> cmds = Arrays.asList(
  627. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  628. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  629. cmds.get(1).setForceRefLog(true);
  630. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  631. assertResults(cmds, OK, OK);
  632. assertReflogUnchanged(oldLogs, "refs/heads/master");
  633. assertReflogEquals(
  634. reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
  635. getLastReflog("refs/heads/branch"));
  636. }
  637. @Test
  638. public void packedRefsLockFailure() throws Exception {
  639. assumeFalse(useReftable);
  640. writeLooseRef("refs/heads/master", A);
  641. List<ReceiveCommand> cmds = Arrays.asList(
  642. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  643. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  644. LockFile myLock = refdir.lockPackedRefs();
  645. try {
  646. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  647. assertFalse(getLockFile("refs/heads/master").exists());
  648. assertFalse(getLockFile("refs/heads/branch").exists());
  649. if (atomic) {
  650. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
  651. assertRefs("refs/heads/master", A);
  652. assertEquals(1, refsChangedEvents);
  653. } else {
  654. // Only operates on loose refs, doesn't care that packed-refs is
  655. // locked.
  656. assertResults(cmds, OK, OK);
  657. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  658. assertEquals(3, refsChangedEvents);
  659. }
  660. } finally {
  661. myLock.unlock();
  662. }
  663. }
  664. @Test
  665. public void oneRefLockFailure() throws Exception {
  666. assumeFalse(useReftable);
  667. writeLooseRef("refs/heads/master", A);
  668. List<ReceiveCommand> cmds = Arrays.asList(
  669. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE),
  670. new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  671. LockFile myLock = new LockFile(refdir.fileFor("refs/heads/master"));
  672. assertTrue(myLock.lock());
  673. try {
  674. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  675. assertFalse(LockFile.getLockFile(refdir.packedRefsFile).exists());
  676. assertFalse(getLockFile("refs/heads/branch").exists());
  677. if (atomic) {
  678. assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
  679. assertRefs("refs/heads/master", A);
  680. assertEquals(1, refsChangedEvents);
  681. } else {
  682. assertResults(cmds, OK, LOCK_FAILURE);
  683. assertRefs("refs/heads/branch", B, "refs/heads/master", A);
  684. assertEquals(2, refsChangedEvents);
  685. }
  686. } finally {
  687. myLock.unlock();
  688. }
  689. }
  690. @Test
  691. public void singleRefUpdateDoesNotRequirePackedRefsLock() throws Exception {
  692. assumeFalse(useReftable);
  693. writeLooseRef("refs/heads/master", A);
  694. List<ReceiveCommand> cmds = Arrays
  695. .asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  696. LockFile myLock = refdir.lockPackedRefs();
  697. try {
  698. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  699. assertFalse(getLockFile("refs/heads/master").exists());
  700. assertResults(cmds, OK);
  701. assertEquals(2, refsChangedEvents);
  702. assertRefs("refs/heads/master", B);
  703. } finally {
  704. myLock.unlock();
  705. }
  706. }
  707. @Test
  708. public void atomicUpdateRespectsInProcessLock() throws Exception {
  709. assumeTrue(atomic);
  710. assumeFalse(useReftable);
  711. writeLooseRef("refs/heads/master", A);
  712. List<ReceiveCommand> cmds = Arrays.asList(
  713. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  714. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  715. Thread t = new Thread(() -> {
  716. try {
  717. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  718. } catch (Exception e) {
  719. throw new RuntimeException(e);
  720. }
  721. });
  722. ReentrantLock l = refdir.inProcessPackedRefsLock;
  723. l.lock();
  724. try {
  725. t.start();
  726. long timeoutSecs = 10;
  727. long startNanos = System.nanoTime();
  728. // Hold onto the lock until we observe the worker thread has
  729. // attempted to
  730. // acquire it.
  731. while (l.getQueueLength() == 0) {
  732. long elapsedNanos = System.nanoTime() - startNanos;
  733. assertTrue(
  734. "timed out waiting for work thread to attempt to acquire lock",
  735. NANOSECONDS.toSeconds(elapsedNanos) < timeoutSecs);
  736. Thread.sleep(3);
  737. }
  738. // Once we unlock, the worker thread should finish the update
  739. // promptly.
  740. l.unlock();
  741. t.join(SECONDS.toMillis(timeoutSecs));
  742. assertFalse(t.isAlive());
  743. } finally {
  744. if (l.isHeldByCurrentThread()) {
  745. l.unlock();
  746. }
  747. }
  748. assertResults(cmds, OK, OK);
  749. assertEquals(2, refsChangedEvents);
  750. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  751. }
  752. private void setLogAllRefUpdates(boolean enable) throws Exception {
  753. StoredConfig cfg = diskRepo.getConfig();
  754. cfg.load();
  755. cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  756. ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, enable);
  757. cfg.save();
  758. }
  759. private void writeLooseRef(String name, AnyObjectId id) throws IOException {
  760. if (useReftable) {
  761. writeRef(name, id);
  762. } else {
  763. write(new File(diskRepo.getDirectory(), name), id.name() + "\n");
  764. }
  765. }
  766. private void writeLooseRefs(String name1, AnyObjectId id1, String name2,
  767. AnyObjectId id2) throws IOException {
  768. if (useReftable) {
  769. BatchRefUpdate bru = diskRepo.getRefDatabase().newBatchUpdate();
  770. Ref r1 = diskRepo.exactRef(name1);
  771. ReceiveCommand c1 = new ReceiveCommand(
  772. r1 != null ? r1.getObjectId() : ObjectId.zeroId(),
  773. id1.toObjectId(), name1, r1 == null ? CREATE : UPDATE);
  774. Ref r2 = diskRepo.exactRef(name2);
  775. ReceiveCommand c2 = new ReceiveCommand(
  776. r2 != null ? r2.getObjectId() : ObjectId.zeroId(),
  777. id2.toObjectId(), name2, r2 == null ? CREATE : UPDATE);
  778. bru.addCommand(c1, c2);
  779. try (RevWalk rw = new RevWalk(diskRepo)) {
  780. bru.execute(rw, NullProgressMonitor.INSTANCE);
  781. }
  782. assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
  783. assertEquals(c1.getResult(), ReceiveCommand.Result.OK);
  784. } else {
  785. writeLooseRef(name1, id1);
  786. writeLooseRef(name2, id2);
  787. }
  788. }
  789. private void writeRef(String name, AnyObjectId id) throws IOException {
  790. RefUpdate u = diskRepo.updateRef(name);
  791. u.setRefLogMessage(getClass().getSimpleName(), false);
  792. u.setForceUpdate(true);
  793. u.setNewObjectId(id);
  794. RefUpdate.Result r = u.update();
  795. switch (r) {
  796. case NEW:
  797. case FORCED:
  798. return;
  799. default:
  800. throw new IOException("Got " + r + " while updating " + name);
  801. }
  802. }
  803. private BatchRefUpdate newBatchUpdate(List<ReceiveCommand> cmds) {
  804. BatchRefUpdate u = diskRepo.getRefDatabase().newBatchUpdate();
  805. if (atomic) {
  806. assertTrue(u.isAtomic());
  807. } else {
  808. u.setAtomic(false);
  809. }
  810. u.addCommand(cmds);
  811. return u;
  812. }
  813. private void execute(BatchRefUpdate u) throws IOException {
  814. execute(u, false);
  815. }
  816. private void execute(BatchRefUpdate u, boolean strictWork)
  817. throws IOException {
  818. try (RevWalk rw = new RevWalk(diskRepo)) {
  819. u.execute(rw, strictWork ? new StrictWorkMonitor()
  820. : NullProgressMonitor.INSTANCE);
  821. }
  822. }
  823. private void assertRefs(Object... args) throws IOException {
  824. if (args.length % 2 != 0) {
  825. throw new IllegalArgumentException(
  826. "expected even number of args: " + Arrays.toString(args));
  827. }
  828. Map<String, AnyObjectId> expected = new LinkedHashMap<>();
  829. for (int i = 0; i < args.length; i += 2) {
  830. expected.put((String) args[i], (AnyObjectId) args[i + 1]);
  831. }
  832. Map<String, Ref> refs = diskRepo.getRefDatabase()
  833. .getRefs(RefDatabase.ALL);
  834. Ref actualHead = refs.remove(Constants.HEAD);
  835. if (actualHead != null) {
  836. String actualLeafName = actualHead.getLeaf().getName();
  837. assertEquals(
  838. "expected HEAD to point to refs/heads/master, got: "
  839. + actualLeafName,
  840. "refs/heads/master", actualLeafName);
  841. AnyObjectId expectedMaster = expected.get("refs/heads/master");
  842. assertNotNull("expected master ref since HEAD exists",
  843. expectedMaster);
  844. assertEquals(expectedMaster, actualHead.getObjectId());
  845. }
  846. Map<String, AnyObjectId> actual = new LinkedHashMap<>();
  847. refs.forEach((n, r) -> actual.put(n, r.getObjectId()));
  848. assertEquals(expected.keySet(), actual.keySet());
  849. actual.forEach((n, a) -> assertEquals(n, expected.get(n), a));
  850. }
  851. enum Result {
  852. OK(ReceiveCommand.Result.OK), LOCK_FAILURE(
  853. ReceiveCommand.Result.LOCK_FAILURE), REJECTED_NONFASTFORWARD(
  854. ReceiveCommand.Result.REJECTED_NONFASTFORWARD), REJECTED_MISSING_OBJECT(
  855. ReceiveCommand.Result.REJECTED_MISSING_OBJECT), TRANSACTION_ABORTED(
  856. ReceiveCommand::isTransactionAborted);
  857. @SuppressWarnings("ImmutableEnumChecker")
  858. final Predicate<? super ReceiveCommand> p;
  859. private Result(Predicate<? super ReceiveCommand> p) {
  860. this.p = p;
  861. }
  862. private Result(ReceiveCommand.Result result) {
  863. this(c -> c.getResult() == result);
  864. }
  865. }
  866. private void assertResults(List<ReceiveCommand> cmds, Result... expected) {
  867. if (expected.length != cmds.size()) {
  868. throw new IllegalArgumentException(
  869. "expected " + cmds.size() + " result args");
  870. }
  871. for (int i = 0; i < cmds.size(); i++) {
  872. ReceiveCommand c = cmds.get(i);
  873. Result r = expected[i];
  874. assertTrue(String.format(
  875. "result of command (%d) should be %s, got %s %s%s",
  876. Integer.valueOf(i), r, c, c.getResult(),
  877. c.getMessage() != null ? " (" + c.getMessage() + ")" : ""),
  878. r.p.test(c));
  879. }
  880. }
  881. private Map<String, ReflogEntry> getLastReflogs(String... names)
  882. throws IOException {
  883. Map<String, ReflogEntry> result = new LinkedHashMap<>();
  884. for (String name : names) {
  885. ReflogEntry e = getLastReflog(name);
  886. if (e != null) {
  887. result.put(name, e);
  888. }
  889. }
  890. return result;
  891. }
  892. private ReflogEntry getLastReflog(String name) throws IOException {
  893. ReflogReader r = diskRepo.getReflogReader(name);
  894. if (r == null) {
  895. return null;
  896. }
  897. return r.getLastEntry();
  898. }
  899. private File getLockFile(String refName) {
  900. return LockFile.getLockFile(refdir.fileFor(refName));
  901. }
  902. private void assertReflogUnchanged(Map<String, ReflogEntry> old,
  903. String name) throws IOException {
  904. assertReflogEquals(old.get(name), getLastReflog(name), true);
  905. }
  906. private static void assertReflogEquals(ReflogEntry expected,
  907. ReflogEntry actual) {
  908. assertReflogEquals(expected, actual, false);
  909. }
  910. private static void assertReflogEquals(ReflogEntry expected,
  911. ReflogEntry actual, boolean strictTime) {
  912. if (expected == null) {
  913. assertNull(actual);
  914. return;
  915. }
  916. assertNotNull(actual);
  917. assertEquals(expected.getOldId(), actual.getOldId());
  918. assertEquals(expected.getNewId(), actual.getNewId());
  919. if (strictTime) {
  920. assertEquals(expected.getWho(), actual.getWho());
  921. } else {
  922. assertEquals(expected.getWho().getName(),
  923. actual.getWho().getName());
  924. assertEquals(expected.getWho().getEmailAddress(),
  925. actual.getWho().getEmailAddress());
  926. }
  927. assertEquals(expected.getComment(), actual.getComment());
  928. }
  929. private static ReflogEntry reflog(ObjectId oldId, ObjectId newId,
  930. PersonIdent who, String comment) {
  931. return new ReflogEntry() {
  932. @Override
  933. public ObjectId getOldId() {
  934. return oldId;
  935. }
  936. @Override
  937. public ObjectId getNewId() {
  938. return newId;
  939. }
  940. @Override
  941. public PersonIdent getWho() {
  942. return who;
  943. }
  944. @Override
  945. public String getComment() {
  946. return comment;
  947. }
  948. @Override
  949. public CheckoutEntry parseCheckout() {
  950. throw new UnsupportedOperationException();
  951. }
  952. };
  953. }
  954. private boolean batchesRefUpdates() {
  955. return atomic || useReftable;
  956. }
  957. }