diff options
author | Vicent Marti <vicent@github.com> | 2014-05-22 02:28:42 +0200 |
---|---|---|
committer | Vicent Marti <vicent@github.com> | 2014-05-22 02:28:42 +0200 |
commit | a598264463442da0013d6e8b6a0c93d075e14d3a (patch) | |
tree | 0e92ceda6fb9168525181ce01ed2c70a20429e69 /src/transports | |
parent | 7ee233a9203d09a08e269788ca4877778bc0c489 (diff) | |
parent | 2a597116583696486141261a8b459319b513facf (diff) | |
download | libgit2-a598264463442da0013d6e8b6a0c93d075e14d3a.tar.gz |
Merge pull request #2376 from libgit2/cmn/remote-symref
Add support for the symref extension
Diffstat (limited to 'src/transports')
-rw-r--r-- | src/transports/local.c | 22 | ||||
-rw-r--r-- | src/transports/smart.c | 45 | ||||
-rw-r--r-- | src/transports/smart.h | 5 | ||||
-rw-r--r-- | src/transports/smart_pkt.c | 1 | ||||
-rw-r--r-- | src/transports/smart_protocol.c | 58 |
5 files changed, 120 insertions, 11 deletions
diff --git a/src/transports/local.c b/src/transports/local.c index 2c17e6271..8d3619388 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -43,14 +43,19 @@ typedef struct { static int add_ref(transport_local *t, const char *name) { const char peeled[] = "^{}"; - git_oid head_oid; + git_reference *ref, *resolved; git_remote_head *head; + git_oid obj_id; git_object *obj = NULL, *target = NULL; git_buf buf = GIT_BUF_INIT; int error; - error = git_reference_name_to_id(&head_oid, t->repo, name); + if ((error = git_reference_lookup(&ref, t->repo, name)) < 0) + return error; + + error = git_reference_resolve(&resolved, ref); if (error < 0) { + git_reference_free(ref); if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) { /* This is actually okay. Empty repos often have a HEAD that * points to a nonexistent "refs/heads/master". */ @@ -60,13 +65,22 @@ static int add_ref(transport_local *t, const char *name) return error; } + git_oid_cpy(&obj_id, git_reference_target(resolved)); + git_reference_free(resolved); + head = git__calloc(1, sizeof(git_remote_head)); GITERR_CHECK_ALLOC(head); head->name = git__strdup(name); GITERR_CHECK_ALLOC(head->name); - git_oid_cpy(&head->oid, &head_oid); + git_oid_cpy(&head->oid, &obj_id); + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { + head->symref_target = git__strdup(git_reference_symbolic_target(ref)); + GITERR_CHECK_ALLOC(head->symref_target); + } + git_reference_free(ref); if ((error = git_vector_insert(&t->refs, head)) < 0) { git__free(head->name); @@ -176,7 +190,7 @@ static int path_from_url_or_path(git_buf *local_path_out, const char *url_or_pat /* * Try to open the url as a git directory. The direction doesn't - * matter in this case because we're calulating the heads ourselves. + * matter in this case because we're calculating the heads ourselves. */ static int local_connect( git_transport *transport, diff --git a/src/transports/smart.c b/src/transports/smart.c index 7fb41f788..c9845bff9 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -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; @@ -74,6 +75,25 @@ int git_smart__update_heads(transport_smart *t) if (pkt->type != GIT_PKT_REF) continue; + if (symrefs) { + git_refspec *spec; + git_buf buf = GIT_BUF_INIT; + size_t j; + int error; + + git_vector_foreach(symrefs, j, spec) { + git_buf_clear(&buf); + if (git_refspec_src_matches(spec, ref->head.name) && + !(error = git_refspec_transform(&buf, spec, ref->head.name))) + ref->head.symref_target = git_buf_detach(&buf); + } + + git_buf_free(&buf); + + if (error < 0) + return error; + } + if (git_vector_insert(&t->heads, &ref->head) < 0) return -1; } @@ -81,6 +101,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 +127,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 +181,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 +196,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; diff --git a/src/transports/smart.h b/src/transports/smart.h index a2b6b2a71..f1fc29520 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -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); diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index e9376ae6f..b5f9d6dbe 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -433,6 +433,7 @@ void git_pkt_free(git_pkt *pkt) if (pkt->type == GIT_PKT_REF) { git_pkt_ref *p = (git_pkt_ref *) pkt; git__free(p->head.name); + git__free(p->head.symref_target); } if (pkt->type == GIT_PKT_OK) { diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 5dd6bab24..bab0cf113 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -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: |