diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java index d2faaa774..77a0708e1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java @@ -60,7 +60,9 @@ import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; @@ -271,6 +273,26 @@ public class PushCertificateStoreTest { assertCerts(store2, "refs/heads/branch", addBranch); } + @Test + public void saveInBatch() throws Exception { + BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate(); + PushCertificate addMaster = newCert( + command(zeroId(), ID1, "refs/heads/master")); + store.put(addMaster, newIdent()); + store.save(batch); + + List commands = batch.getCommands(); + assertEquals(1, commands.size()); + ReceiveCommand cmd = commands.get(0); + + try (RevWalk rw = new RevWalk(repo)) { + assertEquals("refs/meta/push-certs", cmd.getRefName()); + assertEquals(ReceiveCommand.Result.NOT_ATTEMPTED, cmd.getResult()); + batch.execute(rw, NullProgressMonitor.INSTANCE); + assertEquals(ReceiveCommand.Result.OK, cmd.getResult()); + } + } + private PersonIdent newIdent() { return new PersonIdent( "A U. Thor", "author@example.com", ts.getAndIncrement(), 0); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java index 94677f9c4..43346205f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java @@ -67,6 +67,7 @@ import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; @@ -326,22 +327,12 @@ public class PushCertificateStore implements AutoCloseable { * repository. */ public RefUpdate.Result save() throws IOException { - if (pending.isEmpty()) { + ObjectId newId = write(); + if (newId == null) { return RefUpdate.Result.NO_CHANGE; } - if (reader == null) { - load(); - } - sortPending(pending); - - ObjectId curr = commit; - DirCache dc = newDirCache(); try (ObjectInserter inserter = db.newObjectInserter()) { - for (PendingCert pc : pending) { - curr = saveCert(inserter, dc, pc, curr); - } - inserter.flush(); - RefUpdate.Result result = updateRef(curr); + RefUpdate.Result result = updateRef(newId); switch (result) { case FAST_FORWARD: case NEW: @@ -357,6 +348,59 @@ public class PushCertificateStore implements AutoCloseable { } } + /** + * Save pending certificates to the store in an existing batch ref update. + *

+ * One commit is created per certificate added with {@link + * #put(PushCertificate, PersonIdent)}, in order of identity timestamps, all + * commits are flushed, and a single command is added to the batch. + *

+ * The pending list is not cleared. If the ref update succeeds, the + * caller is responsible for calling {@link #clear()}. + * + * @param batch + * update to save to. + * @throws IOException + * if there was an error reading from or writing to the + * repository. + */ + public void save(BatchRefUpdate batch) throws IOException { + ObjectId newId = write(); + if (newId == null) { + return; + } + batch.addCommand(new ReceiveCommand( + commit != null ? commit : ObjectId.zeroId(), newId, REF_NAME)); + } + + /** + * Clear pending certificates added with {@link #put(PushCertificate, + * PersonIdent)}. + */ + public void clear() { + pending.clear(); + } + + private ObjectId write() throws IOException { + if (pending.isEmpty()) { + return null; + } + if (reader == null) { + load(); + } + sortPending(pending); + + ObjectId curr = commit; + DirCache dc = newDirCache(); + try (ObjectInserter inserter = db.newObjectInserter()) { + for (PendingCert pc : pending) { + curr = saveCert(inserter, dc, pc, curr); + } + inserter.flush(); + return curr; + } + } + private static void sortPending(List pending) { Collections.sort(pending, new Comparator() { @Override