mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
Merge branch 'cc/promisor-remote-capability' into jch
The v2 protocol learned to allow the server to advertise possible promisor remotes, and the client to respond with what promissor remotes it uses, so that the server side can omit objects that the client can lazily obtain from these other promissor remotes. Comments? I got an impression that this is premature without finishing the discussion on a larger picture. cf. <ZvpZv_fed_su4w2-@pks.im> * cc/promisor-remote-capability: promisor-remote: check advertised name or URL Add 'promisor-remote' capability to protocol v2 strbuf: refactor strbuf_trim_trailing_ch() version: refactor strbuf_sanitize()
This commit is contained in:
@@ -1,3 +1,30 @@
|
||||
promisor.quiet::
|
||||
If set to "true" assume `--quiet` when fetching additional
|
||||
objects for a partial clone.
|
||||
|
||||
promisor.advertise::
|
||||
If set to "true", a server will use the "promisor-remote"
|
||||
capability, see linkgit:gitprotocol-v2[5], to advertise the
|
||||
promisor remotes it is using, if it uses some. Default is
|
||||
"false", which means the "promisor-remote" capability is not
|
||||
advertised.
|
||||
|
||||
promisor.acceptFromServer::
|
||||
If set to "all", a client will accept all the promisor remotes
|
||||
a server might advertise using the "promisor-remote"
|
||||
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 "knownUrl", 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].
|
||||
|
||||
@@ -781,6 +781,60 @@ retrieving the header from a bundle at the indicated URI, and thus
|
||||
save themselves and the server(s) the request(s) needed to inspect the
|
||||
headers of that bundle or bundles.
|
||||
|
||||
promisor-remote=<pr-infos>
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The server may advertise some promisor remotes it is using or knows
|
||||
about to a client which may want to use them as its promisor remotes,
|
||||
instead of this repository. In this case <pr-infos> should be of the
|
||||
form:
|
||||
|
||||
pr-infos = pr-info | pr-infos ";" pr-info
|
||||
|
||||
pr-info = "name=" pr-name | "name=" pr-name "," "url=" pr-url
|
||||
|
||||
where `pr-name` is the urlencoded name of a promisor remote, and
|
||||
`pr-url` the urlencoded URL of that promisor remote.
|
||||
|
||||
In this case, if the client decides to use one or more promisor
|
||||
remotes the server advertised, it can reply with
|
||||
"promisor-remote=<pr-names>" where <pr-names> should be of the form:
|
||||
|
||||
pr-names = pr-name | pr-names ";" pr-name
|
||||
|
||||
where `pr-name` is the urlencoded name of a promisor remote the server
|
||||
advertised and the client accepts.
|
||||
|
||||
Note that, everywhere in this document, `pr-name` MUST be a valid
|
||||
remote name, and the ';' and ',' characters MUST be encoded if they
|
||||
appear in `pr-name` or `pr-url`.
|
||||
|
||||
If the server doesn't know any promisor remote that could be good for
|
||||
a client to use, or prefers a client not to use any promisor remote it
|
||||
uses or knows about, it shouldn't advertise the "promisor-remote"
|
||||
capability at all.
|
||||
|
||||
In this case, or if the client doesn't want to use any promisor remote
|
||||
the server advertised, the client shouldn't advertise the
|
||||
"promisor-remote" capability at all in its reply.
|
||||
|
||||
The "promisor.advertise" and "promisor.acceptFromServer" configuration
|
||||
options can be used on the server and client side respectively to
|
||||
control what they advertise or accept respectively. See the
|
||||
documentation of these configuration options for more information.
|
||||
|
||||
Note that in the future it would be nice if the "promisor-remote"
|
||||
protocol capability could be used by the server, when responding to
|
||||
`git fetch` or `git clone`, to advertise better-connected remotes that
|
||||
the client can use as promisor remotes, instead of this repository, so
|
||||
that the client can lazily fetch objects from these other
|
||||
better-connected remotes. This would require the server to omit in its
|
||||
response the objects available on the better-connected remotes that
|
||||
the client has accepted. This hasn't been implemented yet though. So
|
||||
for now this "promisor-remote" capability is useful only when the
|
||||
server advertises some promisor remotes it already uses to borrow
|
||||
objects from.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "protocol.h"
|
||||
#include "alias.h"
|
||||
#include "bundle-uri.h"
|
||||
#include "promisor-remote.h"
|
||||
|
||||
static char *server_capabilities_v1;
|
||||
static struct strvec server_capabilities_v2 = STRVEC_INIT;
|
||||
@@ -487,6 +488,7 @@ void check_stateless_delimiter(int stateless_rpc,
|
||||
static void send_capabilities(int fd_out, struct packet_reader *reader)
|
||||
{
|
||||
const char *hash_name;
|
||||
const char *promisor_remote_info;
|
||||
|
||||
if (server_supports_v2("agent"))
|
||||
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
|
||||
@@ -500,6 +502,13 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
|
||||
} else {
|
||||
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
|
||||
}
|
||||
if (server_feature_v2("promisor-remote", &promisor_remote_info)) {
|
||||
char *reply = promisor_remote_reply(promisor_remote_info);
|
||||
if (reply) {
|
||||
packet_write_fmt(fd_out, "promisor-remote=%s", reply);
|
||||
free(reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "strvec.h"
|
||||
#include "packfile.h"
|
||||
#include "environment.h"
|
||||
#include "url.h"
|
||||
|
||||
struct promisor_remote_config {
|
||||
struct promisor_remote *promisors;
|
||||
@@ -221,6 +222,18 @@ int repo_has_promisor_remote(struct repository *r)
|
||||
return !!repo_promisor_remote_find(r, NULL);
|
||||
}
|
||||
|
||||
int repo_has_accepted_promisor_remote(struct repository *r)
|
||||
{
|
||||
struct promisor_remote *p;
|
||||
|
||||
promisor_remote_init(r);
|
||||
|
||||
for (p = r->promisor_remote_config->promisors; p; p = p->next)
|
||||
if (p->accepted)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_fetched_oids(struct repository *repo,
|
||||
struct object_id **oids,
|
||||
int oid_nr, int to_free)
|
||||
@@ -292,3 +305,234 @@ all_fetched:
|
||||
if (to_free)
|
||||
free(remaining_oids);
|
||||
}
|
||||
|
||||
static int allow_unsanitized(char ch)
|
||||
{
|
||||
if (ch == ',' || ch == ';' || ch == '%')
|
||||
return 0;
|
||||
return ch > 32 && ch < 127;
|
||||
}
|
||||
|
||||
static void promisor_info_vecs(struct repository *repo,
|
||||
struct strvec *names,
|
||||
struct strvec *urls)
|
||||
{
|
||||
struct promisor_remote *r;
|
||||
|
||||
promisor_remote_init(repo);
|
||||
|
||||
for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
|
||||
char *url;
|
||||
char *url_key = xstrfmt("remote.%s.url", r->name);
|
||||
|
||||
strvec_push(names, r->name);
|
||||
strvec_push(urls, git_config_get_string(url_key, &url) ? NULL : url);
|
||||
|
||||
free(url);
|
||||
free(url_key);
|
||||
}
|
||||
}
|
||||
|
||||
char *promisor_remote_info(struct repository *repo)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
int advertise_promisors = 0;
|
||||
struct strvec names = STRVEC_INIT;
|
||||
struct strvec urls = STRVEC_INIT;
|
||||
|
||||
git_config_get_bool("promisor.advertise", &advertise_promisors);
|
||||
|
||||
if (!advertise_promisors)
|
||||
return NULL;
|
||||
|
||||
promisor_info_vecs(repo, &names, &urls);
|
||||
|
||||
if (!names.nr)
|
||||
return NULL;
|
||||
|
||||
for (size_t i = 0; i < names.nr; i++) {
|
||||
if (i)
|
||||
strbuf_addch(&sb, ';');
|
||||
strbuf_addstr(&sb, "name=");
|
||||
strbuf_addstr_urlencode(&sb, names.v[i], allow_unsanitized);
|
||||
if (urls.v[i]) {
|
||||
strbuf_addstr(&sb, ",url=");
|
||||
strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
|
||||
}
|
||||
}
|
||||
|
||||
strbuf_sanitize(&sb);
|
||||
|
||||
strvec_clear(&names);
|
||||
strvec_clear(&urls);
|
||||
|
||||
return strbuf_detach(&sb, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find first index of 'vec' where there is 'val'. 'val' is compared
|
||||
* case insensively to the strings in 'vec'. If not found 'vec->nr' is
|
||||
* returned.
|
||||
*/
|
||||
static size_t strvec_find_index(struct strvec *vec, const char *val)
|
||||
{
|
||||
for (size_t i = 0; i < vec->nr; i++)
|
||||
if (!strcasecmp(vec->v[i], val))
|
||||
return i;
|
||||
return vec->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, const char *remote_url,
|
||||
struct strvec *names, struct strvec *urls)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (accept == ACCEPT_ALL)
|
||||
return 1;
|
||||
|
||||
i = strvec_find_index(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 (!strcasecmp(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 repository *repo,
|
||||
struct strvec *accepted,
|
||||
const char *info)
|
||||
{
|
||||
struct strbuf **remotes;
|
||||
char *accept_str;
|
||||
enum accept_promisor accept = ACCEPT_NONE;
|
||||
struct strvec names = STRVEC_INIT;
|
||||
struct strvec urls = STRVEC_INIT;
|
||||
|
||||
if (!git_config_get_string("promisor.acceptfromserver", &accept_str)) {
|
||||
if (!accept_str || !*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
|
||||
warning(_("unknown '%s' value for '%s' config option"),
|
||||
accept_str, "promisor.acceptfromserver");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
for (size_t i = 0; remotes[i]; i++) {
|
||||
struct strbuf **elems;
|
||||
const char *remote_name = NULL;
|
||||
const char *remote_url = NULL;
|
||||
char *decoded_name = NULL;
|
||||
char *decoded_url = NULL;
|
||||
|
||||
strbuf_trim_trailing_ch(remotes[i], ';');
|
||||
elems = strbuf_split_str(remotes[i]->buf, ',', 0);
|
||||
|
||||
for (size_t j = 0; elems[j]; j++) {
|
||||
int res;
|
||||
strbuf_trim_trailing_ch(elems[j], ',');
|
||||
res = skip_prefix(elems[j]->buf, "name=", &remote_name) ||
|
||||
skip_prefix(elems[j]->buf, "url=", &remote_url);
|
||||
if (!res)
|
||||
warning(_("unknown element '%s' from remote info"),
|
||||
elems[j]->buf);
|
||||
}
|
||||
|
||||
if (remote_name)
|
||||
decoded_name = url_percent_decode(remote_name);
|
||||
if (remote_url)
|
||||
decoded_url = url_percent_decode(remote_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_name);
|
||||
free(decoded_url);
|
||||
}
|
||||
|
||||
free(accept_str);
|
||||
strvec_clear(&names);
|
||||
strvec_clear(&urls);
|
||||
strbuf_list_free(remotes);
|
||||
}
|
||||
|
||||
char *promisor_remote_reply(const char *info)
|
||||
{
|
||||
struct strvec accepted = STRVEC_INIT;
|
||||
struct strbuf reply = STRBUF_INIT;
|
||||
|
||||
filter_promisor_remote(the_repository, &accepted, info);
|
||||
|
||||
if (!accepted.nr)
|
||||
return NULL;
|
||||
|
||||
for (size_t i = 0; i < accepted.nr; i++) {
|
||||
if (i)
|
||||
strbuf_addch(&reply, ';');
|
||||
strbuf_addstr_urlencode(&reply, accepted.v[i], allow_unsanitized);
|
||||
}
|
||||
|
||||
strvec_clear(&accepted);
|
||||
|
||||
return strbuf_detach(&reply, NULL);
|
||||
}
|
||||
|
||||
void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes)
|
||||
{
|
||||
struct strbuf **accepted_remotes = strbuf_split_str(remotes, ';', 0);
|
||||
|
||||
for (size_t i = 0; accepted_remotes[i]; i++) {
|
||||
struct promisor_remote *p;
|
||||
char *decoded_remote;
|
||||
|
||||
strbuf_trim_trailing_ch(accepted_remotes[i], ';');
|
||||
decoded_remote = url_percent_decode(accepted_remotes[i]->buf);
|
||||
|
||||
p = repo_promisor_remote_find(r, decoded_remote);
|
||||
if (p)
|
||||
p->accepted = 1;
|
||||
else
|
||||
warning(_("accepted promisor remote '%s' not found"),
|
||||
decoded_remote);
|
||||
|
||||
free(decoded_remote);
|
||||
}
|
||||
|
||||
strbuf_list_free(accepted_remotes);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,13 @@ struct object_id;
|
||||
* Promisor remote linked list
|
||||
*
|
||||
* Information in its fields come from remote.XXX config entries or
|
||||
* from extensions.partialclone.
|
||||
* from extensions.partialclone, except for 'accepted' which comes
|
||||
* from protocol v2 capabilities exchange.
|
||||
*/
|
||||
struct promisor_remote {
|
||||
struct promisor_remote *next;
|
||||
char *partial_clone_filter;
|
||||
unsigned int accepted : 1;
|
||||
const char name[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
@@ -32,4 +34,36 @@ void promisor_remote_get_direct(struct repository *repo,
|
||||
const struct object_id *oids,
|
||||
int oid_nr);
|
||||
|
||||
/*
|
||||
* Prepare a "promisor-remote" advertisement by a server.
|
||||
* Check the value of "promisor.advertise" and maybe the configured
|
||||
* promisor remotes, if any, to prepare information to send in an
|
||||
* advertisement.
|
||||
* Return value is NULL if no promisor remote advertisement should be
|
||||
* made. Otherwise it contains the names and urls of the advertised
|
||||
* promisor remotes separated by ';'
|
||||
*/
|
||||
char *promisor_remote_info(struct repository *repo);
|
||||
|
||||
/*
|
||||
* Prepare a reply to a "promisor-remote" advertisement from a server.
|
||||
* Check the value of "promisor.acceptfromserver" and maybe the
|
||||
* configured promisor remotes, if any, to prepare the reply.
|
||||
* Return value is NULL if no promisor remote from the server
|
||||
* is accepted. Otherwise it contains the names of the accepted promisor
|
||||
* remotes separated by ';'.
|
||||
*/
|
||||
char *promisor_remote_reply(const char *info);
|
||||
|
||||
/*
|
||||
* Set the 'accepted' flag for some promisor remotes. Useful when some
|
||||
* promisor remotes have been accepted by the client.
|
||||
*/
|
||||
void mark_promisor_remotes_as_accepted(struct repository *repo, const char *remotes);
|
||||
|
||||
/*
|
||||
* Has any promisor remote been accepted by the client?
|
||||
*/
|
||||
int repo_has_accepted_promisor_remote(struct repository *r);
|
||||
|
||||
#endif /* PROMISOR_REMOTE_H */
|
||||
|
||||
26
serve.c
26
serve.c
@@ -12,6 +12,7 @@
|
||||
#include "upload-pack.h"
|
||||
#include "bundle-uri.h"
|
||||
#include "trace2.h"
|
||||
#include "promisor-remote.h"
|
||||
|
||||
static int advertise_sid = -1;
|
||||
static int advertise_object_info = -1;
|
||||
@@ -31,6 +32,26 @@ static int agent_advertise(struct repository *r UNUSED,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int promisor_remote_advertise(struct repository *r,
|
||||
struct strbuf *value)
|
||||
{
|
||||
if (value) {
|
||||
char *info = promisor_remote_info(r);
|
||||
if (!info)
|
||||
return 0;
|
||||
strbuf_addstr(value, info);
|
||||
free(info);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void promisor_remote_receive(struct repository *r,
|
||||
const char *remotes)
|
||||
{
|
||||
mark_promisor_remotes_as_accepted(r, remotes);
|
||||
}
|
||||
|
||||
|
||||
static int object_format_advertise(struct repository *r,
|
||||
struct strbuf *value)
|
||||
{
|
||||
@@ -159,6 +180,11 @@ static struct protocol_capability capabilities[] = {
|
||||
.advertise = bundle_uri_advertise,
|
||||
.command = bundle_uri_command,
|
||||
},
|
||||
{
|
||||
.name = "promisor-remote",
|
||||
.advertise = promisor_remote_advertise,
|
||||
.receive = promisor_remote_receive,
|
||||
},
|
||||
};
|
||||
|
||||
void protocol_v2_advertise_capabilities(void)
|
||||
|
||||
16
strbuf.c
16
strbuf.c
@@ -134,6 +134,13 @@ void strbuf_trim_trailing_dir_sep(struct strbuf *sb)
|
||||
sb->buf[sb->len] = '\0';
|
||||
}
|
||||
|
||||
void strbuf_trim_trailing_ch(struct strbuf *sb, int c)
|
||||
{
|
||||
while (sb->len > 0 && sb->buf[sb->len - 1] == c)
|
||||
sb->len--;
|
||||
sb->buf[sb->len] = '\0';
|
||||
}
|
||||
|
||||
void strbuf_trim_trailing_newline(struct strbuf *sb)
|
||||
{
|
||||
if (sb->len > 0 && sb->buf[sb->len - 1] == '\n') {
|
||||
@@ -1082,3 +1089,12 @@ void strbuf_strip_file_from_path(struct strbuf *sb)
|
||||
char *path_sep = find_last_dir_sep(sb->buf);
|
||||
strbuf_setlen(sb, path_sep ? path_sep - sb->buf + 1 : 0);
|
||||
}
|
||||
|
||||
void strbuf_sanitize(struct strbuf *sb)
|
||||
{
|
||||
strbuf_trim(sb);
|
||||
for (size_t i = 0; i < sb->len; i++) {
|
||||
if (sb->buf[i] <= 32 || sb->buf[i] >= 127)
|
||||
sb->buf[i] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
10
strbuf.h
10
strbuf.h
@@ -197,6 +197,9 @@ void strbuf_trim_trailing_dir_sep(struct strbuf *sb);
|
||||
/* Strip trailing LF or CR/LF */
|
||||
void strbuf_trim_trailing_newline(struct strbuf *sb);
|
||||
|
||||
/* Strip trailing character c */
|
||||
void strbuf_trim_trailing_ch(struct strbuf *sb, int c);
|
||||
|
||||
/**
|
||||
* Replace the contents of the strbuf with a reencoded form. Returns -1
|
||||
* on error, 0 on success.
|
||||
@@ -664,6 +667,13 @@ typedef int (*char_predicate)(char ch);
|
||||
void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
|
||||
char_predicate allow_unencoded_fn);
|
||||
|
||||
/*
|
||||
* Trim and replace each character with ascii code below 32 or above
|
||||
* 127 (included) using a dot '.' character. Useful for sending
|
||||
* capabilities.
|
||||
*/
|
||||
void strbuf_sanitize(struct strbuf *sb);
|
||||
|
||||
__attribute__((format (printf,1,2)))
|
||||
int printf_ln(const char *fmt, ...);
|
||||
__attribute__((format (printf,2,3)))
|
||||
|
||||
192
t/t5710-promisor-remote-capability.sh
Executable file
192
t/t5710-promisor-remote-capability.sh
Executable file
@@ -0,0 +1,192 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='handling of promisor remote advertisement'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
# Setup the repository with three commits, this way HEAD is always
|
||||
# available and we can hide commit 1 or 2.
|
||||
test_expect_success 'setup: create "template" repository' '
|
||||
git init template &&
|
||||
test_commit -C template 1 &&
|
||||
test_commit -C template 2 &&
|
||||
test_commit -C template 3 &&
|
||||
test-tool genrandom foo 10240 >template/foo &&
|
||||
git -C template add foo &&
|
||||
git -C template commit -m foo
|
||||
'
|
||||
|
||||
# A bare repo will act as a server repo with unpacked objects.
|
||||
test_expect_success 'setup: create bare "server" repository' '
|
||||
git clone --bare --no-local template server &&
|
||||
mv server/objects/pack/pack-* . &&
|
||||
packfile=$(ls pack-*.pack) &&
|
||||
git -C server unpack-objects --strict <"$packfile"
|
||||
'
|
||||
|
||||
check_missing_objects () {
|
||||
git -C "$1" rev-list --objects --all --missing=print > all.txt &&
|
||||
perl -ne 'print if s/^[?]//' all.txt >missing.txt &&
|
||||
test_line_count = "$2" missing.txt &&
|
||||
test "$3" = "$(cat missing.txt)"
|
||||
}
|
||||
|
||||
initialize_server () {
|
||||
# Repack everything first
|
||||
git -C server -c repack.writebitmaps=false repack -a -d &&
|
||||
|
||||
# Remove promisor file in case they exist, useful when reinitializing
|
||||
rm -rf server/objects/pack/*.promisor &&
|
||||
|
||||
# Repack without the largest object and create a promisor pack on server
|
||||
git -C server -c repack.writebitmaps=false repack -a -d \
|
||||
--filter=blob:limit=5k --filter-to="$(pwd)" &&
|
||||
promisor_file=$(ls server/objects/pack/*.pack | sed "s/\.pack/.promisor/") &&
|
||||
touch "$promisor_file" &&
|
||||
|
||||
# Check that only one object is missing on the server
|
||||
check_missing_objects server 1 "$oid"
|
||||
}
|
||||
|
||||
test_expect_success "setup for testing promisor remote advertisement" '
|
||||
# Create another bare repo called "server2"
|
||||
git init --bare server2 &&
|
||||
|
||||
# Copy the largest object from server to server2
|
||||
obj="HEAD:foo" &&
|
||||
oid="$(git -C server rev-parse $obj)" &&
|
||||
oid_path="$(test_oid_to_path $oid)" &&
|
||||
path="server/objects/$oid_path" &&
|
||||
path2="server2/objects/$oid_path" &&
|
||||
mkdir -p $(dirname "$path2") &&
|
||||
cp "$path" "$path2" &&
|
||||
|
||||
initialize_server &&
|
||||
|
||||
# Configure server2 as promisor remote for server
|
||||
git -C server remote add server2 "file://$(pwd)/server2" &&
|
||||
git -C server config remote.server2.promisor true &&
|
||||
|
||||
git -C server2 config uploadpack.allowFilter true &&
|
||||
git -C server2 config uploadpack.allowAnySHA1InWant true &&
|
||||
git -C server config uploadpack.allowFilter true &&
|
||||
git -C server config uploadpack.allowAnySHA1InWant true
|
||||
'
|
||||
|
||||
test_expect_success "fetch with promisor.advertise set to 'true'" '
|
||||
git -C server config promisor.advertise true &&
|
||||
|
||||
# Clone from server to create a client
|
||||
GIT_NO_LAZY_FETCH=0 git clone -c remote.server2.promisor=true \
|
||||
-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
|
||||
-c remote.server2.url="file://$(pwd)/server2" \
|
||||
-c promisor.acceptfromserver=All \
|
||||
--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 "fetch with promisor.advertise set to 'false'" '
|
||||
git -C server config promisor.advertise false &&
|
||||
|
||||
# Clone from server to create a client
|
||||
GIT_NO_LAZY_FETCH=0 git clone -c remote.server2.promisor=true \
|
||||
-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
|
||||
-c remote.server2.url="file://$(pwd)/server2" \
|
||||
-c promisor.acceptfromserver=All \
|
||||
--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
|
||||
'
|
||||
|
||||
test_expect_success "fetch with promisor.acceptfromserver set to 'None'" '
|
||||
git -C server config promisor.advertise true &&
|
||||
|
||||
# Clone from server to create a client
|
||||
GIT_NO_LAZY_FETCH=0 git clone -c remote.server2.promisor=true \
|
||||
-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
|
||||
-c remote.server2.url="file://$(pwd)/server2" \
|
||||
-c promisor.acceptfromserver=None \
|
||||
--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
|
||||
'
|
||||
|
||||
test_expect_success "fetch 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.server2.promisor=true \
|
||||
-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
|
||||
-c remote.server2.url="file://$(pwd)/server2" \
|
||||
-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 "fetch 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/server2/*" \
|
||||
-c remote.serverTwo.url="file://$(pwd)/server2" \
|
||||
-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
|
||||
'
|
||||
|
||||
test_expect_success "fetch 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.server2.promisor=true \
|
||||
-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
|
||||
-c remote.server2.url="file://$(pwd)/server2" \
|
||||
-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 "fetch with 'KnownUrl' and different remote urls" '
|
||||
ln -s server2 serverTwo &&
|
||||
|
||||
git -C server config promisor.advertise true &&
|
||||
|
||||
# Clone from server to create a client
|
||||
GIT_NO_LAZY_FETCH=0 git clone -c remote.server2.promisor=true \
|
||||
-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
|
||||
-c remote.server2.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 ""
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -35,10 +35,7 @@ static int tr2_cfg_load_patterns(void)
|
||||
|
||||
tr2_cfg_patterns = strbuf_split_buf(envvar, strlen(envvar), ',', -1);
|
||||
for (s = tr2_cfg_patterns; *s; s++) {
|
||||
struct strbuf *buf = *s;
|
||||
|
||||
if (buf->len && buf->buf[buf->len - 1] == ',')
|
||||
strbuf_setlen(buf, buf->len - 1);
|
||||
strbuf_trim_trailing_ch(*s, ',');
|
||||
strbuf_trim_trailing_newline(*s);
|
||||
strbuf_trim(*s);
|
||||
}
|
||||
@@ -74,10 +71,7 @@ static int tr2_load_env_vars(void)
|
||||
|
||||
tr2_cfg_env_vars = strbuf_split_buf(varlist, strlen(varlist), ',', -1);
|
||||
for (s = tr2_cfg_env_vars; *s; s++) {
|
||||
struct strbuf *buf = *s;
|
||||
|
||||
if (buf->len && buf->buf[buf->len - 1] == ',')
|
||||
strbuf_setlen(buf, buf->len - 1);
|
||||
strbuf_trim_trailing_ch(*s, ',');
|
||||
strbuf_trim_trailing_newline(*s);
|
||||
strbuf_trim(*s);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "write-or-die.h"
|
||||
#include "json-writer.h"
|
||||
#include "strmap.h"
|
||||
#include "promisor-remote.h"
|
||||
|
||||
/* Remember to update object flag allocation in object.h */
|
||||
#define THEY_HAVE (1u << 11)
|
||||
@@ -317,6 +318,8 @@ static void create_pack_file(struct upload_pack_data *pack_data,
|
||||
strvec_push(&pack_objects.args, "--delta-base-offset");
|
||||
if (pack_data->use_include_tag)
|
||||
strvec_push(&pack_objects.args, "--include-tag");
|
||||
if (repo_has_accepted_promisor_remote(the_repository))
|
||||
strvec_push(&pack_objects.args, "--missing=allow-promisor");
|
||||
if (pack_data->filter_options.choice) {
|
||||
const char *spec =
|
||||
expand_list_objects_filter_spec(&pack_data->filter_options);
|
||||
|
||||
@@ -24,15 +24,10 @@ const char *git_user_agent_sanitized(void)
|
||||
|
||||
if (!agent) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int i;
|
||||
|
||||
strbuf_addstr(&buf, git_user_agent());
|
||||
strbuf_trim(&buf);
|
||||
for (i = 0; i < buf.len; i++) {
|
||||
if (buf.buf[i] <= 32 || buf.buf[i] >= 127)
|
||||
buf.buf[i] = '.';
|
||||
}
|
||||
agent = buf.buf;
|
||||
strbuf_sanitize(&buf);
|
||||
agent = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
return agent;
|
||||
|
||||
Reference in New Issue
Block a user