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