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