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 43KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  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.file;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static java.util.concurrent.TimeUnit.NANOSECONDS;
  13. import static java.util.concurrent.TimeUnit.SECONDS;
  14. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.LOCK_FAILURE;
  15. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.OK;
  16. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.REJECTED_MISSING_OBJECT;
  17. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.REJECTED_NONFASTFORWARD;
  18. import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.TRANSACTION_ABORTED;
  19. import static org.eclipse.jgit.lib.ObjectId.zeroId;
  20. import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE;
  21. import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
  22. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
  23. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
  24. import static org.junit.Assert.assertEquals;
  25. import static org.junit.Assert.assertFalse;
  26. import static org.junit.Assert.assertNotNull;
  27. import static org.junit.Assert.assertNull;
  28. import static org.junit.Assert.assertTrue;
  29. import static org.junit.Assume.assumeFalse;
  30. import static org.junit.Assume.assumeTrue;
  31. import java.io.File;
  32. import java.io.IOException;
  33. import java.nio.file.Files;
  34. import java.util.Arrays;
  35. import java.util.Collection;
  36. import java.util.Collections;
  37. import java.util.LinkedHashMap;
  38. import java.util.List;
  39. import java.util.Map;
  40. import java.util.concurrent.locks.ReentrantLock;
  41. import java.util.function.Predicate;
  42. import org.eclipse.jgit.events.ListenerHandle;
  43. import org.eclipse.jgit.events.RefsChangedListener;
  44. import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
  45. import org.eclipse.jgit.junit.StrictWorkMonitor;
  46. import org.eclipse.jgit.junit.TestRepository;
  47. import org.eclipse.jgit.lib.AnyObjectId;
  48. import org.eclipse.jgit.lib.BatchRefUpdate;
  49. import org.eclipse.jgit.lib.CheckoutEntry;
  50. import org.eclipse.jgit.lib.ConfigConstants;
  51. import org.eclipse.jgit.lib.Constants;
  52. import org.eclipse.jgit.lib.NullProgressMonitor;
  53. import org.eclipse.jgit.lib.ObjectId;
  54. import org.eclipse.jgit.lib.PersonIdent;
  55. import org.eclipse.jgit.lib.Ref;
  56. import org.eclipse.jgit.lib.RefDatabase;
  57. import org.eclipse.jgit.lib.RefUpdate;
  58. import org.eclipse.jgit.lib.ReflogEntry;
  59. import org.eclipse.jgit.lib.ReflogReader;
  60. import org.eclipse.jgit.lib.Repository;
  61. import org.eclipse.jgit.lib.StoredConfig;
  62. import org.eclipse.jgit.revwalk.RevCommit;
  63. import org.eclipse.jgit.revwalk.RevWalk;
  64. import org.eclipse.jgit.transport.ReceiveCommand;
  65. import org.junit.After;
  66. import org.junit.Before;
  67. import org.junit.Test;
  68. import org.junit.runner.RunWith;
  69. import org.junit.runners.Parameterized;
  70. import org.junit.runners.Parameterized.Parameter;
  71. import org.junit.runners.Parameterized.Parameters;
  72. @SuppressWarnings("boxing")
  73. @RunWith(Parameterized.class)
  74. public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
  75. @Parameter(0)
  76. public boolean atomic;
  77. @Parameter(1)
  78. public boolean useReftable;
  79. @Parameters(name = "atomic={0} reftable={1}")
  80. public static Collection<Object[]> data() {
  81. return Arrays.asList(new Object[][] { { Boolean.FALSE, Boolean.FALSE },
  82. { Boolean.TRUE, Boolean.FALSE },
  83. { Boolean.FALSE, Boolean.TRUE },
  84. { Boolean.TRUE, Boolean.TRUE }, });
  85. }
  86. private Repository diskRepo;
  87. private TestRepository<Repository> repo;
  88. private RefDirectory refdir;
  89. private RevCommit A;
  90. private RevCommit B; // B descends from A.
  91. /**
  92. * When asserting the number of RefsChangedEvents you must account for one
  93. * additional event due to the initial ref setup via a number of calls to
  94. * {@link #writeLooseRef(String, AnyObjectId)} (will be fired in execute()
  95. * when it is detected that the on-disk loose refs have changed), or for one
  96. * additional event per {@link #writeRef(String, AnyObjectId)}.
  97. */
  98. private int refsChangedEvents;
  99. private ListenerHandle handle;
  100. private RefsChangedListener refsChangedListener = event -> {
  101. refsChangedEvents++;
  102. };
  103. @Override
  104. @Before
  105. public void setUp() throws Exception {
  106. super.setUp();
  107. FileRepository fileRepo = createBareRepository();
  108. if (useReftable) {
  109. fileRepo.convertToReftable(false, false);
  110. }
  111. diskRepo = fileRepo;
  112. setLogAllRefUpdates(true);
  113. if (!useReftable) {
  114. refdir = (RefDirectory) diskRepo.getRefDatabase();
  115. refdir.setRetrySleepMs(Arrays.asList(0, 0));
  116. }
  117. repo = new TestRepository<>(diskRepo);
  118. A = repo.commit().create();
  119. B = repo.commit(repo.getRevWalk().parseCommit(A));
  120. refsChangedEvents = 0;
  121. handle = diskRepo.getListenerList()
  122. .addRefsChangedListener(refsChangedListener);
  123. }
  124. @After
  125. public void removeListener() {
  126. handle.remove();
  127. refsChangedEvents = 0;
  128. }
  129. @Test
  130. public void packedRefsFileIsSorted() throws IOException {
  131. assumeTrue(atomic);
  132. assumeFalse(useReftable);
  133. for (int i = 0; i < 2; i++) {
  134. BatchRefUpdate bu = diskRepo.getRefDatabase().newBatchUpdate();
  135. String b1 = String.format("refs/heads/a%d", i);
  136. String b2 = String.format("refs/heads/b%d", i);
  137. bu.setAtomic(atomic);
  138. ReceiveCommand c1 = new ReceiveCommand(ObjectId.zeroId(), A, b1);
  139. ReceiveCommand c2 = new ReceiveCommand(ObjectId.zeroId(), B, b2);
  140. bu.addCommand(c1, c2);
  141. try (RevWalk rw = new RevWalk(diskRepo)) {
  142. bu.execute(rw, NullProgressMonitor.INSTANCE);
  143. }
  144. assertEquals(c1.getResult(), ReceiveCommand.Result.OK);
  145. assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
  146. }
  147. File packed = new File(diskRepo.getDirectory(), "packed-refs");
  148. String packedStr = new String(Files.readAllBytes(packed.toPath()),
  149. UTF_8);
  150. int a2 = packedStr.indexOf("refs/heads/a1");
  151. int b1 = packedStr.indexOf("refs/heads/b0");
  152. assertTrue(a2 < b1);
  153. }
  154. @Test
  155. public void simpleNoForce() throws IOException {
  156. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  157. List<ReceiveCommand> cmds = Arrays.asList(
  158. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  159. new ReceiveCommand(B, A, "refs/heads/masters",
  160. UPDATE_NONFASTFORWARD));
  161. execute(newBatchUpdate(cmds));
  162. if (atomic) {
  163. assertResults(cmds, TRANSACTION_ABORTED, REJECTED_NONFASTFORWARD);
  164. assertRefs("refs/heads/master", A, "refs/heads/masters", B);
  165. } else {
  166. assertResults(cmds, OK, REJECTED_NONFASTFORWARD);
  167. assertRefs("refs/heads/master", B, "refs/heads/masters", B);
  168. }
  169. }
  170. @Test
  171. public void simpleNoForceRefsChangedEvents() throws IOException {
  172. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  173. int initialRefsChangedEvents = refsChangedEvents;
  174. List<ReceiveCommand> cmds = Arrays.asList(
  175. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  176. new ReceiveCommand(B, A, "refs/heads/masters",
  177. UPDATE_NONFASTFORWARD));
  178. execute(newBatchUpdate(cmds));
  179. assertEquals(atomic ? initialRefsChangedEvents
  180. : initialRefsChangedEvents + 1, refsChangedEvents);
  181. }
  182. @Test
  183. public void simpleForce() throws IOException {
  184. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  185. List<ReceiveCommand> cmds = Arrays.asList(
  186. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  187. new ReceiveCommand(B, A, "refs/heads/masters",
  188. UPDATE_NONFASTFORWARD));
  189. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  190. assertResults(cmds, OK, OK);
  191. assertRefs("refs/heads/master", B, "refs/heads/masters", A);
  192. }
  193. @Test
  194. public void simpleForceRefsChangedEvents() throws IOException {
  195. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  196. int initialRefsChangedEvents = refsChangedEvents;
  197. List<ReceiveCommand> cmds = Arrays.asList(
  198. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  199. new ReceiveCommand(B, A, "refs/heads/masters",
  200. UPDATE_NONFASTFORWARD));
  201. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  202. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  203. : initialRefsChangedEvents + 2, refsChangedEvents);
  204. }
  205. @Test
  206. public void nonFastForwardDoesNotDoExpensiveMergeCheck()
  207. throws IOException {
  208. writeLooseRef("refs/heads/master", B);
  209. List<ReceiveCommand> cmds = Arrays.asList(new ReceiveCommand(B, A,
  210. "refs/heads/master", UPDATE_NONFASTFORWARD));
  211. try (RevWalk rw = new RevWalk(diskRepo) {
  212. @Override
  213. public boolean isMergedInto(RevCommit base, RevCommit tip) {
  214. throw new AssertionError("isMergedInto() should not be called");
  215. }
  216. }) {
  217. newBatchUpdate(cmds).setAllowNonFastForwards(true).execute(rw,
  218. new StrictWorkMonitor());
  219. }
  220. assertResults(cmds, OK);
  221. assertRefs("refs/heads/master", A);
  222. }
  223. @Test
  224. public void nonFastForwardDoesNotDoExpensiveMergeCheckRefsChangedEvents()
  225. throws IOException {
  226. writeLooseRef("refs/heads/master", B);
  227. int initialRefsChangedEvents = refsChangedEvents;
  228. List<ReceiveCommand> cmds = Arrays.asList(new ReceiveCommand(B, A,
  229. "refs/heads/master", UPDATE_NONFASTFORWARD));
  230. try (RevWalk rw = new RevWalk(diskRepo) {
  231. @Override
  232. public boolean isMergedInto(RevCommit base, RevCommit tip) {
  233. throw new AssertionError("isMergedInto() should not be called");
  234. }
  235. }) {
  236. newBatchUpdate(cmds).setAllowNonFastForwards(true).execute(rw,
  237. new StrictWorkMonitor());
  238. }
  239. assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
  240. }
  241. @Test
  242. public void fileDirectoryConflict() throws IOException {
  243. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  244. List<ReceiveCommand> cmds = Arrays.asList(
  245. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  246. new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE),
  247. new ReceiveCommand(zeroId(), A, "refs/heads", CREATE));
  248. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  249. if (atomic) {
  250. // Atomic update sees that master and master/x are conflicting, then
  251. // marks the first one in the list as LOCK_FAILURE and aborts the rest.
  252. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED,
  253. TRANSACTION_ABORTED);
  254. assertRefs("refs/heads/master", A, "refs/heads/masters", B);
  255. } else {
  256. // Non-atomic updates are applied in order: master succeeds, then
  257. // master/x fails due to conflict.
  258. assertResults(cmds, OK, LOCK_FAILURE, LOCK_FAILURE);
  259. assertRefs("refs/heads/master", B, "refs/heads/masters", B);
  260. }
  261. }
  262. @Test
  263. public void fileDirectoryConflictRefsChangedEvents() throws IOException {
  264. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  265. int initialRefsChangedEvents = refsChangedEvents;
  266. List<ReceiveCommand> cmds = Arrays.asList(
  267. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  268. new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE),
  269. new ReceiveCommand(zeroId(), A, "refs/heads", CREATE));
  270. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  271. assertEquals(atomic ? initialRefsChangedEvents
  272. : initialRefsChangedEvents + 1, refsChangedEvents);
  273. }
  274. @Test
  275. public void conflictThanksToDelete() throws IOException {
  276. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  277. List<ReceiveCommand> cmds = Arrays.asList(
  278. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  279. new ReceiveCommand(zeroId(), A, "refs/heads/masters/x", CREATE),
  280. new ReceiveCommand(B, zeroId(), "refs/heads/masters", DELETE));
  281. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  282. assertResults(cmds, OK, OK, OK);
  283. assertRefs("refs/heads/master", B, "refs/heads/masters/x", A);
  284. }
  285. @Test
  286. public void conflictThanksToDeleteRefsChangedEvents() throws IOException {
  287. writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
  288. int initialRefsChangedEvents = refsChangedEvents;
  289. List<ReceiveCommand> cmds = Arrays.asList(
  290. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  291. new ReceiveCommand(zeroId(), A, "refs/heads/masters/x", CREATE),
  292. new ReceiveCommand(B, zeroId(), "refs/heads/masters", DELETE));
  293. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  294. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  295. : initialRefsChangedEvents + 3, refsChangedEvents);
  296. }
  297. @Test
  298. public void updateToMissingObject() throws IOException {
  299. writeLooseRef("refs/heads/master", A);
  300. ObjectId bad = ObjectId
  301. .fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  302. List<ReceiveCommand> cmds = Arrays.asList(
  303. new ReceiveCommand(A, bad, "refs/heads/master", UPDATE),
  304. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  305. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  306. if (atomic) {
  307. assertResults(cmds, REJECTED_MISSING_OBJECT, TRANSACTION_ABORTED);
  308. assertRefs("refs/heads/master", A);
  309. } else {
  310. assertResults(cmds, REJECTED_MISSING_OBJECT, OK);
  311. assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
  312. }
  313. }
  314. @Test
  315. public void updateToMissingObjectRefsChangedEvents() throws IOException {
  316. writeLooseRef("refs/heads/master", A);
  317. int initialRefsChangedEvents = refsChangedEvents;
  318. ObjectId bad = ObjectId
  319. .fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  320. List<ReceiveCommand> cmds = Arrays.asList(
  321. new ReceiveCommand(A, bad, "refs/heads/master", UPDATE),
  322. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  323. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  324. assertEquals(atomic ? initialRefsChangedEvents
  325. : initialRefsChangedEvents + 1, refsChangedEvents);
  326. }
  327. @Test
  328. public void addMissingObject() throws IOException {
  329. writeLooseRef("refs/heads/master", A);
  330. ObjectId bad = ObjectId
  331. .fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  332. List<ReceiveCommand> cmds = Arrays.asList(
  333. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  334. new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", CREATE));
  335. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  336. if (atomic) {
  337. assertResults(cmds, TRANSACTION_ABORTED, REJECTED_MISSING_OBJECT);
  338. assertRefs("refs/heads/master", A);
  339. } else {
  340. assertResults(cmds, OK, REJECTED_MISSING_OBJECT);
  341. assertRefs("refs/heads/master", B);
  342. }
  343. }
  344. @Test
  345. public void addMissingObjectRefsChangedEvents() throws IOException {
  346. writeLooseRef("refs/heads/master", A);
  347. int initialRefsChangedEvents = refsChangedEvents;
  348. ObjectId bad = ObjectId
  349. .fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  350. List<ReceiveCommand> cmds = Arrays.asList(
  351. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  352. new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", CREATE));
  353. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
  354. assertEquals(atomic ? initialRefsChangedEvents
  355. : initialRefsChangedEvents + 1, refsChangedEvents);
  356. }
  357. @Test
  358. public void oneNonExistentRef() throws IOException {
  359. List<ReceiveCommand> cmds = Arrays.asList(
  360. new ReceiveCommand(A, B, "refs/heads/foo1", UPDATE),
  361. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  362. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  363. if (atomic) {
  364. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
  365. assertRefs();
  366. assertEquals(0, refsChangedEvents);
  367. } else {
  368. assertResults(cmds, LOCK_FAILURE, OK);
  369. assertRefs("refs/heads/foo2", B);
  370. assertEquals(1, refsChangedEvents);
  371. }
  372. }
  373. @Test
  374. public void oneRefWrongOldValue() throws IOException {
  375. writeLooseRef("refs/heads/master", A);
  376. List<ReceiveCommand> cmds = Arrays.asList(
  377. new ReceiveCommand(B, B, "refs/heads/master", UPDATE),
  378. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  379. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  380. if (atomic) {
  381. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
  382. assertRefs("refs/heads/master", A);
  383. } else {
  384. assertResults(cmds, LOCK_FAILURE, OK);
  385. assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
  386. }
  387. }
  388. @Test
  389. public void oneRefWrongOldValueRefsChangedEvents() throws IOException {
  390. writeLooseRef("refs/heads/master", A);
  391. int initialRefsChangedEvents = refsChangedEvents;
  392. List<ReceiveCommand> cmds = Arrays.asList(
  393. new ReceiveCommand(B, B, "refs/heads/master", UPDATE),
  394. new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
  395. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  396. assertEquals(atomic ? initialRefsChangedEvents
  397. : initialRefsChangedEvents + 1, refsChangedEvents);
  398. }
  399. @Test
  400. public void nonExistentRef() throws IOException {
  401. writeLooseRef("refs/heads/master", A);
  402. List<ReceiveCommand> cmds = Arrays.asList(
  403. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  404. new ReceiveCommand(A, zeroId(), "refs/heads/foo2", DELETE));
  405. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  406. if (atomic) {
  407. assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
  408. assertRefs("refs/heads/master", A);
  409. } else {
  410. assertResults(cmds, OK, LOCK_FAILURE);
  411. assertRefs("refs/heads/master", B);
  412. }
  413. }
  414. @Test
  415. public void nonExistentRefRefsChangedEvents() throws IOException {
  416. writeLooseRef("refs/heads/master", A);
  417. int initialRefsChangedEvents = refsChangedEvents;
  418. List<ReceiveCommand> cmds = Arrays.asList(
  419. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  420. new ReceiveCommand(A, zeroId(), "refs/heads/foo2", DELETE));
  421. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  422. assertEquals(atomic ? initialRefsChangedEvents
  423. : initialRefsChangedEvents + 1, refsChangedEvents);
  424. }
  425. @Test
  426. public void noRefLog() throws IOException {
  427. writeRef("refs/heads/master", A);
  428. int initialRefsChangedEvents = refsChangedEvents;
  429. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  430. "refs/heads/branch");
  431. assertEquals(Collections.singleton("refs/heads/master"),
  432. oldLogs.keySet());
  433. List<ReceiveCommand> cmds = Arrays.asList(
  434. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  435. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  436. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  437. assertResults(cmds, OK, OK);
  438. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  439. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  440. : initialRefsChangedEvents + 2, refsChangedEvents);
  441. assertReflogUnchanged(oldLogs, "refs/heads/master");
  442. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  443. }
  444. @Test
  445. public void reflogDefaultIdent() throws IOException {
  446. writeRef("refs/heads/master", A);
  447. writeRef("refs/heads/branch2", A);
  448. int initialRefsChangedEvents = refsChangedEvents;
  449. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  450. "refs/heads/branch1", "refs/heads/branch2");
  451. List<ReceiveCommand> cmds = Arrays.asList(
  452. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  453. new ReceiveCommand(zeroId(), B, "refs/heads/branch1", CREATE));
  454. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)
  455. .setRefLogMessage("a reflog", false));
  456. assertResults(cmds, OK, OK);
  457. assertRefs("refs/heads/master", B, "refs/heads/branch1", B,
  458. "refs/heads/branch2", A);
  459. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  460. : initialRefsChangedEvents + 2, refsChangedEvents);
  461. assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  462. getLastReflog("refs/heads/master"));
  463. assertReflogEquals(
  464. reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
  465. getLastReflog("refs/heads/branch1"));
  466. assertReflogUnchanged(oldLogs, "refs/heads/branch2");
  467. }
  468. @Test
  469. public void reflogAppendStatusNoMessage() throws IOException {
  470. writeRef("refs/heads/master", A);
  471. writeRef("refs/heads/branch1", B);
  472. int initialRefsChangedEvents = refsChangedEvents;
  473. List<ReceiveCommand> cmds = Arrays.asList(
  474. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  475. new ReceiveCommand(B, A, "refs/heads/branch1",
  476. UPDATE_NONFASTFORWARD),
  477. new ReceiveCommand(zeroId(), A, "refs/heads/branch2", CREATE));
  478. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)
  479. .setRefLogMessage(null, true));
  480. assertResults(cmds, OK, OK, OK);
  481. assertRefs("refs/heads/master", B, "refs/heads/branch1", A,
  482. "refs/heads/branch2", A);
  483. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  484. : initialRefsChangedEvents + 3, refsChangedEvents);
  485. assertReflogEquals(
  486. // Always forced; setAllowNonFastForwards(true) bypasses the
  487. // check.
  488. reflog(A, B, new PersonIdent(diskRepo), "forced-update"),
  489. getLastReflog("refs/heads/master"));
  490. assertReflogEquals(
  491. reflog(B, A, new PersonIdent(diskRepo), "forced-update"),
  492. getLastReflog("refs/heads/branch1"));
  493. assertReflogEquals(
  494. reflog(zeroId(), A, new PersonIdent(diskRepo), "created"),
  495. getLastReflog("refs/heads/branch2"));
  496. }
  497. @Test
  498. public void reflogAppendStatusFastForward() throws IOException {
  499. writeRef("refs/heads/master", A);
  500. int initialRefsChangedEvents = refsChangedEvents;
  501. List<ReceiveCommand> cmds = Arrays
  502. .asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  503. execute(newBatchUpdate(cmds).setRefLogMessage(null, true));
  504. assertResults(cmds, OK);
  505. assertRefs("refs/heads/master", B);
  506. assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
  507. assertReflogEquals(
  508. reflog(A, B, new PersonIdent(diskRepo), "fast-forward"),
  509. getLastReflog("refs/heads/master"));
  510. }
  511. @Test
  512. public void reflogAppendStatusWithMessage() throws IOException {
  513. writeRef("refs/heads/master", A);
  514. int initialRefsChangedEvents = refsChangedEvents;
  515. List<ReceiveCommand> cmds = Arrays.asList(
  516. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  517. new ReceiveCommand(zeroId(), A, "refs/heads/branch", CREATE));
  518. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
  519. assertResults(cmds, OK, OK);
  520. assertRefs("refs/heads/master", B, "refs/heads/branch", A);
  521. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  522. : initialRefsChangedEvents + 2, refsChangedEvents);
  523. assertReflogEquals(
  524. reflog(A, B, new PersonIdent(diskRepo),
  525. "a reflog: fast-forward"),
  526. getLastReflog("refs/heads/master"));
  527. assertReflogEquals(
  528. reflog(zeroId(), A, new PersonIdent(diskRepo),
  529. "a reflog: created"),
  530. getLastReflog("refs/heads/branch"));
  531. }
  532. @Test
  533. public void reflogCustomIdent() throws IOException {
  534. writeRef("refs/heads/master", A);
  535. int initialRefsChangedEvents = refsChangedEvents;
  536. List<ReceiveCommand> cmds = Arrays.asList(
  537. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  538. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  539. PersonIdent ident = new PersonIdent("A Reflog User",
  540. "reflog@example.com");
  541. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false)
  542. .setRefLogIdent(ident));
  543. assertResults(cmds, OK, OK);
  544. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  545. : initialRefsChangedEvents + 2, refsChangedEvents);
  546. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  547. assertReflogEquals(reflog(A, B, ident, "a reflog"),
  548. getLastReflog("refs/heads/master"), true);
  549. assertReflogEquals(reflog(zeroId(), B, ident, "a reflog"),
  550. getLastReflog("refs/heads/branch"), true);
  551. }
  552. @Test
  553. public void reflogDelete() throws IOException {
  554. writeRef("refs/heads/master", A);
  555. writeRef("refs/heads/branch", A);
  556. int initialRefsChangedEvents = refsChangedEvents;
  557. assertEquals(2, getLastReflogs("refs/heads/master", "refs/heads/branch")
  558. .size());
  559. List<ReceiveCommand> cmds = Arrays.asList(
  560. new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE),
  561. new ReceiveCommand(A, B, "refs/heads/branch", UPDATE));
  562. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  563. assertResults(cmds, OK, OK);
  564. assertRefs("refs/heads/branch", B);
  565. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  566. : initialRefsChangedEvents + 2, refsChangedEvents);
  567. if (useReftable) {
  568. // reftable retains reflog entries for deleted branches.
  569. assertReflogEquals(
  570. reflog(A, zeroId(), new PersonIdent(diskRepo), "a reflog"),
  571. getLastReflog("refs/heads/master"));
  572. } else {
  573. assertNull(getLastReflog("refs/heads/master"));
  574. }
  575. assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  576. getLastReflog("refs/heads/branch"));
  577. }
  578. @Test
  579. public void reflogFileDirectoryConflict() throws IOException {
  580. writeRef("refs/heads/master", A);
  581. int initialRefsChangedEvents = refsChangedEvents;
  582. List<ReceiveCommand> cmds = Arrays.asList(
  583. new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE),
  584. new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE));
  585. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  586. assertResults(cmds, OK, OK);
  587. assertRefs("refs/heads/master/x", A);
  588. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  589. : initialRefsChangedEvents + 2, refsChangedEvents);
  590. if (!useReftable) {
  591. // reftable retains reflog entries for deleted branches.
  592. assertNull(getLastReflog("refs/heads/master"));
  593. }
  594. assertReflogEquals(
  595. reflog(zeroId(), A, new PersonIdent(diskRepo), "a reflog"),
  596. getLastReflog("refs/heads/master/x"));
  597. }
  598. @Test
  599. public void reflogOnLockFailure() throws IOException {
  600. writeRef("refs/heads/master", A);
  601. int initialRefsChangedEvents = refsChangedEvents;
  602. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  603. "refs/heads/branch");
  604. List<ReceiveCommand> cmds = Arrays.asList(
  605. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  606. new ReceiveCommand(A, B, "refs/heads/branch", UPDATE));
  607. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  608. if (atomic) {
  609. assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
  610. assertEquals(initialRefsChangedEvents, refsChangedEvents);
  611. assertReflogUnchanged(oldLogs, "refs/heads/master");
  612. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  613. } else {
  614. assertResults(cmds, OK, LOCK_FAILURE);
  615. assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
  616. assertReflogEquals(
  617. reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  618. getLastReflog("refs/heads/master"));
  619. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  620. }
  621. }
  622. @Test
  623. public void overrideRefLogMessage() throws Exception {
  624. writeRef("refs/heads/master", A);
  625. int initialRefsChangedEvents = refsChangedEvents;
  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(0).setRefLogMessage("custom log", false);
  630. PersonIdent ident = new PersonIdent(diskRepo);
  631. execute(newBatchUpdate(cmds).setRefLogIdent(ident)
  632. .setRefLogMessage("a reflog", true));
  633. assertResults(cmds, OK, OK);
  634. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  635. : initialRefsChangedEvents + 2, refsChangedEvents);
  636. assertReflogEquals(reflog(A, B, ident, "custom log"),
  637. getLastReflog("refs/heads/master"), true);
  638. assertReflogEquals(reflog(zeroId(), B, ident, "a reflog: created"),
  639. getLastReflog("refs/heads/branch"), true);
  640. }
  641. @Test
  642. public void overrideDisableRefLog() throws Exception {
  643. writeRef("refs/heads/master", A);
  644. int initialRefsChangedEvents = refsChangedEvents;
  645. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  646. "refs/heads/branch");
  647. List<ReceiveCommand> cmds = Arrays.asList(
  648. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  649. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  650. cmds.get(0).disableRefLog();
  651. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
  652. assertResults(cmds, OK, OK);
  653. assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
  654. : initialRefsChangedEvents + 2, refsChangedEvents);
  655. assertReflogUnchanged(oldLogs, "refs/heads/master");
  656. assertReflogEquals(
  657. reflog(zeroId(), B, new PersonIdent(diskRepo),
  658. "a reflog: created"),
  659. getLastReflog("refs/heads/branch"));
  660. }
  661. @Test
  662. public void refLogNotWrittenWithoutConfigOption() throws Exception {
  663. assumeFalse(useReftable);
  664. setLogAllRefUpdates(false);
  665. writeRef("refs/heads/master", A);
  666. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  667. "refs/heads/branch");
  668. assertTrue(oldLogs.isEmpty());
  669. List<ReceiveCommand> cmds = Arrays.asList(
  670. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  671. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  672. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  673. assertResults(cmds, OK, OK);
  674. assertReflogUnchanged(oldLogs, "refs/heads/master");
  675. assertReflogUnchanged(oldLogs, "refs/heads/branch");
  676. }
  677. @Test
  678. public void forceRefLogInUpdate() throws Exception {
  679. assumeFalse(useReftable);
  680. setLogAllRefUpdates(false);
  681. writeRef("refs/heads/master", A);
  682. assertTrue(getLastReflogs("refs/heads/master", "refs/heads/branch")
  683. .isEmpty());
  684. List<ReceiveCommand> cmds = Arrays.asList(
  685. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  686. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  687. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false)
  688. .setForceRefLog(true));
  689. assertResults(cmds, OK, OK);
  690. assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
  691. getLastReflog("refs/heads/master"));
  692. assertReflogEquals(
  693. reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
  694. getLastReflog("refs/heads/branch"));
  695. }
  696. @Test
  697. public void forceRefLogInCommand() throws Exception {
  698. assumeFalse(useReftable);
  699. setLogAllRefUpdates(false);
  700. writeRef("refs/heads/master", A);
  701. Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
  702. "refs/heads/branch");
  703. assertTrue(oldLogs.isEmpty());
  704. List<ReceiveCommand> cmds = Arrays.asList(
  705. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  706. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  707. cmds.get(1).setForceRefLog(true);
  708. execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
  709. assertResults(cmds, OK, OK);
  710. assertReflogUnchanged(oldLogs, "refs/heads/master");
  711. assertReflogEquals(
  712. reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
  713. getLastReflog("refs/heads/branch"));
  714. }
  715. @Test
  716. public void packedRefsLockFailure() throws Exception {
  717. assumeFalse(useReftable);
  718. writeLooseRef("refs/heads/master", A);
  719. List<ReceiveCommand> cmds = Arrays.asList(
  720. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  721. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  722. LockFile myLock = refdir.lockPackedRefs();
  723. try {
  724. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  725. assertFalse(getLockFile("refs/heads/master").exists());
  726. assertFalse(getLockFile("refs/heads/branch").exists());
  727. if (atomic) {
  728. assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
  729. assertRefs("refs/heads/master", A);
  730. } else {
  731. // Only operates on loose refs, doesn't care that packed-refs is
  732. // locked.
  733. assertResults(cmds, OK, OK);
  734. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  735. }
  736. } finally {
  737. myLock.unlock();
  738. }
  739. }
  740. @Test
  741. public void packedRefsLockFailureRefsChangedEvents() throws Exception {
  742. assumeFalse(useReftable);
  743. writeLooseRef("refs/heads/master", A);
  744. int initialRefsChangedEvents = refsChangedEvents;
  745. List<ReceiveCommand> cmds = Arrays.asList(
  746. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  747. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  748. LockFile myLock = refdir.lockPackedRefs();
  749. try {
  750. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  751. assertEquals(atomic ? initialRefsChangedEvents
  752. : initialRefsChangedEvents + 2, refsChangedEvents);
  753. } finally {
  754. myLock.unlock();
  755. }
  756. }
  757. @Test
  758. public void oneRefLockFailure() throws Exception {
  759. assumeFalse(useReftable);
  760. writeLooseRef("refs/heads/master", A);
  761. List<ReceiveCommand> cmds = Arrays.asList(
  762. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE),
  763. new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  764. LockFile myLock = new LockFile(refdir.fileFor("refs/heads/master"));
  765. assertTrue(myLock.lock());
  766. try {
  767. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  768. assertFalse(LockFile.getLockFile(refdir.packedRefsFile).exists());
  769. assertFalse(getLockFile("refs/heads/branch").exists());
  770. if (atomic) {
  771. assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
  772. assertRefs("refs/heads/master", A);
  773. } else {
  774. assertResults(cmds, OK, LOCK_FAILURE);
  775. assertRefs("refs/heads/branch", B, "refs/heads/master", A);
  776. }
  777. } finally {
  778. myLock.unlock();
  779. }
  780. }
  781. @Test
  782. public void oneRefLockFailureRefsChangedEvents() throws Exception {
  783. assumeFalse(useReftable);
  784. writeLooseRef("refs/heads/master", A);
  785. int initialRefsChangedEvents = refsChangedEvents;
  786. List<ReceiveCommand> cmds = Arrays.asList(
  787. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE),
  788. new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  789. LockFile myLock = new LockFile(refdir.fileFor("refs/heads/master"));
  790. assertTrue(myLock.lock());
  791. try {
  792. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  793. assertEquals(atomic ? initialRefsChangedEvents
  794. : initialRefsChangedEvents + 1, refsChangedEvents);
  795. } finally {
  796. myLock.unlock();
  797. }
  798. }
  799. @Test
  800. public void singleRefUpdateDoesNotRequirePackedRefsLock() throws Exception {
  801. assumeFalse(useReftable);
  802. writeLooseRef("refs/heads/master", A);
  803. List<ReceiveCommand> cmds = Arrays
  804. .asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  805. LockFile myLock = refdir.lockPackedRefs();
  806. try {
  807. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  808. assertFalse(getLockFile("refs/heads/master").exists());
  809. assertResults(cmds, OK);
  810. assertRefs("refs/heads/master", B);
  811. } finally {
  812. myLock.unlock();
  813. }
  814. }
  815. @Test
  816. public void singleRefUpdateDoesNotRequirePackedRefsLockRefsChangedEvents()
  817. throws Exception {
  818. assumeFalse(useReftable);
  819. writeLooseRef("refs/heads/master", A);
  820. int initialRefsChangedEvents = refsChangedEvents;
  821. List<ReceiveCommand> cmds = Arrays
  822. .asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
  823. LockFile myLock = refdir.lockPackedRefs();
  824. try {
  825. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  826. assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
  827. } finally {
  828. myLock.unlock();
  829. }
  830. }
  831. @Test
  832. public void atomicUpdateRespectsInProcessLock() throws Exception {
  833. assumeTrue(atomic);
  834. assumeFalse(useReftable);
  835. writeLooseRef("refs/heads/master", A);
  836. List<ReceiveCommand> cmds = Arrays.asList(
  837. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  838. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  839. Thread t = new Thread(() -> {
  840. try {
  841. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  842. } catch (Exception e) {
  843. throw new RuntimeException(e);
  844. }
  845. });
  846. ReentrantLock l = refdir.inProcessPackedRefsLock;
  847. l.lock();
  848. try {
  849. t.start();
  850. long timeoutSecs = 10;
  851. long startNanos = System.nanoTime();
  852. // Hold onto the lock until we observe the worker thread has
  853. // attempted to
  854. // acquire it.
  855. while (l.getQueueLength() == 0) {
  856. long elapsedNanos = System.nanoTime() - startNanos;
  857. assertTrue(
  858. "timed out waiting for work thread to attempt to acquire lock",
  859. NANOSECONDS.toSeconds(elapsedNanos) < timeoutSecs);
  860. Thread.sleep(3);
  861. }
  862. // Once we unlock, the worker thread should finish the update
  863. // promptly.
  864. l.unlock();
  865. t.join(SECONDS.toMillis(timeoutSecs));
  866. assertFalse(t.isAlive());
  867. } finally {
  868. if (l.isHeldByCurrentThread()) {
  869. l.unlock();
  870. }
  871. }
  872. assertResults(cmds, OK, OK);
  873. assertRefs("refs/heads/master", B, "refs/heads/branch", B);
  874. }
  875. @Test
  876. public void atomicUpdateRespectsInProcessLockRefsChangedEvents()
  877. throws Exception {
  878. assumeTrue(atomic);
  879. assumeFalse(useReftable);
  880. writeLooseRef("refs/heads/master", A);
  881. int initialRefsChangedEvents = refsChangedEvents;
  882. List<ReceiveCommand> cmds = Arrays.asList(
  883. new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
  884. new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
  885. Thread t = new Thread(() -> {
  886. try {
  887. execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
  888. } catch (Exception e) {
  889. throw new RuntimeException(e);
  890. }
  891. });
  892. ReentrantLock l = refdir.inProcessPackedRefsLock;
  893. l.lock();
  894. try {
  895. t.start();
  896. long timeoutSecs = 10;
  897. // Hold onto the lock until we observe the worker thread has
  898. // attempted to
  899. // acquire it.
  900. while (l.getQueueLength() == 0) {
  901. Thread.sleep(3);
  902. }
  903. // Once we unlock, the worker thread should finish the update
  904. // promptly.
  905. l.unlock();
  906. t.join(SECONDS.toMillis(timeoutSecs));
  907. } finally {
  908. if (l.isHeldByCurrentThread()) {
  909. l.unlock();
  910. }
  911. }
  912. assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
  913. }
  914. private void setLogAllRefUpdates(boolean enable) throws Exception {
  915. StoredConfig cfg = diskRepo.getConfig();
  916. cfg.load();
  917. cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  918. ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, enable);
  919. cfg.save();
  920. }
  921. private void writeLooseRef(String name, AnyObjectId id) throws IOException {
  922. if (useReftable) {
  923. writeRef(name, id);
  924. } else {
  925. write(new File(diskRepo.getDirectory(), name), id.name() + "\n");
  926. // force the refs-changed event to be fired for the loose ref that
  927. // was created. We do this to get the events fired during the test
  928. // 'setup' out of the way and this allows us to now accurately
  929. // assert only for the new events fired during the BatchRefUpdate.
  930. refdir.exactRef(name);
  931. }
  932. }
  933. private void writeLooseRefs(String name1, AnyObjectId id1, String name2,
  934. AnyObjectId id2) throws IOException {
  935. if (useReftable) {
  936. BatchRefUpdate bru = diskRepo.getRefDatabase().newBatchUpdate();
  937. Ref r1 = diskRepo.exactRef(name1);
  938. ReceiveCommand c1 = new ReceiveCommand(
  939. r1 != null ? r1.getObjectId() : ObjectId.zeroId(),
  940. id1.toObjectId(), name1, r1 == null ? CREATE : UPDATE);
  941. Ref r2 = diskRepo.exactRef(name2);
  942. ReceiveCommand c2 = new ReceiveCommand(
  943. r2 != null ? r2.getObjectId() : ObjectId.zeroId(),
  944. id2.toObjectId(), name2, r2 == null ? CREATE : UPDATE);
  945. bru.addCommand(c1, c2);
  946. try (RevWalk rw = new RevWalk(diskRepo)) {
  947. bru.execute(rw, NullProgressMonitor.INSTANCE);
  948. }
  949. assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
  950. assertEquals(c1.getResult(), ReceiveCommand.Result.OK);
  951. } else {
  952. writeLooseRef(name1, id1);
  953. writeLooseRef(name2, id2);
  954. }
  955. }
  956. private void writeRef(String name, AnyObjectId id) throws IOException {
  957. RefUpdate u = diskRepo.updateRef(name);
  958. u.setRefLogMessage(getClass().getSimpleName(), false);
  959. u.setForceUpdate(true);
  960. u.setNewObjectId(id);
  961. RefUpdate.Result r = u.update();
  962. switch (r) {
  963. case NEW:
  964. case FORCED:
  965. return;
  966. default:
  967. throw new IOException("Got " + r + " while updating " + name);
  968. }
  969. }
  970. private BatchRefUpdate newBatchUpdate(List<ReceiveCommand> cmds) {
  971. BatchRefUpdate u = diskRepo.getRefDatabase().newBatchUpdate();
  972. if (atomic) {
  973. assertTrue(u.isAtomic());
  974. } else {
  975. u.setAtomic(false);
  976. }
  977. u.addCommand(cmds);
  978. return u;
  979. }
  980. private void execute(BatchRefUpdate u) throws IOException {
  981. execute(u, false);
  982. }
  983. private void execute(BatchRefUpdate u, boolean strictWork)
  984. throws IOException {
  985. try (RevWalk rw = new RevWalk(diskRepo)) {
  986. u.execute(rw, strictWork ? new StrictWorkMonitor()
  987. : NullProgressMonitor.INSTANCE);
  988. }
  989. }
  990. private void assertRefs(Object... args) throws IOException {
  991. if (args.length % 2 != 0) {
  992. throw new IllegalArgumentException(
  993. "expected even number of args: " + Arrays.toString(args));
  994. }
  995. Map<String, AnyObjectId> expected = new LinkedHashMap<>();
  996. for (int i = 0; i < args.length; i += 2) {
  997. expected.put((String) args[i], (AnyObjectId) args[i + 1]);
  998. }
  999. Map<String, Ref> refs = diskRepo.getRefDatabase()
  1000. .getRefs(RefDatabase.ALL);
  1001. Ref actualHead = refs.remove(Constants.HEAD);
  1002. if (actualHead != null) {
  1003. String actualLeafName = actualHead.getLeaf().getName();
  1004. assertEquals(
  1005. "expected HEAD to point to refs/heads/master, got: "
  1006. + actualLeafName,
  1007. "refs/heads/master", actualLeafName);
  1008. AnyObjectId expectedMaster = expected.get("refs/heads/master");
  1009. assertNotNull("expected master ref since HEAD exists",
  1010. expectedMaster);
  1011. assertEquals(expectedMaster, actualHead.getObjectId());
  1012. }
  1013. Map<String, AnyObjectId> actual = new LinkedHashMap<>();
  1014. refs.forEach((n, r) -> actual.put(n, r.getObjectId()));
  1015. assertEquals(expected.keySet(), actual.keySet());
  1016. actual.forEach((n, a) -> assertEquals(n, expected.get(n), a));
  1017. }
  1018. enum Result {
  1019. OK(ReceiveCommand.Result.OK),
  1020. LOCK_FAILURE(ReceiveCommand.Result.LOCK_FAILURE),
  1021. REJECTED_NONFASTFORWARD(ReceiveCommand.Result.REJECTED_NONFASTFORWARD),
  1022. REJECTED_MISSING_OBJECT(ReceiveCommand.Result.REJECTED_MISSING_OBJECT),
  1023. TRANSACTION_ABORTED(ReceiveCommand::isTransactionAborted);
  1024. @SuppressWarnings("ImmutableEnumChecker")
  1025. final Predicate<? super ReceiveCommand> p;
  1026. private Result(Predicate<? super ReceiveCommand> p) {
  1027. this.p = p;
  1028. }
  1029. private Result(ReceiveCommand.Result result) {
  1030. this(c -> c.getResult() == result);
  1031. }
  1032. }
  1033. private void assertResults(List<ReceiveCommand> cmds, Result... expected) {
  1034. if (expected.length != cmds.size()) {
  1035. throw new IllegalArgumentException(
  1036. "expected " + cmds.size() + " result args");
  1037. }
  1038. for (int i = 0; i < cmds.size(); i++) {
  1039. ReceiveCommand c = cmds.get(i);
  1040. Result r = expected[i];
  1041. assertTrue(String.format(
  1042. "result of command (%d) should be %s, got %s %s%s",
  1043. Integer.valueOf(i), r, c, c.getResult(),
  1044. c.getMessage() != null ? " (" + c.getMessage() + ")" : ""),
  1045. r.p.test(c));
  1046. }
  1047. }
  1048. private Map<String, ReflogEntry> getLastReflogs(String... names)
  1049. throws IOException {
  1050. Map<String, ReflogEntry> result = new LinkedHashMap<>();
  1051. for (String name : names) {
  1052. ReflogEntry e = getLastReflog(name);
  1053. if (e != null) {
  1054. result.put(name, e);
  1055. }
  1056. }
  1057. return result;
  1058. }
  1059. private ReflogEntry getLastReflog(String name) throws IOException {
  1060. ReflogReader r = diskRepo.getReflogReader(name);
  1061. if (r == null) {
  1062. return null;
  1063. }
  1064. return r.getLastEntry();
  1065. }
  1066. private File getLockFile(String refName) {
  1067. return LockFile.getLockFile(refdir.fileFor(refName));
  1068. }
  1069. private void assertReflogUnchanged(Map<String, ReflogEntry> old,
  1070. String name) throws IOException {
  1071. assertReflogEquals(old.get(name), getLastReflog(name), true);
  1072. }
  1073. private static void assertReflogEquals(ReflogEntry expected,
  1074. ReflogEntry actual) {
  1075. assertReflogEquals(expected, actual, false);
  1076. }
  1077. private static void assertReflogEquals(ReflogEntry expected,
  1078. ReflogEntry actual, boolean strictTime) {
  1079. if (expected == null) {
  1080. assertNull(actual);
  1081. return;
  1082. }
  1083. assertNotNull(actual);
  1084. assertEquals(expected.getOldId(), actual.getOldId());
  1085. assertEquals(expected.getNewId(), actual.getNewId());
  1086. if (strictTime) {
  1087. assertEquals(expected.getWho(), actual.getWho());
  1088. } else {
  1089. assertEquals(expected.getWho().getName(),
  1090. actual.getWho().getName());
  1091. assertEquals(expected.getWho().getEmailAddress(),
  1092. actual.getWho().getEmailAddress());
  1093. }
  1094. assertEquals(expected.getComment(), actual.getComment());
  1095. }
  1096. private static ReflogEntry reflog(ObjectId oldId, ObjectId newId,
  1097. PersonIdent who, String comment) {
  1098. return new ReflogEntry() {
  1099. @Override
  1100. public ObjectId getOldId() {
  1101. return oldId;
  1102. }
  1103. @Override
  1104. public ObjectId getNewId() {
  1105. return newId;
  1106. }
  1107. @Override
  1108. public PersonIdent getWho() {
  1109. return who;
  1110. }
  1111. @Override
  1112. public String getComment() {
  1113. return comment;
  1114. }
  1115. @Override
  1116. public CheckoutEntry parseCheckout() {
  1117. throw new UnsupportedOperationException();
  1118. }
  1119. };
  1120. }
  1121. private boolean batchesRefUpdates() {
  1122. return atomic || useReftable;
  1123. }
  1124. }