Browse Source

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>
stable-5.0
Jonathan Tan 7 years ago committed by Jonathan Nieder
parent
commit
332bc61124
  1. 81
      org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
  2. 7
      org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
  3. 38
      org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
  4. 35
      org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java

81
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.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy; import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
@ -368,6 +369,7 @@ public class UploadPackTest {
// capability advertisement (always sent) // capability advertisement (always sent)
assertThat(pckIn.readString(), is("version 2")); assertThat(pckIn.readString(), is("version 2"));
assertThat(pckIn.readString(), is("ls-refs"));
assertTrue(pckIn.readString() == PacketLineIn.END); assertTrue(pckIn.readString() == PacketLineIn.END);
return recvStream; return recvStream;
} }
@ -380,4 +382,83 @@ public class UploadPackTest {
assertThat(recvStream.available(), is(0)); 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);
}
} }

7
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$ 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 { static enum MultiAck {
OFF, CONTINUE, DETAILED; OFF, CONTINUE, DETAILED;
} }

38
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.CharacterCodingException;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult; import java.nio.charset.CoderResult;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
@ -176,6 +177,11 @@ public abstract class RefAdvertiser {
boolean first = true; 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. * Initialize this advertiser with a repository for peeling tags.
* *
@ -186,6 +192,15 @@ public abstract class RefAdvertiser {
repository = src; repository = src;
} }
/**
* @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. * Toggle tag peeling.
* <p> * <p>
@ -253,7 +268,11 @@ public abstract class RefAdvertiser {
* @since 3.6 * @since 3.6
*/ */
public void addSymref(String from, String to) { 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) if (ref.getObjectId() == null)
continue; 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()); advertiseAny(ref.getObjectId(), ref.getName());
if (!derefTags) if (!derefTags)

35
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java

@ -44,6 +44,7 @@
package org.eclipse.jgit.transport; package org.eclipse.jgit.transport;
import static org.eclipse.jgit.lib.RefDatabase.ALL; 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_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT; 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; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
@ -117,6 +118,7 @@ public class UploadPack {
// supports protocol version 2. // supports protocol version 2.
private static final String[] v2CapabilityAdvertisement = { private static final String[] v2CapabilityAdvertisement = {
"version 2", "version 2",
COMMAND_LS_REFS
}; };
/** Policy the server uses to validate client requests */ /** Policy the server uses to validate client requests */
@ -864,6 +866,35 @@ public class UploadPack {
sendPack(accumulator); 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 * Returns true if this is the last command and we should tear down the
* connection. * connection.
@ -882,6 +913,10 @@ public class UploadPack {
// case. // case.
return true; return true;
} }
if (command.equals("command=" + COMMAND_LS_REFS)) {
lsRefsV2();
return false;
}
throw new PackProtocolException("unknown command " + command); throw new PackProtocolException("unknown command " + command);
} }

Loading…
Cancel
Save