promisor.acceptFromServer::
If set to "all", a client will accept all the promisor remotes
a server might advertise using the "promisor-remote"
- capability. Default is "none", which means no promisor remote
- advertised by a server will be accepted. By accepting a
- promisor remote, the client agrees that the server might omit
- objects that are lazily fetchable from this promisor remote
- from its responses to "fetch" and "clone" requests from the
- client. See linkgit:gitprotocol-v2[5].
+ capability. If set to "knownName" the client will accept
+ promisor remotes which are already configured on the client
+ and have the same name as those advertised by the client. This
+ is not very secure, but could be used in a corporate setup
+ where servers and clients are trusted to not switch name and
+ URLs. If set to "knownUrl", the client will accept promisor
+ remotes which have both the same name and the same URL
+ configured on the client as the name and URL advertised by the
+ server. This is more secure than "all" or "knownName", so it
+ should be used if possible instead of those options. Default
+ is "none", which means no promisor remote advertised by a
+ server will be accepted. By accepting a promisor remote, the
+ client agrees that the server might omit objects that are
+ lazily fetchable from this promisor remote from its responses
+ to "fetch" and "clone" requests from the client. See
+ linkgit:gitprotocol-v2[5].
return strbuf_detach(&sb, NULL);
}
+/*
+ * Find first index of 'nicks' where there is 'nick'. 'nick' is
+ * compared case insensitively to the strings in 'nicks'. If not found
+ * 'nicks->nr' is returned.
+ */
+static size_t remote_nick_find(struct strvec *nicks, const char *nick)
+{
+ for (size_t i = 0; i < nicks->nr; i++)
+ if (!strcasecmp(nicks->v[i], nick))
+ return i;
+ return nicks->nr;
+}
+
enum accept_promisor {
ACCEPT_NONE = 0,
+ ACCEPT_KNOWN_URL,
+ ACCEPT_KNOWN_NAME,
ACCEPT_ALL
};
static int should_accept_remote(enum accept_promisor accept,
- const char *remote_name UNUSED,
- const char *remote_url UNUSED)
+ const char *remote_name, const char *remote_url,
+ struct strvec *names, struct strvec *urls)
{
+ size_t i;
+
if (accept == ACCEPT_ALL)
return 1;
- BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
+ i = remote_nick_find(names, remote_name);
+
+ if (i >= names->nr)
+ /* We don't know about that remote */
+ return 0;
+
+ if (accept == ACCEPT_KNOWN_NAME)
+ return 1;
+
+ if (accept != ACCEPT_KNOWN_URL)
+ BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
+
+ if (!strcmp(urls->v[i], remote_url))
+ return 1;
+
+ warning(_("known remote named '%s' but with url '%s' instead of '%s'"),
+ remote_name, urls->v[i], remote_url);
+
+ return 0;
}
-static void filter_promisor_remote(struct strvec *accepted, const char *info)
+static void filter_promisor_remote(struct repository *repo,
+ struct strvec *accepted,
+ const char *info)
{
struct strbuf **remotes;
const char *accept_str;
enum accept_promisor accept = ACCEPT_NONE;
+ struct strvec names = STRVEC_INIT;
+ struct strvec urls = STRVEC_INIT;
if (!git_config_get_string_tmp("promisor.acceptfromserver", &accept_str)) {
if (!*accept_str || !strcasecmp("None", accept_str))
accept = ACCEPT_NONE;
+ else if (!strcasecmp("KnownUrl", accept_str))
+ accept = ACCEPT_KNOWN_URL;
+ else if (!strcasecmp("KnownName", accept_str))
+ accept = ACCEPT_KNOWN_NAME;
else if (!strcasecmp("All", accept_str))
accept = ACCEPT_ALL;
else
if (accept == ACCEPT_NONE)
return;
+ if (accept != ACCEPT_ALL)
+ promisor_info_vecs(repo, &names, &urls);
+
/* Parse remote info received */
remotes = strbuf_split_str(info, ';', 0);
if (remote_url)
decoded_url = url_percent_decode(remote_url);
- if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url))
+ if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url, &names, &urls))
strvec_push(accepted, decoded_name);
strbuf_list_free(elems);
free(decoded_url);
}
+ strvec_clear(&names);
+ strvec_clear(&urls);
strbuf_list_free(remotes);
}
struct strvec accepted = STRVEC_INIT;
struct strbuf reply = STRBUF_INIT;
- filter_promisor_remote(&accepted, info);
+ filter_promisor_remote(the_repository, &accepted, info);
if (!accepted.nr)
return NULL;
check_missing_objects server 1 "$oid"
'
+test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
+ git -C server config promisor.advertise true &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownName \
+ --no-local --filter="blob:limit=5k" server client &&
+ test_when_finished "rm -rf client" &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownName' and different remote names" '
+ git -C server config promisor.advertise true &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.serverTwo.promisor=true \
+ -c remote.serverTwo.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.serverTwo.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownName \
+ --no-local --filter="blob:limit=5k" server client &&
+ test_when_finished "rm -rf client" &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.acceptfromserver set to 'KnownUrl'" '
+ git -C server config promisor.advertise true &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+ test_when_finished "rm -rf client" &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownUrl' and different remote urls" '
+ ln -s lop serverTwo &&
+
+ git -C server config promisor.advertise true &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/serverTwo" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+ test_when_finished "rm -rf client" &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
git -C server config promisor.advertise true &&