@@ -116,6 +116,9 @@ Supported commands: 'stateless-connect'.
+
Supported commands: 'list for-push', 'push'.
+'push-base'::
+ Indicates that this remote helper supports the 'push-base' option.
+
'export'::
Can discover remote refs and push specified objects from a
fast-import stream to remote refs.
@@ -541,6 +544,11 @@ set by Git if the remote helper has the 'option' capability.
If set to an algorithm, indicate that the caller wants to interact with
the remote side using that algorithm.
+'option push-base' <ref>::
+ Only supported if this remote helper advertises the 'push-base'
+ capability. Indicate that <ref> is an ancestor of the commits to be
+ pushed, and it is believed to be known by the server.
+
SEE ALSO
--------
linkgit:git-remote[1]
@@ -29,6 +29,7 @@ struct options {
struct string_list deepen_not;
struct string_list push_options;
char *filter;
+ struct object_id push_base;
unsigned progress : 1,
check_self_contained_and_connected : 1,
cloning : 1,
@@ -205,6 +206,10 @@ static int set_option(const char *name, const char *value)
options.hash_algo = &hash_algos[algo];
}
return 0;
+ } else if (!strcmp(name, "push-base")) {
+ if (get_oid(value, &options.push_base))
+ die(_("%s is not a valid object"), value);
+ return 0;
} else {
return 1 /* unsupported */;
}
@@ -364,16 +369,21 @@ static int show_http_message(struct strbuf *type, struct strbuf *charset,
}
static int get_protocol_http_header(enum protocol_version version,
+ const struct object_id *base,
struct strbuf *header)
{
- if (version > 0) {
+ if (version > 0)
strbuf_addf(header, GIT_PROTOCOL_HEADER ": version=%d",
version);
-
- return 1;
+ if (!is_null_oid(base)) {
+ if (version > 0)
+ strbuf_addch(header, ':');
+ else
+ strbuf_addstr(header, GIT_PROTOCOL_HEADER ": ");
+ strbuf_addf(header, "base=%s", oid_to_hex(base));
}
- return 0;
+ return !!(version > 0 || !(is_null_oid(base)));
}
static void check_smart_http(struct discovery *d, const char *service,
@@ -469,7 +479,7 @@ static struct discovery *discover_refs(const char *service, int for_push)
version = protocol_v0;
/* Add the extra Git-Protocol header */
- if (get_protocol_http_header(version, &protocol_header))
+ if (get_protocol_http_header(version, &options.push_base, &protocol_header))
string_list_append(&extra_headers, protocol_header.buf);
memset(&http_options, 0, sizeof(http_options));
@@ -1074,7 +1084,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
strbuf_addf(&buf, "Accept: application/x-%s-result", svc);
rpc->hdr_accept = strbuf_detach(&buf, NULL);
- if (get_protocol_http_header(heads->version, &buf))
+ if (get_protocol_http_header(heads->version, &options.push_base, &buf))
rpc->protocol_header = strbuf_detach(&buf, NULL);
else
rpc->protocol_header = NULL;
@@ -1406,7 +1416,7 @@ static int stateless_connect(const char *service_name)
rpc.service_url = xstrfmt("%s%s", url.buf, rpc.service_name);
rpc.hdr_content_type = xstrfmt("Content-Type: application/x-%s-request", rpc.service_name);
rpc.hdr_accept = xstrfmt("Accept: application/x-%s-result", rpc.service_name);
- if (get_protocol_http_header(discover->version, &buf)) {
+ if (get_protocol_http_header(discover->version, &options.push_base, &buf)) {
rpc.protocol_header = strbuf_detach(&buf, NULL);
} else {
rpc.protocol_header = NULL;
@@ -1538,6 +1548,7 @@ int cmd_main(int argc, const char **argv)
printf("push\n");
printf("check-connectivity\n");
printf("object-format\n");
+ printf("push-base\n");
printf("\n");
fflush(stdout);
} else if (skip_prefix(buf.buf, "stateless-connect ", &arg)) {
@@ -373,6 +373,36 @@ test_expect_success 'push with http:// using protocol v1' '
grep "git< version 1" log
'
+test_expect_success 'push with http:// using protocol v1 and --base' '
+ test_commit -C http_child four &&
+ COMMON_HASH=$(git -C http_child rev-parse three) &&
+
+ # Push to another branch, as the target repository has the
+ # master branch checked out and we cannot push into it.
+ GIT_TRACE_PACKET=1 test_might_fail git -C http_child -c protocol.version=1 \
+ push --base=three origin HEAD:client_branch_four 2>log &&
+
+ # Server responded using protocol v1
+ grep "git< version 1" log &&
+ # Server advertised only the expected object
+ grep "$COMMON_HASH .have" log
+'
+
+test_expect_success 'push with http:// using protocol v0 and --base' '
+ test_commit -C http_child five &&
+ COMMON_HASH=$(git -C http_child rev-parse four) &&
+
+ # Push to another branch, as the target repository has the
+ # master branch checked out and we cannot push into it.
+ GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=0 \
+ push --base=four origin HEAD:client_branch_five 2>log &&
+
+ # Server did not respond with any version
+ ! grep "git< version" log &&
+ # Server advertised only the expected object
+ grep "$COMMON_HASH .have" log
+'
+
# DO NOT add non-httpd-specific tests here, because the last part of this
# test script is only executed when httpd is available and enabled.
@@ -27,6 +27,7 @@ struct helper_data {
export : 1,
option : 1,
push : 1,
+ push_base : 1,
connect : 1,
stateless_connect : 1,
signed_tags : 1,
@@ -186,6 +187,8 @@ static struct child_process *get_helper(struct transport *transport)
data->option = 1;
else if (!strcmp(capname, "push"))
data->push = 1;
+ else if (!strcmp(capname, "push-base"))
+ data->push_base = 1;
else if (!strcmp(capname, "import"))
data->import = 1;
else if (!strcmp(capname, "bidi-import"))
@@ -1183,6 +1186,18 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
exit(128);
}
+ if (!is_null_oid(&data->transport_options.push_base)) {
+ if (data->push_base) {
+ write_str_in_full(helper->in, "option push-base ");
+ write_str_in_full(helper->in, oid_to_hex(&data->transport_options.push_base));
+ write_str_in_full(helper->in, "\n");
+ if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
+ exit(128);
+ } else {
+ warning(_("transport does not support --base"));
+ }
+ }
+
if (data->push && for_push)
write_str_in_full(helper->in, "list for-push\n");
else
For the http(s):// transports, teach push the "--base" parameter, which indicates an ancestor of the commits to be pushed that is believed to be known by the server. This is done through a new remote helper capability and option "push-base". If a remote helper advertises this capability, then it also supports the option of the same name. Git can then use this option to inform the remote helper of a base that the user has specified. See the commit message of this commit's parent for more information. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> --- Documentation/gitremote-helpers.txt | 8 ++++++++ remote-curl.c | 25 +++++++++++++++++------- t/t5700-protocol-v1.sh | 30 +++++++++++++++++++++++++++++ transport-helper.c | 15 +++++++++++++++ 4 files changed, 71 insertions(+), 7 deletions(-)