Commit 8156835d by Carlos Martín Nieto

smart: store reported symrefs

The protocol has a capability which allows the server to tell us which
refs are symrefs, so we can e.g. know which is the default branch.

This capability is different from the ones we already support, as it's
not setting a flag to true, but requires us to store a list of
refspec-formatted mappings.

This commit does not yet expose the information in the reference
listing.
parent 58532ed0
......@@ -7,6 +7,7 @@
#include "git2.h"
#include "smart.h"
#include "refs.h"
#include "refspec.h"
static int git_smart__recv_cb(gitno_buffer *buf)
{
......@@ -63,7 +64,7 @@ static int git_smart__set_callbacks(
return 0;
}
int git_smart__update_heads(transport_smart *t)
int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
{
size_t i;
git_pkt *pkt;
......@@ -81,6 +82,19 @@ int git_smart__update_heads(transport_smart *t)
return 0;
}
static void free_symrefs(git_vector *symrefs)
{
git_refspec *spec;
size_t i;
git_vector_foreach(symrefs, i, spec) {
git_refspec__free(spec);
git__free(spec);
}
git_vector_free(symrefs);
}
static int git_smart__connect(
git_transport *transport,
const char *url,
......@@ -94,6 +108,7 @@ static int git_smart__connect(
int error;
git_pkt *pkt;
git_pkt_ref *first;
git_vector symrefs;
git_smart_service_t service;
if (git_smart__reset_stream(t, true) < 0)
......@@ -147,8 +162,11 @@ static int git_smart__connect(
first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
return error;
/* Detect capabilities */
if (git_smart__detect_caps(first, &t->caps) < 0)
if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
return -1;
/* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
......@@ -159,7 +177,9 @@ static int git_smart__connect(
}
/* Keep a list of heads for _ls */
git_smart__update_heads(t);
git_smart__update_heads(t, &symrefs);
free_symrefs(&symrefs);
if (t->rpc && git_smart__reset_stream(t, false) < 0)
return -1;
......
......@@ -23,6 +23,7 @@
#define GIT_CAP_DELETE_REFS "delete-refs"
#define GIT_CAP_REPORT_STATUS "report-status"
#define GIT_CAP_THIN_PACK "thin-pack"
#define GIT_CAP_SYMREF "symref"
enum git_pkt_type {
GIT_PKT_CMD,
......@@ -154,7 +155,7 @@ typedef struct {
/* smart_protocol.c */
int git_smart__store_refs(transport_smart *t, int flushes);
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps);
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch(
......@@ -174,7 +175,7 @@ int git_smart__download_pack(
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
int git_smart__update_heads(transport_smart *t);
int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
......
......@@ -78,7 +78,52 @@ int git_smart__store_refs(transport_smart *t, int flushes)
return flush;
}
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
{
int error;
const char *end;
git_buf buf = GIT_BUF_INIT;
git_refspec *mapping;
ptr += strlen(GIT_CAP_SYMREF);
if (*ptr != '=')
goto on_invalid;
ptr++;
if (!(end = strchr(ptr, ' ')) &&
!(end = strchr(ptr, '\0')))
goto on_invalid;
if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
return error;
/* symref mapping has refspec format */
mapping = git__malloc(sizeof(git_refspec));
GITERR_CHECK_ALLOC(mapping);
error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
git_buf_free(&buf);
/* if the error isn't OOM, then it's a parse error; let's use a nicer message */
if (error < 0) {
if (giterr_last()->klass != GITERR_NOMEMORY)
goto on_invalid;
return error;
}
if ((error = git_vector_insert(symrefs, mapping)) < 0)
return error;
*out = end;
return 0;
on_invalid:
giterr_set(GITERR_NET, "remote sent invalid symref");
return -1;
}
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
{
const char *ptr;
......@@ -141,6 +186,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
continue;
}
if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
int error;
if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
return error;
continue;
}
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
......@@ -969,7 +1023,7 @@ int git_smart__push(git_transport *transport, git_push *push)
if (error < 0)
goto done;
error = git_smart__update_heads(t);
error = git_smart__update_heads(t, NULL);
}
done:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment