BatchUpdate: Support chaining ReceiveCommands on the same ref

This is not natively supported by BatchRefUpdate: if multiple commands
for the same ref are added to a batch, the behavior is undefined. To
support this, buffer commands in a temporary object, and ensure the
old and new IDs line up.

Change-Id: I60a55fa3ca9106dbadc1d3a1546bf1ee5c6fe37b
This commit is contained in:
Dave Borowitz
2016-01-11 15:48:25 -05:00
parent 01cfa56a73
commit 224db94249

View File

@@ -49,6 +49,7 @@ import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@@ -146,16 +147,8 @@ public class BatchUpdate implements AutoCloseable {
return BatchUpdate.this.getObjectInserter();
}
private BatchRefUpdate getBatchRefUpdate() throws IOException {
initRepository();
if (batchRefUpdate == null) {
batchRefUpdate = repo.getRefDatabase().newBatchUpdate();
}
return batchRefUpdate;
}
public void addRefUpdate(ReceiveCommand cmd) throws IOException {
getBatchRefUpdate().addCommand(cmd);
public void addRefUpdate(ReceiveCommand cmd) {
commands.add(cmd);
}
public TimeZone getTimeZone() {
@@ -213,6 +206,40 @@ public class BatchUpdate implements AutoCloseable {
public abstract Change getChange();
}
private static class ChainedReceiveCommands {
private final Map<String, ReceiveCommand> commands = new LinkedHashMap<>();
private boolean isEmpty() {
return commands.isEmpty();
}
private void add(ReceiveCommand cmd) {
checkArgument(!cmd.getOldId().equals(cmd.getNewId()),
"ref update is a no-op: %s", cmd);
ReceiveCommand old = commands.get(cmd.getRefName());
if (old == null) {
commands.put(cmd.getRefName(), cmd);
return;
}
checkArgument(old.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED,
"cannot chain ref update %s after update %s with result %s",
cmd, old, old.getResult());
checkArgument(cmd.getOldId().equals(old.getNewId()),
"cannot chain ref update %s after update %s with different new ID",
cmd, old);
commands.put(cmd.getRefName(), new ReceiveCommand(
old.getOldId(), cmd.getNewId(), cmd.getRefName()));
}
private void addTo(BatchRefUpdate bru) {
checkState(!isEmpty(), "no commands to add");
for (ReceiveCommand cmd : commands.values()) {
bru.addCommand(cmd);
}
}
}
private final ReviewDb db;
private final GitRepositoryManager repoManager;
private final ChangeIndexer indexer;
@@ -233,6 +260,7 @@ public class BatchUpdate implements AutoCloseable {
private Repository repo;
private ObjectInserter inserter;
private RevWalk revWalk;
private ChainedReceiveCommands commands = new ChainedReceiveCommands();
private BatchRefUpdate batchRefUpdate;
private boolean closeRepo;
private Order order;
@@ -376,11 +404,14 @@ public class BatchUpdate implements AutoCloseable {
throw new UpdateException(e);
}
if (repo == null || batchRefUpdate == null
|| batchRefUpdate.getCommands().isEmpty()) {
if (commands.isEmpty()) {
return;
}
// May not be opened if the caller added ref updates but no new objects.
initRepository();
inserter.flush();
batchRefUpdate = repo.getRefDatabase().newBatchUpdate();
commands.addTo(batchRefUpdate);
batchRefUpdate.execute(revWalk, NullProgressMonitor.INSTANCE);
boolean ok = true;
for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {