diff --git a/include/git2/remote.h b/include/git2/remote.h index 8c9c26f3f..108fcfe71 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -812,6 +812,11 @@ typedef struct { * Extra headers for this push operation */ git_strarray custom_headers; + + /** + * Push options + */ + git_strarray push_options; } git_push_options; #define GIT_PUSH_OPTIONS_VERSION 1 diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h index 0eae9234d..07309ab09 100644 --- a/include/git2/sys/remote.h +++ b/include/git2/sys/remote.h @@ -26,6 +26,9 @@ typedef enum { /** Remote supports fetching an individual reachable object. */ GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1), + + /** Remote supports push options. */ + GIT_REMOTE_CAPABILITY_PUSH_OPTIONS = (1 << 2), } git_remote_capability_t; /** diff --git a/src/libgit2/push.c b/src/libgit2/push.c index d477b4f0d..b1cbcd4ae 100644 --- a/src/libgit2/push.c +++ b/src/libgit2/push.c @@ -68,6 +68,14 @@ int git_push_new(git_push **out, git_remote *remote, const git_push_options *opt return -1; } + if (git_vector_init(&p->push_options, 0, git__strcmp_cb) < 0) { + git_vector_free(&p->status); + git_vector_free(&p->specs); + git_vector_free(&p->updates); + git__free(p); + return -1; + } + *out = p; return 0; } @@ -472,12 +480,23 @@ static int filter_refs(git_remote *remote) int git_push_finish(git_push *push) { int error; + unsigned int remote_caps; if (!git_remote_connected(push->remote)) { git_error_set(GIT_ERROR_NET, "remote is disconnected"); return -1; } + if ((error = git_remote_capabilities(&remote_caps, push->remote)) < 0){ + git_error_set(GIT_ERROR_INVALID, "remote capabilities not available"); + return -1; + } + + if (git_vector_length(&push->push_options) > 0 && !(remote_caps & GIT_REMOTE_CAPABILITY_PUSH_OPTIONS)) { + git_error_set(GIT_ERROR_INVALID, "push-options not supported by remote"); + return -1; + } + if ((error = filter_refs(push->remote)) < 0 || (error = do_push(push)) < 0) return error; @@ -521,6 +540,7 @@ void git_push_free(git_push *push) push_spec *spec; push_status *status; git_push_update *update; + char *option; unsigned int i; if (push == NULL) @@ -543,6 +563,11 @@ void git_push_free(git_push *push) } git_vector_free(&push->updates); + git_vector_foreach(&push->push_options, i, option) { + git__free(option); + } + git_vector_free(&push->push_options); + git__free(push); } diff --git a/src/libgit2/push.h b/src/libgit2/push.h index fc72e845e..17c3e2f68 100644 --- a/src/libgit2/push.h +++ b/src/libgit2/push.h @@ -34,6 +34,7 @@ struct git_push { git_vector specs; git_vector updates; bool report_status; + git_vector push_options; /* report-status */ bool unpack_ok; diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c index 02d271d7d..a1be71629 100644 --- a/src/libgit2/remote.c +++ b/src/libgit2/remote.c @@ -2958,6 +2958,13 @@ int git_remote_upload( } } + if (opts && opts->push_options.count > 0) + for (i = 0; i < opts->push_options.count; ++i) { + if ((error = git_vector_insert(&push->push_options, git__strdup(opts->push_options.strings[i]))) < 0) { + goto cleanup; + } + } + if ((error = git_push_finish(push)) < 0) goto cleanup; diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c index 7f57dba2a..4de8a3d78 100644 --- a/src/libgit2/transports/smart.c +++ b/src/libgit2/transports/smart.c @@ -233,6 +233,9 @@ static int git_smart__capabilities(unsigned int *capabilities, git_transport *tr *capabilities = 0; + if (t->caps.push_options) + *capabilities |= GIT_REMOTE_CAPABILITY_PUSH_OPTIONS; + if (t->caps.want_tip_sha1) *capabilities |= GIT_REMOTE_CAPABILITY_TIP_OID; diff --git a/src/libgit2/transports/smart.h b/src/libgit2/transports/smart.h index 9323d6c44..2aea414e9 100644 --- a/src/libgit2/transports/smart.h +++ b/src/libgit2/transports/smart.h @@ -32,6 +32,7 @@ #define GIT_CAP_SYMREF "symref" #define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want" #define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want" +#define GIT_CAP_PUSH_OPTIONS "push-options" extern bool git_smart__ofs_delta_enabled; @@ -132,7 +133,8 @@ typedef struct transport_smart_caps { report_status:1, thin_pack:1, want_tip_sha1:1, - want_reachable_sha1:1; + want_reachable_sha1:1, + push_options:1; } transport_smart_caps; typedef int (*packetsize_cb)(size_t received, void *payload); diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c index 09778b335..525053f42 100644 --- a/src/libgit2/transports/smart_protocol.c +++ b/src/libgit2/transports/smart_protocol.c @@ -190,6 +190,12 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec continue; } + if (!git__prefixcmp(ptr, GIT_CAP_PUSH_OPTIONS)) { + caps->common = caps->push_options = 1; + ptr += strlen(GIT_CAP_PUSH_OPTIONS); + continue; + } + if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) { caps->common = caps->thin_pack = 1; ptr += strlen(GIT_CAP_THIN_PACK); @@ -642,6 +648,7 @@ done: static int gen_pktline(git_str *buf, git_push *push) { push_spec *spec; + char *option; size_t i, len; char old_id[GIT_OID_SHA1_HEXSIZE+1], new_id[GIT_OID_SHA1_HEXSIZE+1]; @@ -654,6 +661,8 @@ static int gen_pktline(git_str *buf, git_push *push) ++len; /* '\0' */ if (push->report_status) len += strlen(GIT_CAP_REPORT_STATUS) + 1; + if (git_vector_length(&push->push_options) > 0) + len += strlen(GIT_CAP_PUSH_OPTIONS) + 1; len += strlen(GIT_CAP_SIDE_BAND_64K) + 1; } @@ -669,6 +678,10 @@ static int gen_pktline(git_str *buf, git_push *push) git_str_putc(buf, ' '); git_str_printf(buf, GIT_CAP_REPORT_STATUS); } + if (git_vector_length(&push->push_options) > 0) { + git_str_putc(buf, ' '); + git_str_printf(buf, GIT_CAP_PUSH_OPTIONS); + } git_str_putc(buf, ' '); git_str_printf(buf, GIT_CAP_SIDE_BAND_64K); } @@ -676,6 +689,12 @@ static int gen_pktline(git_str *buf, git_push *push) git_str_putc(buf, '\n'); } + if (git_vector_length(&push->push_options) > 0) { + git_str_printf(buf, "0000"); + git_vector_foreach(&push->push_options, i, option) { + git_str_printf(buf, "%04"PRIxZ"%s", strlen(option) + 4 , option); + } + } git_str_puts(buf, "0000"); return git_str_oom(buf) ? -1 : 0; }