push: support push-options

Push options are an optional capability of a git server.  If they are
listed in the servers capabilities, when the client lists push-options
in it's list of capabilities, then the client sends it's list of push
options followed by a flush-pkt.

So, If we have any declared push options, then we will list it as a
client capability, and send the options.

If the request contains push options but the server has no push
options capability, then error.
This commit is contained in:
Russell Sim
2022-11-25 13:35:08 +01:00
parent 12832bab73
commit ecc6f2fb83
8 changed files with 66 additions and 1 deletions

View File

@@ -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

View File

@@ -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;
/**

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}