Merge branch 'stable-5.8' into stable-5.9

* stable-5.8:
  Prepare 5.1.17-SNAPSHOT builds
  JGit v5.1.16.202106041830-r
  BatchRefUpdate: Skip saving conflicting ref names and prefixes in memory
  BatchRefUpdateTest: Accurately assert RefsChangedEvent(s) fired
  Optimize RefDirectory.isNameConflicting()
  Update bazlets and bazel version

Change-Id: I9abf7dd8b8e5eb3199fd6b43a4653c4e4cf4bf1b
This commit is contained in:
Matthias Sohn 2021-06-13 23:55:03 +02:00
commit 84063386b5
3 changed files with 317 additions and 119 deletions

View File

@ -190,14 +190,27 @@ public void simpleNoForce() throws IOException {
if (atomic) { if (atomic) {
assertResults(cmds, TRANSACTION_ABORTED, REJECTED_NONFASTFORWARD); assertResults(cmds, TRANSACTION_ABORTED, REJECTED_NONFASTFORWARD);
assertRefs("refs/heads/master", A, "refs/heads/masters", B); assertRefs("refs/heads/master", A, "refs/heads/masters", B);
assertEquals(1, refsChangedEvents);
} else { } else {
assertResults(cmds, OK, REJECTED_NONFASTFORWARD); assertResults(cmds, OK, REJECTED_NONFASTFORWARD);
assertRefs("refs/heads/master", B, "refs/heads/masters", B); assertRefs("refs/heads/master", B, "refs/heads/masters", B);
assertEquals(2, refsChangedEvents);
} }
} }
@Test
public void simpleNoForceRefsChangedEvents() throws IOException {
writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(B, A, "refs/heads/masters",
UPDATE_NONFASTFORWARD));
execute(newBatchUpdate(cmds));
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
}
@Test @Test
public void simpleForce() throws IOException { public void simpleForce() throws IOException {
writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B); writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
@ -210,7 +223,21 @@ public void simpleForce() throws IOException {
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/masters", A); assertRefs("refs/heads/master", B, "refs/heads/masters", A);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); }
@Test
public void simpleForceRefsChangedEvents() throws IOException {
writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(B, A, "refs/heads/masters",
UPDATE_NONFASTFORWARD));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
} }
@Test @Test
@ -232,7 +259,27 @@ public boolean isMergedInto(RevCommit base, RevCommit tip) {
assertResults(cmds, OK); assertResults(cmds, OK);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(2, refsChangedEvents); }
@Test
public void nonFastForwardDoesNotDoExpensiveMergeCheckRefsChangedEvents()
throws IOException {
writeLooseRef("refs/heads/master", B);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(new ReceiveCommand(B, A,
"refs/heads/master", UPDATE_NONFASTFORWARD));
try (RevWalk rw = new RevWalk(diskRepo) {
@Override
public boolean isMergedInto(RevCommit base, RevCommit tip) {
throw new AssertionError("isMergedInto() should not be called");
}
}) {
newBatchUpdate(cmds).setAllowNonFastForwards(true).execute(rw,
new StrictWorkMonitor());
}
assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
} }
@Test @Test
@ -251,16 +298,29 @@ public void fileDirectoryConflict() throws IOException {
assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED, assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED,
TRANSACTION_ABORTED); TRANSACTION_ABORTED);
assertRefs("refs/heads/master", A, "refs/heads/masters", B); assertRefs("refs/heads/master", A, "refs/heads/masters", B);
assertEquals(1, refsChangedEvents);
} else { } else {
// Non-atomic updates are applied in order: master succeeds, then // Non-atomic updates are applied in order: master succeeds, then
// master/x fails due to conflict. // master/x fails due to conflict.
assertResults(cmds, OK, LOCK_FAILURE, LOCK_FAILURE); assertResults(cmds, OK, LOCK_FAILURE, LOCK_FAILURE);
assertRefs("refs/heads/master", B, "refs/heads/masters", B); assertRefs("refs/heads/master", B, "refs/heads/masters", B);
assertEquals(2, refsChangedEvents);
} }
} }
@Test
public void fileDirectoryConflictRefsChangedEvents() throws IOException {
writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE),
new ReceiveCommand(zeroId(), A, "refs/heads", CREATE));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
}
@Test @Test
public void conflictThanksToDelete() throws IOException { public void conflictThanksToDelete() throws IOException {
writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B); writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
@ -273,15 +333,21 @@ public void conflictThanksToDelete() throws IOException {
assertResults(cmds, OK, OK, OK); assertResults(cmds, OK, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/masters/x", A); assertRefs("refs/heads/master", B, "refs/heads/masters/x", A);
if (atomic) { }
assertEquals(2, refsChangedEvents);
} else if (!useReftable) { @Test
// The non-atomic case actually produces 5 events, but that's an public void conflictThanksToDeleteRefsChangedEvents() throws IOException {
// implementation detail. We expect at least 4 events, one for the writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
// initial read due to writeLooseRef(), and then one for each int initialRefsChangedEvents = refsChangedEvents;
// successful ref update.
assertTrue(refsChangedEvents >= 4); List<ReceiveCommand> cmds = Arrays.asList(
} new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), A, "refs/heads/masters/x", CREATE),
new ReceiveCommand(B, zeroId(), "refs/heads/masters", DELETE));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 3, refsChangedEvents);
} }
@Test @Test
@ -298,14 +364,28 @@ public void updateToMissingObject() throws IOException {
if (atomic) { if (atomic) {
assertResults(cmds, REJECTED_MISSING_OBJECT, TRANSACTION_ABORTED); assertResults(cmds, REJECTED_MISSING_OBJECT, TRANSACTION_ABORTED);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(1, refsChangedEvents);
} else { } else {
assertResults(cmds, REJECTED_MISSING_OBJECT, OK); assertResults(cmds, REJECTED_MISSING_OBJECT, OK);
assertRefs("refs/heads/master", A, "refs/heads/foo2", B); assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
assertEquals(2, refsChangedEvents);
} }
} }
@Test
public void updateToMissingObjectRefsChangedEvents() throws IOException {
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
ObjectId bad = ObjectId
.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, bad, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
}
@Test @Test
public void addMissingObject() throws IOException { public void addMissingObject() throws IOException {
writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/master", A);
@ -320,14 +400,28 @@ public void addMissingObject() throws IOException {
if (atomic) { if (atomic) {
assertResults(cmds, TRANSACTION_ABORTED, REJECTED_MISSING_OBJECT); assertResults(cmds, TRANSACTION_ABORTED, REJECTED_MISSING_OBJECT);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(1, refsChangedEvents);
} else { } else {
assertResults(cmds, OK, REJECTED_MISSING_OBJECT); assertResults(cmds, OK, REJECTED_MISSING_OBJECT);
assertRefs("refs/heads/master", B); assertRefs("refs/heads/master", B);
assertEquals(2, refsChangedEvents);
} }
} }
@Test
public void addMissingObjectRefsChangedEvents() throws IOException {
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
ObjectId bad = ObjectId
.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", CREATE));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
}
@Test @Test
public void oneNonExistentRef() throws IOException { public void oneNonExistentRef() throws IOException {
List<ReceiveCommand> cmds = Arrays.asList( List<ReceiveCommand> cmds = Arrays.asList(
@ -358,14 +452,26 @@ public void oneRefWrongOldValue() throws IOException {
if (atomic) { if (atomic) {
assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED); assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(1, refsChangedEvents);
} else { } else {
assertResults(cmds, LOCK_FAILURE, OK); assertResults(cmds, LOCK_FAILURE, OK);
assertRefs("refs/heads/master", A, "refs/heads/foo2", B); assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
assertEquals(2, refsChangedEvents);
} }
} }
@Test
public void oneRefWrongOldValueRefsChangedEvents() throws IOException {
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(B, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
}
@Test @Test
public void nonExistentRef() throws IOException { public void nonExistentRef() throws IOException {
writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/master", A);
@ -378,17 +484,31 @@ public void nonExistentRef() throws IOException {
if (atomic) { if (atomic) {
assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE); assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(1, refsChangedEvents);
} else { } else {
assertResults(cmds, OK, LOCK_FAILURE); assertResults(cmds, OK, LOCK_FAILURE);
assertRefs("refs/heads/master", B); assertRefs("refs/heads/master", B);
assertEquals(2, refsChangedEvents);
} }
} }
@Test
public void nonExistentRefRefsChangedEvents() throws IOException {
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(A, zeroId(), "refs/heads/foo2", DELETE));
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
}
@Test @Test
public void noRefLog() throws IOException { public void noRefLog() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master", Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
"refs/heads/branch"); "refs/heads/branch");
@ -402,7 +522,8 @@ public void noRefLog() throws IOException {
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/branch", B); assertRefs("refs/heads/master", B, "refs/heads/branch", B);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
assertReflogUnchanged(oldLogs, "refs/heads/master"); assertReflogUnchanged(oldLogs, "refs/heads/master");
assertReflogUnchanged(oldLogs, "refs/heads/branch"); assertReflogUnchanged(oldLogs, "refs/heads/branch");
} }
@ -411,6 +532,7 @@ public void noRefLog() throws IOException {
public void reflogDefaultIdent() throws IOException { public void reflogDefaultIdent() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
writeRef("refs/heads/branch2", A); writeRef("refs/heads/branch2", A);
int initialRefsChangedEvents = refsChangedEvents;
Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master", Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
"refs/heads/branch1", "refs/heads/branch2"); "refs/heads/branch1", "refs/heads/branch2");
@ -423,7 +545,8 @@ public void reflogDefaultIdent() throws IOException {
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/branch1", B, assertRefs("refs/heads/master", B, "refs/heads/branch1", B,
"refs/heads/branch2", A); "refs/heads/branch2", A);
assertEquals(batchesRefUpdates() ? 3 : 4, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"), assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
getLastReflog("refs/heads/master")); getLastReflog("refs/heads/master"));
assertReflogEquals( assertReflogEquals(
@ -436,6 +559,7 @@ public void reflogDefaultIdent() throws IOException {
public void reflogAppendStatusNoMessage() throws IOException { public void reflogAppendStatusNoMessage() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
writeRef("refs/heads/branch1", B); writeRef("refs/heads/branch1", B);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList( List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE), new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@ -448,7 +572,8 @@ public void reflogAppendStatusNoMessage() throws IOException {
assertResults(cmds, OK, OK, OK); assertResults(cmds, OK, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/branch1", A, assertRefs("refs/heads/master", B, "refs/heads/branch1", A,
"refs/heads/branch2", A); "refs/heads/branch2", A);
assertEquals(batchesRefUpdates() ? 3 : 5, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 3, refsChangedEvents);
assertReflogEquals( assertReflogEquals(
// Always forced; setAllowNonFastForwards(true) bypasses the // Always forced; setAllowNonFastForwards(true) bypasses the
// check. // check.
@ -465,6 +590,7 @@ public void reflogAppendStatusNoMessage() throws IOException {
@Test @Test
public void reflogAppendStatusFastForward() throws IOException { public void reflogAppendStatusFastForward() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays List<ReceiveCommand> cmds = Arrays
.asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE)); .asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
@ -472,7 +598,7 @@ public void reflogAppendStatusFastForward() throws IOException {
assertResults(cmds, OK); assertResults(cmds, OK);
assertRefs("refs/heads/master", B); assertRefs("refs/heads/master", B);
assertEquals(2, refsChangedEvents); assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
assertReflogEquals( assertReflogEquals(
reflog(A, B, new PersonIdent(diskRepo), "fast-forward"), reflog(A, B, new PersonIdent(diskRepo), "fast-forward"),
getLastReflog("refs/heads/master")); getLastReflog("refs/heads/master"));
@ -481,6 +607,7 @@ public void reflogAppendStatusFastForward() throws IOException {
@Test @Test
public void reflogAppendStatusWithMessage() throws IOException { public void reflogAppendStatusWithMessage() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList( List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE), new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@ -489,7 +616,8 @@ public void reflogAppendStatusWithMessage() throws IOException {
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/branch", A); assertRefs("refs/heads/master", B, "refs/heads/branch", A);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
assertReflogEquals( assertReflogEquals(
reflog(A, B, new PersonIdent(diskRepo), reflog(A, B, new PersonIdent(diskRepo),
"a reflog: fast-forward"), "a reflog: fast-forward"),
@ -503,6 +631,7 @@ public void reflogAppendStatusWithMessage() throws IOException {
@Test @Test
public void reflogCustomIdent() throws IOException { public void reflogCustomIdent() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList( List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE), new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@ -513,7 +642,8 @@ public void reflogCustomIdent() throws IOException {
.setRefLogIdent(ident)); .setRefLogIdent(ident));
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
assertRefs("refs/heads/master", B, "refs/heads/branch", B); assertRefs("refs/heads/master", B, "refs/heads/branch", B);
assertReflogEquals(reflog(A, B, ident, "a reflog"), assertReflogEquals(reflog(A, B, ident, "a reflog"),
getLastReflog("refs/heads/master"), true); getLastReflog("refs/heads/master"), true);
@ -525,6 +655,7 @@ public void reflogCustomIdent() throws IOException {
public void reflogDelete() throws IOException { public void reflogDelete() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
writeRef("refs/heads/branch", A); writeRef("refs/heads/branch", A);
int initialRefsChangedEvents = refsChangedEvents;
assertEquals(2, getLastReflogs("refs/heads/master", "refs/heads/branch") assertEquals(2, getLastReflogs("refs/heads/master", "refs/heads/branch")
.size()); .size());
@ -535,7 +666,8 @@ public void reflogDelete() throws IOException {
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/branch", B); assertRefs("refs/heads/branch", B);
assertEquals(batchesRefUpdates() ? 3 : 4, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
if (useReftable) { if (useReftable) {
// reftable retains reflog entries for deleted branches. // reftable retains reflog entries for deleted branches.
assertReflogEquals( assertReflogEquals(
@ -551,6 +683,7 @@ public void reflogDelete() throws IOException {
@Test @Test
public void reflogFileDirectoryConflict() throws IOException { public void reflogFileDirectoryConflict() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList( List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE), new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE),
@ -559,7 +692,8 @@ public void reflogFileDirectoryConflict() throws IOException {
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/master/x", A); assertRefs("refs/heads/master/x", A);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
if (!useReftable) { if (!useReftable) {
// reftable retains reflog entries for deleted branches. // reftable retains reflog entries for deleted branches.
assertNull(getLastReflog("refs/heads/master")); assertNull(getLastReflog("refs/heads/master"));
@ -572,6 +706,7 @@ public void reflogFileDirectoryConflict() throws IOException {
@Test @Test
public void reflogOnLockFailure() throws IOException { public void reflogOnLockFailure() throws IOException {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master", Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
"refs/heads/branch"); "refs/heads/branch");
@ -583,12 +718,12 @@ public void reflogOnLockFailure() throws IOException {
if (atomic) { if (atomic) {
assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE); assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
assertEquals(1, refsChangedEvents); assertEquals(initialRefsChangedEvents, refsChangedEvents);
assertReflogUnchanged(oldLogs, "refs/heads/master"); assertReflogUnchanged(oldLogs, "refs/heads/master");
assertReflogUnchanged(oldLogs, "refs/heads/branch"); assertReflogUnchanged(oldLogs, "refs/heads/branch");
} else { } else {
assertResults(cmds, OK, LOCK_FAILURE); assertResults(cmds, OK, LOCK_FAILURE);
assertEquals(2, refsChangedEvents); assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
assertReflogEquals( assertReflogEquals(
reflog(A, B, new PersonIdent(diskRepo), "a reflog"), reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
getLastReflog("refs/heads/master")); getLastReflog("refs/heads/master"));
@ -599,6 +734,7 @@ public void reflogOnLockFailure() throws IOException {
@Test @Test
public void overrideRefLogMessage() throws Exception { public void overrideRefLogMessage() throws Exception {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList( List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE), new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@ -609,7 +745,8 @@ public void overrideRefLogMessage() throws Exception {
.setRefLogMessage("a reflog", true)); .setRefLogMessage("a reflog", true));
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
assertReflogEquals(reflog(A, B, ident, "custom log"), assertReflogEquals(reflog(A, B, ident, "custom log"),
getLastReflog("refs/heads/master"), true); getLastReflog("refs/heads/master"), true);
assertReflogEquals(reflog(zeroId(), B, ident, "a reflog: created"), assertReflogEquals(reflog(zeroId(), B, ident, "a reflog: created"),
@ -619,6 +756,7 @@ public void overrideRefLogMessage() throws Exception {
@Test @Test
public void overrideDisableRefLog() throws Exception { public void overrideDisableRefLog() throws Exception {
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master", Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
"refs/heads/branch"); "refs/heads/branch");
@ -630,7 +768,8 @@ public void overrideDisableRefLog() throws Exception {
execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true)); execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertEquals(batchesRefUpdates() ? 2 : 3, refsChangedEvents); assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
: initialRefsChangedEvents + 2, refsChangedEvents);
assertReflogUnchanged(oldLogs, "refs/heads/master"); assertReflogUnchanged(oldLogs, "refs/heads/master");
assertReflogEquals( assertReflogEquals(
reflog(zeroId(), B, new PersonIdent(diskRepo), reflog(zeroId(), B, new PersonIdent(diskRepo),
@ -641,6 +780,7 @@ public void overrideDisableRefLog() throws Exception {
@Test @Test
public void refLogNotWrittenWithoutConfigOption() throws Exception { public void refLogNotWrittenWithoutConfigOption() throws Exception {
assumeFalse(useReftable); assumeFalse(useReftable);
setLogAllRefUpdates(false); setLogAllRefUpdates(false);
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
@ -661,6 +801,7 @@ public void refLogNotWrittenWithoutConfigOption() throws Exception {
@Test @Test
public void forceRefLogInUpdate() throws Exception { public void forceRefLogInUpdate() throws Exception {
assumeFalse(useReftable); assumeFalse(useReftable);
setLogAllRefUpdates(false); setLogAllRefUpdates(false);
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
assertTrue(getLastReflogs("refs/heads/master", "refs/heads/branch") assertTrue(getLastReflogs("refs/heads/master", "refs/heads/branch")
@ -683,6 +824,7 @@ public void forceRefLogInUpdate() throws Exception {
@Test @Test
public void forceRefLogInCommand() throws Exception { public void forceRefLogInCommand() throws Exception {
assumeFalse(useReftable); assumeFalse(useReftable);
setLogAllRefUpdates(false); setLogAllRefUpdates(false);
writeRef("refs/heads/master", A); writeRef("refs/heads/master", A);
@ -723,19 +865,39 @@ public void packedRefsLockFailure() throws Exception {
if (atomic) { if (atomic) {
assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED); assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(1, refsChangedEvents);
} else { } else {
// Only operates on loose refs, doesn't care that packed-refs is // Only operates on loose refs, doesn't care that packed-refs is
// locked. // locked.
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertRefs("refs/heads/master", B, "refs/heads/branch", B); assertRefs("refs/heads/master", B, "refs/heads/branch", B);
assertEquals(3, refsChangedEvents);
} }
} finally { } finally {
myLock.unlock(); myLock.unlock();
} }
} }
@Test
public void packedRefsLockFailureRefsChangedEvents() throws Exception {
assumeFalse(useReftable);
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
LockFile myLock = refdir.lockPackedRefs();
try {
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 2, refsChangedEvents);
} finally {
myLock.unlock();
}
}
@Test @Test
public void oneRefLockFailure() throws Exception { public void oneRefLockFailure() throws Exception {
assumeFalse(useReftable); assumeFalse(useReftable);
@ -757,20 +919,42 @@ public void oneRefLockFailure() throws Exception {
if (atomic) { if (atomic) {
assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE); assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE);
assertRefs("refs/heads/master", A); assertRefs("refs/heads/master", A);
assertEquals(1, refsChangedEvents);
} else { } else {
assertResults(cmds, OK, LOCK_FAILURE); assertResults(cmds, OK, LOCK_FAILURE);
assertRefs("refs/heads/branch", B, "refs/heads/master", A); assertRefs("refs/heads/branch", B, "refs/heads/master", A);
assertEquals(2, refsChangedEvents);
} }
} finally { } finally {
myLock.unlock(); myLock.unlock();
} }
} }
@Test
public void oneRefLockFailureRefsChangedEvents() throws Exception {
assumeFalse(useReftable);
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE),
new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
LockFile myLock = new LockFile(refdir.fileFor("refs/heads/master"));
assertTrue(myLock.lock());
try {
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(atomic ? initialRefsChangedEvents
: initialRefsChangedEvents + 1, refsChangedEvents);
} finally {
myLock.unlock();
}
}
@Test @Test
public void singleRefUpdateDoesNotRequirePackedRefsLock() throws Exception { public void singleRefUpdateDoesNotRequirePackedRefsLock() throws Exception {
assumeFalse(useReftable); assumeFalse(useReftable);
writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/master", A);
List<ReceiveCommand> cmds = Arrays List<ReceiveCommand> cmds = Arrays
@ -782,13 +966,33 @@ public void singleRefUpdateDoesNotRequirePackedRefsLock() throws Exception {
assertFalse(getLockFile("refs/heads/master").exists()); assertFalse(getLockFile("refs/heads/master").exists());
assertResults(cmds, OK); assertResults(cmds, OK);
assertEquals(2, refsChangedEvents);
assertRefs("refs/heads/master", B); assertRefs("refs/heads/master", B);
} finally { } finally {
myLock.unlock(); myLock.unlock();
} }
} }
@Test
public void singleRefUpdateDoesNotRequirePackedRefsLockRefsChangedEvents()
throws Exception {
assumeFalse(useReftable);
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays
.asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
LockFile myLock = refdir.lockPackedRefs();
try {
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
} finally {
myLock.unlock();
}
}
@Test @Test
public void atomicUpdateRespectsInProcessLock() throws Exception { public void atomicUpdateRespectsInProcessLock() throws Exception {
assumeTrue(atomic); assumeTrue(atomic);
@ -838,10 +1042,56 @@ public void atomicUpdateRespectsInProcessLock() throws Exception {
} }
assertResults(cmds, OK, OK); assertResults(cmds, OK, OK);
assertEquals(2, refsChangedEvents);
assertRefs("refs/heads/master", B, "refs/heads/branch", B); assertRefs("refs/heads/master", B, "refs/heads/branch", B);
} }
@Test
public void atomicUpdateRespectsInProcessLockRefsChangedEvents()
throws Exception {
assumeTrue(atomic);
assumeFalse(useReftable);
writeLooseRef("refs/heads/master", A);
int initialRefsChangedEvents = refsChangedEvents;
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
Thread t = new Thread(() -> {
try {
execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
ReentrantLock l = refdir.inProcessPackedRefsLock;
l.lock();
try {
t.start();
long timeoutSecs = 10;
// Hold onto the lock until we observe the worker thread has
// attempted to
// acquire it.
while (l.getQueueLength() == 0) {
Thread.sleep(3);
}
// Once we unlock, the worker thread should finish the update
// promptly.
l.unlock();
t.join(SECONDS.toMillis(timeoutSecs));
} finally {
if (l.isHeldByCurrentThread()) {
l.unlock();
}
}
assertEquals(initialRefsChangedEvents + 1, refsChangedEvents);
}
private void setLogAllRefUpdates(boolean enable) throws Exception { private void setLogAllRefUpdates(boolean enable) throws Exception {
StoredConfig cfg = diskRepo.getConfig(); StoredConfig cfg = diskRepo.getConfig();
cfg.load(); cfg.load();
@ -855,6 +1105,11 @@ private void writeLooseRef(String name, AnyObjectId id) throws IOException {
writeRef(name, id); writeRef(name, id);
} else { } else {
write(new File(diskRepo.getDirectory(), name), id.name() + "\n"); write(new File(diskRepo.getDirectory(), name), id.name() + "\n");
// force the refs-changed event to be fired for the loose ref that
// was created. We do this to get the events fired during the test
// 'setup' out of the way and this allows us to now accurately
// assert only for the new events fired during the BatchRefUpdate.
refdir.exactRef(name);
} }
} }
@ -957,11 +1212,11 @@ private void assertRefs(Object... args) throws IOException {
} }
enum Result { enum Result {
OK(ReceiveCommand.Result.OK), LOCK_FAILURE( OK(ReceiveCommand.Result.OK),
ReceiveCommand.Result.LOCK_FAILURE), REJECTED_NONFASTFORWARD( LOCK_FAILURE(ReceiveCommand.Result.LOCK_FAILURE),
ReceiveCommand.Result.REJECTED_NONFASTFORWARD), REJECTED_MISSING_OBJECT( REJECTED_NONFASTFORWARD(ReceiveCommand.Result.REJECTED_NONFASTFORWARD),
ReceiveCommand.Result.REJECTED_MISSING_OBJECT), TRANSACTION_ABORTED( REJECTED_MISSING_OBJECT(ReceiveCommand.Result.REJECTED_MISSING_OBJECT),
ReceiveCommand::isTransactionAborted); TRANSACTION_ABORTED(ReceiveCommand::isTransactionAborted);
@SuppressWarnings("ImmutableEnumChecker") @SuppressWarnings("ImmutableEnumChecker")
final Predicate<? super ReceiveCommand> p; final Predicate<? super ReceiveCommand> p;

View File

@ -244,47 +244,18 @@ public void refresh() {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public boolean isNameConflicting(String name) throws IOException { public boolean isNameConflicting(String name) throws IOException {
RefList<Ref> packed = getPackedRefs();
RefList<LooseRef> loose = getLooseRefs();
// Cannot be nested within an existing reference. // Cannot be nested within an existing reference.
int lastSlash = name.lastIndexOf('/'); int lastSlash = name.lastIndexOf('/');
while (0 < lastSlash) { while (0 < lastSlash) {
String needle = name.substring(0, lastSlash); String needle = name.substring(0, lastSlash);
if (loose.contains(needle) || packed.contains(needle)) if (exactRef(needle) != null) {
return true; return true;
}
lastSlash = name.lastIndexOf('/', lastSlash - 1); lastSlash = name.lastIndexOf('/', lastSlash - 1);
} }
// Cannot be the container of an existing reference. // Cannot be the container of an existing reference.
String prefix = name + '/'; return !getRefsByPrefix(name + '/').isEmpty();
int idx;
idx = -(packed.find(prefix) + 1);
if (idx < packed.size() && packed.get(idx).getName().startsWith(prefix))
return true;
idx = -(loose.find(prefix) + 1);
if (idx < loose.size() && loose.get(idx).getName().startsWith(prefix))
return true;
return false;
}
private RefList<LooseRef> getLooseRefs() {
final RefList<LooseRef> oldLoose = looseRefs.get();
LooseScanner scan = new LooseScanner(oldLoose);
scan.scan(ALL);
RefList<LooseRef> loose;
if (scan.newLoose != null) {
loose = scan.newLoose.toRefList();
if (looseRefs.compareAndSet(oldLoose, loose))
modCnt.incrementAndGet();
} else
loose = oldLoose;
return loose;
} }
@Nullable @Nullable

View File

@ -13,7 +13,6 @@
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED; import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON; import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
import static java.util.stream.Collectors.toCollection;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -29,7 +28,6 @@
import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushCertificate; import org.eclipse.jgit.transport.PushCertificate;
import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.ReceiveCommand;
@ -495,42 +493,24 @@ public void execute(RevWalk walk, ProgressMonitor monitor,
} }
} }
if (!commands2.isEmpty()) { if (!commands2.isEmpty()) {
// What part of the name space is already taken // Perform updates that may require more room in the name space
Collection<String> takenNames = refdb.getRefs().stream()
.map(Ref::getName)
.collect(toCollection(HashSet::new));
Collection<String> takenPrefixes = getTakenPrefixes(takenNames);
// Now to the update that may require more room in the name space
for (ReceiveCommand cmd : commands2) { for (ReceiveCommand cmd : commands2) {
try { try {
if (cmd.getResult() == NOT_ATTEMPTED) { if (cmd.getResult() == NOT_ATTEMPTED) {
cmd.updateType(walk); cmd.updateType(walk);
RefUpdate ru = newUpdate(cmd); RefUpdate ru = newUpdate(cmd);
SWITCH: switch (cmd.getType()) { switch (cmd.getType()) {
case DELETE: case DELETE:
// Performed in the first phase // Performed in the first phase
break; break;
case UPDATE: case UPDATE:
case UPDATE_NONFASTFORWARD: case UPDATE_NONFASTFORWARD:
RefUpdate ruu = newUpdate(cmd); RefUpdate ruu = newUpdate(cmd);
cmd.setResult(ruu.update(walk)); cmd.setResult(ruu.update(walk));
break; break;
case CREATE: case CREATE:
for (String prefix : getPrefixes(cmd.getRefName())) { cmd.setResult(ru.update(walk));
if (takenNames.contains(prefix)) { break;
cmd.setResult(Result.LOCK_FAILURE);
break SWITCH;
}
}
if (takenPrefixes.contains(cmd.getRefName())) {
cmd.setResult(Result.LOCK_FAILURE);
break SWITCH;
}
ru.setCheckConflicting(false);
takenPrefixes.addAll(getPrefixes(cmd.getRefName()));
takenNames.add(cmd.getRefName());
cmd.setResult(ru.update(walk));
} }
} }
} catch (IOException err) { } catch (IOException err) {
@ -602,14 +582,6 @@ public void execute(RevWalk walk, ProgressMonitor monitor)
execute(walk, monitor, null); execute(walk, monitor, null);
} }
private static Collection<String> getTakenPrefixes(Collection<String> names) {
Collection<String> ref = new HashSet<>();
for (String name : names) {
addPrefixesTo(name, ref);
}
return ref;
}
/** /**
* Get all path prefixes of a ref name. * Get all path prefixes of a ref name.
* *