Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Documentation/config/promisor.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,13 @@ variable. The fields are checked only if the
`promisor.acceptFromServer` config variable is not set to "None". If
set to "None", this config variable has no effect. See
linkgit:gitprotocol-v2[5].

promisor.createRemotes::
If set to "true", linkgit:git-clone[1] will create entries for
any promisor remotes advertised by the server via the
"promisor-remote" capability. Each remote is added to the new
repository's configuration with its advertised URL, marked as a
promisor remote, and given a default fetch refspec
(`+refs/heads/*:refs/remotes/<name>/*`). Optional fields such as
`partialCloneFilter` and `token` are recorded when present. The
remote is only created if it is not already configured locally.
24 changes: 17 additions & 7 deletions builtin/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#include "hook.h"
#include "bundle.h"
#include "bundle-uri.h"
#include "promisor-remote.h"
#include "connect.h"

/*
* Overall FIXMEs:
Expand Down Expand Up @@ -1585,15 +1587,23 @@ int cmd_clone(int argc,
free(to_free);
}

if (!option_rev)
write_refspec_config(src_ref_prefix, our_head_points_at,
remote_head_points_at, &branch_top);
if (!option_rev)
write_refspec_config(src_ref_prefix, our_head_points_at,
remote_head_points_at, &branch_top);

{
const char *promisor_remote_info;

if (server_feature_v2("promisor-remote", &promisor_remote_info))
promisor_remote_configure_from_info(the_repository,
promisor_remote_info);
}

if (filter_options.choice)
partial_clone_register(remote_name, &filter_options);
if (filter_options.choice)
partial_clone_register(remote_name, &filter_options);

if (is_local)
clone_local(path, git_dir);
if (is_local)
clone_local(path, git_dir);
else if (mapped_refs && complete_refs_before_fetch) {
if (transport_fetch_refs(transport, mapped_refs))
die(_("remote transport reported error"));
Expand Down
116 changes: 102 additions & 14 deletions promisor-remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "trace2.h"
#include "transport.h"
#include "strvec.h"
#include "strbuf.h"
#include "packfile.h"
#include "environment.h"
#include "url.h"
Expand Down Expand Up @@ -769,23 +770,110 @@ char *promisor_remote_reply(const char *info)

void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes)
{
struct string_list accepted_remotes = STRING_LIST_INIT_DUP;
struct string_list_item *item;
struct string_list accepted_remotes = STRING_LIST_INIT_DUP;
struct string_list_item *item;

string_list_split(&accepted_remotes, remotes, ";", -1);
string_list_split(&accepted_remotes, remotes, ";", -1);

for_each_string_list_item(item, &accepted_remotes) {
char *decoded_remote = url_percent_decode(item->string);
struct promisor_remote *p = repo_promisor_remote_find(r, decoded_remote);
for_each_string_list_item(item, &accepted_remotes) {
char *decoded_remote = url_percent_decode(item->string);
struct promisor_remote *p = repo_promisor_remote_find(r, decoded_remote);

if (p)
p->accepted = 1;
else
warning(_("accepted promisor remote '%s' not found"),
decoded_remote);
if (p)
p->accepted = 1;
else
warning(_("accepted promisor remote '%s' not found"),
decoded_remote);

free(decoded_remote);
}
free(decoded_remote);
}

string_list_clear(&accepted_remotes, 0);
}

static int remote_is_already_configured(struct repository *repo, const char *remote_name)
{
const char *value;
char *key = xstrfmt("remote.%s.url", remote_name);
int configured = 0;

if (!repo_config_get_string_tmp(repo, key, &value) && value && *value)
configured = 1;

free(key);
return configured;
}

static void set_remote_config_value(struct repository *repo, const char *remote_name,
const char *suffix, const char *value)
{
char *key = xstrfmt("remote.%s.%s", remote_name, suffix);

repo_config_set(repo, key, value);
free(key);
}

static void set_remote_fetch_default(struct repository *repo, const char *remote_name)
{
char *key = xstrfmt("remote.%s.fetch", remote_name);
struct strbuf value = STRBUF_INIT;

strbuf_addf(&value, "+refs/heads/*:refs/remotes/%s/*", remote_name);
repo_config_set_multivar(repo, key, value.buf, "^$", 0);

free(key);
strbuf_release(&value);
}

static void configure_remote_from_advertisement(struct repository *repo,
struct promisor_info *info)
{
set_remote_config_value(repo, info->name, "url", info->url);
set_remote_config_value(repo, info->name, "promisor", "true");
set_remote_fetch_default(repo, info->name);

if (info->filter)
set_remote_config_value(repo, info->name,
promisor_field_filter, info->filter);

if (info->token)
set_remote_config_value(repo, info->name,
promisor_field_token, info->token);
}

void promisor_remote_configure_from_info(struct repository *repo, const char *info)
{
struct string_list remote_info = STRING_LIST_INIT_DUP;
struct string_list_item *item;
int create_remotes;
int created = 0;

if (!info || !*info)
return;

if (repo_config_get_bool(repo, "promisor.createremotes", &create_remotes) ||
!create_remotes)
return;

string_list_split(&remote_info, info, ";", -1);

for_each_string_list_item(item, &remote_info) {
struct promisor_info *advertised =
parse_one_advertised_remote(item->string);

if (!advertised)
continue;

if (!remote_is_already_configured(repo, advertised->name)) {
configure_remote_from_advertisement(repo, advertised);
created = 1;
}

promisor_info_free(advertised);
}

string_list_clear(&remote_info, 0);

string_list_clear(&accepted_remotes, 0);
if (created)
repo_promisor_remote_reinit(repo);
}
6 changes: 6 additions & 0 deletions promisor-remote.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,10 @@ void mark_promisor_remotes_as_accepted(struct repository *repo, const char *remo
*/
int repo_has_accepted_promisor_remote(struct repository *r);

