summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorDave Borowitz <dborowitz@google.com>2015-07-13 12:03:42 -0700
committerDave Borowitz <dborowitz@google.com>2015-07-15 18:09:41 -0700
commit5706c8e38e501bb1f9721167ca6cbcc169df8b10 (patch)
tree2229ca975a6d222bd3ace282cb7a15fd2a26b4ce /org.eclipse.jgit
parent16ba9919dbb8802c6566dfda3b5bc02a162ec4a4 (diff)
downloadjgit-5706c8e38e501bb1f9721167ca6cbcc169df8b10.tar.gz
jgit-5706c8e38e501bb1f9721167ca6cbcc169df8b10.zip
Allow saving push certs on a subset of refs
Consider a BatchRefUpdate produced by Gerrit Code Review, where the original command pushed over the wire might refer to "refs/for/master", but that command is ignored and replaced with some additional commands like creating "refs/changes/34/1234/1". We do not want to store the cert in "refs/for/master@{cert}", since that may lead someone looking to the ref to the incorrect conclusion that that ref exists. Add a separate put method that takes a collection of commands, and only stores certs on those refs that have a matching command in the cert. Change-Id: I4661bfe2ead28a2883b33a4e3dfe579b3157d68a
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java71
1 files changed, 67 insertions, 4 deletions
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 43346205fd..9b524def66 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -55,10 +55,13 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import org.eclipse.jgit.dircache.DirCache;
@@ -106,10 +109,13 @@ public class PushCertificateStore implements AutoCloseable {
private static class PendingCert {
private PushCertificate cert;
private PersonIdent ident;
+ private Collection<ReceiveCommand> matching;
- private PendingCert(PushCertificate cert, PersonIdent ident) {
+ private PendingCert(PushCertificate cert, PersonIdent ident,
+ Collection<ReceiveCommand> matching) {
this.cert = cert;
this.ident = ident;
+ this.matching = matching;
}
}
@@ -308,7 +314,34 @@ public class PushCertificateStore implements AutoCloseable {
* #save()}.
*/
public void put(PushCertificate cert, PersonIdent ident) {
- pending.add(new PendingCert(cert, ident));
+ put(cert, ident, null);
+ }
+
+ /**
+ * Put a certificate to be saved to the store, matching a set of commands.
+ * <p>
+ * Like {@link #put(PushCertificate, PersonIdent)}, except a value is only
+ * stored for a push certificate if there is a corresponding command in the
+ * list that exactly matches the old/new values mentioned in the push
+ * certificate.
+ * <p>
+ * Pending certificates added to this method are not returned by {@link
+ * #get(String)} and {@link #getAll(String)} until after calling {@link
+ * #save()}.
+ *
+ * @param cert
+ * certificate to store.
+ * @param ident
+ * identity for the commit that stores this certificate. Pending
+ * certificates are sorted by identity timestamp during {@link
+ * #save()}.
+ * @param matching
+ * only store certs for the refs listed in this list whose values
+ * match the commands in the cert.
+ */
+ public void put(PushCertificate cert, PersonIdent ident,
+ Collection<ReceiveCommand> matching) {
+ pending.add(new PendingCert(cert, ident, matching));
}
/**
@@ -355,8 +388,9 @@ public class PushCertificateStore implements AutoCloseable {
* #put(PushCertificate, PersonIdent)}, in order of identity timestamps, all
* commits are flushed, and a single command is added to the batch.
* <p>
- * The pending list is <em>not</em> cleared. If the ref update succeeds, the
- * caller is responsible for calling {@link #clear()}.
+ * The cached ref value and pending list are <em>not</em> cleared. If the ref
+ * update succeeds, the caller is responsible for calling {@link #close()}
+ * and/or {@link #clear()}.
*
* @param batch
* update to save to.
@@ -423,10 +457,27 @@ public class PushCertificateStore implements AutoCloseable {
private ObjectId saveCert(ObjectInserter inserter, DirCache dc,
PendingCert pc, ObjectId curr) throws IOException {
+ Map<String, ReceiveCommand> byRef;
+ if (pc.matching != null) {
+ byRef = new HashMap<>();
+ for (ReceiveCommand cmd : pc.matching) {
+ if (byRef.put(cmd.getRefName(), cmd) != null) {
+ throw new IllegalStateException();
+ }
+ }
+ } else {
+ byRef = null;
+ }
+
DirCacheEditor editor = dc.editor();
String certText = pc.cert.toText() + pc.cert.getSignature();
final ObjectId certId = inserter.insert(OBJ_BLOB, certText.getBytes(UTF_8));
+ boolean any = false;
for (ReceiveCommand cmd : pc.cert.getCommands()) {
+ if (byRef != null && !commandsEqual(cmd, byRef.get(cmd.getRefName()))) {
+ continue;
+ }
+ any = true;
editor.add(new PathEdit(pathName(cmd.getRefName())) {
@Override
public void apply(DirCacheEntry ent) {
@@ -435,6 +486,9 @@ public class PushCertificateStore implements AutoCloseable {
}
});
}
+ if (!any) {
+ return curr;
+ }
editor.finish();
CommitBuilder cb = new CommitBuilder();
cb.setAuthor(pc.ident);
@@ -449,6 +503,15 @@ public class PushCertificateStore implements AutoCloseable {
return inserter.insert(OBJ_COMMIT, cb.build());
}
+ private static boolean commandsEqual(ReceiveCommand c1, ReceiveCommand c2) {
+ if (c1 == null || c2 == null) {
+ return c1 == c2;
+ }
+ return c1.getRefName().equals(c2.getRefName())
+ && c1.getOldId().equals(c2.getOldId())
+ && c1.getNewId().equals(c2.getNewId());
+ }
+
private RefUpdate.Result updateRef(ObjectId newId) throws IOException {
RefUpdate ru = db.updateRef(REF_NAME);
ru.setExpectedOldObjectId(commit != null ? commit : ObjectId.zeroId());