connect: request remote refs using v2
Teach the client to be able to request a remote's refs using protocol v2. This is done by having a client issue a 'ls-refs' request to a v2 server. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
72d0ea0056
commit
e52449b672
138
connect.c
138
connect.c
@ -12,9 +12,11 @@
|
||||
#include "sha1-array.h"
|
||||
#include "transport.h"
|
||||
#include "strbuf.h"
|
||||
#include "version.h"
|
||||
#include "protocol.h"
|
||||
|
||||
static char *server_capabilities;
|
||||
static char *server_capabilities_v1;
|
||||
static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT;
|
||||
static const char *parse_feature_value(const char *, const char *, int *);
|
||||
|
||||
static int check_ref(const char *name, unsigned int flags)
|
||||
@ -62,6 +64,33 @@ static void die_initial_contact(int unexpected)
|
||||
"and the repository exists."));
|
||||
}
|
||||
|
||||
/* Checks if the server supports the capability 'c' */
|
||||
int server_supports_v2(const char *c, int die_on_error)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < server_capabilities_v2.argc; i++) {
|
||||
const char *out;
|
||||
if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
|
||||
(!*out || *out == '='))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (die_on_error)
|
||||
die("server doesn't support '%s'", c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void process_capabilities_v2(struct packet_reader *reader)
|
||||
{
|
||||
while (packet_reader_read(reader) == PACKET_READ_NORMAL)
|
||||
argv_array_push(&server_capabilities_v2, reader->line);
|
||||
|
||||
if (reader->status != PACKET_READ_FLUSH)
|
||||
die("expected flush after capabilities");
|
||||
}
|
||||
|
||||
enum protocol_version discover_version(struct packet_reader *reader)
|
||||
{
|
||||
enum protocol_version version = protocol_unknown_version;
|
||||
@ -84,7 +113,7 @@ enum protocol_version discover_version(struct packet_reader *reader)
|
||||
|
||||
switch (version) {
|
||||
case protocol_v2:
|
||||
die("support for protocol v2 not implemented yet");
|
||||
process_capabilities_v2(reader);
|
||||
break;
|
||||
case protocol_v1:
|
||||
/* Read the peeked version line */
|
||||
@ -128,7 +157,7 @@ reject:
|
||||
static void annotate_refs_with_symref_info(struct ref *ref)
|
||||
{
|
||||
struct string_list symref = STRING_LIST_INIT_DUP;
|
||||
const char *feature_list = server_capabilities;
|
||||
const char *feature_list = server_capabilities_v1;
|
||||
|
||||
while (feature_list) {
|
||||
int len;
|
||||
@ -157,7 +186,7 @@ static void process_capabilities(const char *line, int *len)
|
||||
int nul_location = strlen(line);
|
||||
if (nul_location == *len)
|
||||
return;
|
||||
server_capabilities = xstrdup(line + nul_location + 1);
|
||||
server_capabilities_v1 = xstrdup(line + nul_location + 1);
|
||||
*len = nul_location;
|
||||
}
|
||||
|
||||
@ -292,6 +321,105 @@ struct ref **get_remote_heads(struct packet_reader *reader,
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
|
||||
static int process_ref_v2(const char *line, struct ref ***list)
|
||||
{
|
||||
int ret = 1;
|
||||
int i = 0;
|
||||
struct object_id old_oid;
|
||||
struct ref *ref;
|
||||
struct string_list line_sections = STRING_LIST_INIT_DUP;
|
||||
const char *end;
|
||||
|
||||
/*
|
||||
* Ref lines have a number of fields which are space deliminated. The
|
||||
* first field is the OID of the ref. The second field is the ref
|
||||
* name. Subsequent fields (symref-target and peeled) are optional and
|
||||
* don't have a particular order.
|
||||
*/
|
||||
if (string_list_split(&line_sections, line, ' ', -1) < 2) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (parse_oid_hex(line_sections.items[i++].string, &old_oid, &end) ||
|
||||
*end) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ref = alloc_ref(line_sections.items[i++].string);
|
||||
|
||||
oidcpy(&ref->old_oid, &old_oid);
|
||||
**list = ref;
|
||||
*list = &ref->next;
|
||||
|
||||
for (; i < line_sections.nr; i++) {
|
||||
const char *arg = line_sections.items[i].string;
|
||||
if (skip_prefix(arg, "symref-target:", &arg))
|
||||
ref->symref = xstrdup(arg);
|
||||
|
||||
if (skip_prefix(arg, "peeled:", &arg)) {
|
||||
struct object_id peeled_oid;
|
||||
char *peeled_name;
|
||||
struct ref *peeled;
|
||||
if (parse_oid_hex(arg, &peeled_oid, &end) || *end) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
peeled_name = xstrfmt("%s^{}", ref->name);
|
||||
peeled = alloc_ref(peeled_name);
|
||||
|
||||
oidcpy(&peeled->old_oid, &peeled_oid);
|
||||
**list = peeled;
|
||||
*list = &peeled->next;
|
||||
|
||||
free(peeled_name);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
string_list_clear(&line_sections, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
||||
struct ref **list, int for_push,
|
||||
const struct argv_array *ref_prefixes)
|
||||
{
|
||||
int i;
|
||||
*list = NULL;
|
||||
|
||||
if (server_supports_v2("ls-refs", 1))
|
||||
packet_write_fmt(fd_out, "command=ls-refs\n");
|
||||
|
||||
if (server_supports_v2("agent", 0))
|
||||
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
|
||||
|
||||
packet_delim(fd_out);
|
||||
/* When pushing we don't want to request the peeled tags */
|
||||
if (!for_push)
|
||||
packet_write_fmt(fd_out, "peel\n");
|
||||
packet_write_fmt(fd_out, "symrefs\n");
|
||||
for (i = 0; ref_prefixes && i < ref_prefixes->argc; i++) {
|
||||
packet_write_fmt(fd_out, "ref-prefix %s\n",
|
||||
ref_prefixes->argv[i]);
|
||||
}
|
||||
packet_flush(fd_out);
|
||||
|
||||
/* Process response from server */
|
||||
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
|
||||
if (!process_ref_v2(reader->line, &list))
|
||||
die("invalid ls-refs response: %s", reader->line);
|
||||
}
|
||||
|
||||
if (reader->status != PACKET_READ_FLUSH)
|
||||
die("expected flush after ref listing");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
|
||||
{
|
||||
int len;
|
||||
@ -336,7 +464,7 @@ int parse_feature_request(const char *feature_list, const char *feature)
|
||||
|
||||
const char *server_feature_value(const char *feature, int *len)
|
||||
{
|
||||
return parse_feature_value(server_capabilities, feature, len);
|
||||
return parse_feature_value(server_capabilities_v1, feature, len);
|
||||
}
|
||||
|
||||
int server_supports(const char *feature)
|
||||
|
||||
Reference in New Issue
Block a user