summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Tan <jonathantanmy@google.com>2018-02-22 10:24:19 -0800
committerJonathan Nieder <jrn@google.com>2018-04-23 10:26:51 -0700
commit332bc611249d21f9b604f2c0207bf0bdfbfc3a78 (patch)
tree7238a842e2c4481c0ebe279d30f1a8caa0177a5b
parent2661bc081340ae83d2a2ecba11994d3e8d56586b (diff)
downloadjgit-332bc611249d21f9b604f2c0207bf0bdfbfc3a78.tar.gz
jgit-332bc611249d21f9b604f2c0207bf0bdfbfc3a78.zip
Implement ls-refs in UploadPack
Implement support for Git protocol v2's "ls-refs" command and its "symrefs" and "peel" parameters. This adds support for this command to UploadPack but the git://, ssh://, and git:// transports do not make use of it yet. That will have to wait for later patches. Change-Id: I8abc6bcc6ed4a88c165677ff1245625aca01267b Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Jonathan Nieder <jrn@google.com>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java35
4 files changed, 160 insertions, 1 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 88060c05a0..88ac49a51b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -19,6 +19,7 @@ import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
@@ -368,6 +369,7 @@ public class UploadPackTest {
// capability advertisement (always sent)
assertThat(pckIn.readString(), is("version 2"));
+ assertThat(pckIn.readString(), is("ls-refs"));
assertTrue(pckIn.readString() == PacketLineIn.END);
return recvStream;
}
@@ -380,4 +382,83 @@ public class UploadPackTest {
assertThat(recvStream.available(), is(0));
}
+ @Test
+ public void testV2LsRefs() throws Exception {
+ RevCommit tip = remote.commit().message("message").create();
+ remote.update("master", tip);
+ server.updateRef("HEAD").link("refs/heads/master");
+ RevTag tag = remote.tag("tag", tip);
+ remote.update("refs/tags/tag", tag);
+
+ ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
+ assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
+ assertTrue(pckIn.readString() == PacketLineIn.END);
+ }
+
+ @Test
+ public void testV2LsRefsSymrefs() throws Exception {
+ RevCommit tip = remote.commit().message("message").create();
+ remote.update("master", tip);
+ server.updateRef("HEAD").link("refs/heads/master");
+ RevTag tag = remote.tag("tag", tip);
+ remote.update("refs/tags/tag", tag);
+
+ ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", PacketLineIn.DELIM, "symrefs", PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master"));
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
+ assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
+ assertTrue(pckIn.readString() == PacketLineIn.END);
+ }
+
+ @Test
+ public void testV2LsRefsPeel() throws Exception {
+ RevCommit tip = remote.commit().message("message").create();
+ remote.update("master", tip);
+ server.updateRef("HEAD").link("refs/heads/master");
+ RevTag tag = remote.tag("tag", tip);
+ remote.update("refs/tags/tag", tag);
+
+ ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", PacketLineIn.DELIM, "peel", PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
+ assertThat(
+ pckIn.readString(),
+ is(tag.toObjectId().getName() + " refs/tags/tag peeled:"
+ + tip.toObjectId().getName()));
+ assertTrue(pckIn.readString() == PacketLineIn.END);
+ }
+
+ @Test
+ public void testV2LsRefsMultipleCommands() throws Exception {
+ RevCommit tip = remote.commit().message("message").create();
+ remote.update("master", tip);
+ server.updateRef("HEAD").link("refs/heads/master");
+ RevTag tag = remote.tag("tag", tip);
+ remote.update("refs/tags/tag", tag);
+
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=ls-refs\n", PacketLineIn.DELIM, "symrefs", "peel", PacketLineIn.END,
+ "command=ls-refs\n", PacketLineIn.DELIM, PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master"));
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
+ assertThat(
+ pckIn.readString(),
+ is(tag.toObjectId().getName() + " refs/tags/tag peeled:"
+ + tip.toObjectId().getName()));
+ assertTrue(pckIn.readString() == PacketLineIn.END);
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
+ assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
+ assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
+ assertTrue(pckIn.readString() == PacketLineIn.END);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index 6d39dcd8a7..ccefb51684 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -222,6 +222,13 @@ public class GitProtocolConstants {
*/
public static final String CAPABILITY_PUSH_OPTIONS = "push-options"; //$NON-NLS-1$
+ /**
+ * The server supports listing refs using protocol v2.
+ *
+ * @since 5.0
+ */
+ public static final String COMMAND_LS_REFS = "ls-refs"; //$NON-NLS-1$
+
static enum MultiAck {
OFF, CONTINUE, DETAILED;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 6ad39e3ddd..0257ebec63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -53,6 +53,7 @@ import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -176,6 +177,11 @@ public abstract class RefAdvertiser {
boolean first = true;
+ private boolean useProtocolV2;
+
+ /* only used in protocol v2 */
+ private final Map<String, String> symrefs = new HashMap<>();
+
/**
* Initialize this advertiser with a repository for peeling tags.
*
@@ -187,6 +193,15 @@ public abstract class RefAdvertiser {
}
/**
+ * @param b
+ * true if this advertiser should advertise using the
+ * protocol v2 format, false otherwise
+ */
+ public void setUseProtocolV2(boolean b) {
+ useProtocolV2 = b;
+ }
+
+ /**
* Toggle tag peeling.
* <p>
* <p>
@@ -253,7 +268,11 @@ public abstract class RefAdvertiser {
* @since 3.6
*/
public void addSymref(String from, String to) {
- advertiseCapability(OPTION_SYMREF, from + ':' + to);
+ if (useProtocolV2) {
+ symrefs.put(from, to);
+ } else {
+ advertiseCapability(OPTION_SYMREF, from + ':' + to);
+ }
}
/**
@@ -273,6 +292,23 @@ public abstract class RefAdvertiser {
if (ref.getObjectId() == null)
continue;
+ if (useProtocolV2) {
+ String symrefPart = symrefs.containsKey(ref.getName())
+ ? (" symref-target:" + symrefs.get(ref.getName()))
+ : "";
+ String peelPart = "";
+ if (derefTags) {
+ if (!ref.isPeeled() && repository != null) {
+ ref = repository.peel(ref);
+ }
+ if (ref.getPeeledObjectId() != null) {
+ peelPart = " peeled:" + ref.getPeeledObjectId().getName();
+ }
+ }
+ writeOne(ref.getObjectId().getName() + " " + ref.getName() + symrefPart + peelPart + "\n");
+ continue;
+ }
+
advertiseAny(ref.getObjectId(), ref.getName());
if (!derefTags)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 7f6182bf9b..b2b346a6b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.transport;
import static org.eclipse.jgit.lib.RefDatabase.ALL;
+import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
@@ -117,6 +118,7 @@ public class UploadPack {
// supports protocol version 2.
private static final String[] v2CapabilityAdvertisement = {
"version 2",
+ COMMAND_LS_REFS
};
/** Policy the server uses to validate client requests */
@@ -864,6 +866,35 @@ public class UploadPack {
sendPack(accumulator);
}
+ private void lsRefsV2() throws IOException {
+ PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
+ Map<String, Ref> refs = getAdvertisedOrDefaultRefs();
+ String line;
+
+ adv.setUseProtocolV2(true);
+
+ line = pckIn.readString();
+
+ // Currently, we do not support any capabilities, so the next
+ // line is DELIM if there are arguments or END if not.
+ if (line == PacketLineIn.DELIM) {
+ while ((line = pckIn.readString()) != PacketLineIn.END) {
+ if (line.equals("peel")) {
+ adv.setDerefTags(true);
+ } else if (line.equals("symrefs")) {
+ findSymrefs(adv, refs);
+ } else {
+ throw new PackProtocolException("unexpected " + line);
+ }
+ }
+ } else if (line != PacketLineIn.END) {
+ throw new PackProtocolException("unexpected " + line);
+ }
+
+ adv.send(refs);
+ adv.end();
+ }
+
/*
* Returns true if this is the last command and we should tear down the
* connection.
@@ -882,6 +913,10 @@ public class UploadPack {
// case.
return true;
}
+ if (command.equals("command=" + COMMAND_LS_REFS)) {
+ lsRefsV2();
+ return false;
+ }
throw new PackProtocolException("unknown command " + command);
}