/*
* Create promisor remotes in the configuration based on the
* "promisor-remote" capability advertisement received from a server.
*/
void promisor_remote_configure_from_info(struct repository *repo, const char *info);

#endif /* PROMISOR_REMOTE_H */
38 changes: 31 additions & 7 deletions t/t5710-promisor-remote-capability.sh
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,10 @@ test_expect_success "clone with promisor.sendFields" '
'

test_expect_success "clone with promisor.checkFields" '
git -C server config promisor.advertise true &&
test_when_finished "rm -rf client" &&
git -C server config promisor.advertise true &&
test_when_finished "rm -rf client" &&

git -C server remote add otherLop "https://invalid.invalid" &&
git -C server remote add otherLop "https://invalid.invalid" &&
git -C server config remote.otherLop.token "fooBar" &&
git -C server config remote.otherLop.stuff "baz" &&
git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" &&
Expand All @@ -357,14 +357,38 @@ test_expect_success "clone with promisor.checkFields" '
test_grep ! "clone> promisor-remote=lop;otherLop" trace &&

# Check that the largest object is still missing on the server
check_missing_objects server 1 "$oid"
check_missing_objects server 1 "$oid"
'

test_expect_success "clone creates promisor remotes from hints" '
git -C server config promisor.advertise true &&
test_when_finished "rm -rf client" &&

test_when_finished "git -C server config --unset-all promisor.sendFields" &&
test_config -C server promisor.sendFields "partialCloneFilter, token" &&
git -C server config remote.lop.partialCloneFilter "blob:none" &&
git -C server config remote.lop.token "lop-token" &&
test_when_finished "git -C server config --unset remote.lop.token" &&

GIT_NO_LAZY_FETCH=0 git clone \
-c promisor.acceptfromserver=All \
-c promisor.createRemotes=true \
--no-local --filter="blob:limit=5k" server client &&

test_cmp_config -C client "file://$(pwd)/lop" remote.lop.url &&
test_cmp_config -C client true remote.lop.promisor &&
test_cmp_config -C client "+refs/heads/*:refs/remotes/lop/*" remote.lop.fetch &&
test_cmp_config -C client blob:none remote.lop.partialCloneFilter &&
test_cmp_config -C client lop-token remote.lop.token &&

check_missing_objects 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 &&
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 \
# 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=All \
Expand Down
Loading