Merge branch 'hn/fetch-refmap' into seen
In a shallow repository, adding a new remote and fetching from it used to eagerly fetch all remote branches. The default refspec configured for remotes added in shallow repositories has been changed to rely on `remote.<name>.refmap` rather than `remote.<name>.fetch`, pulling only those remote branches tracked by local branches. * hn/fetch-refmap: remote: default to --limited-fetch in a shallow repository remote: add "git remote add --limited-fetch" fetch: infer branches to fetch from a refmap-only remote fetch: extract collect_upstream_from_remote() helper fetch: add remote.<name>.refmapseen
commit
091dad56d7
|
|
@ -33,6 +33,14 @@ remote.<name>.fetch::
|
|||
The default set of "refspec" for linkgit:git-fetch[1]. See
|
||||
linkgit:git-fetch[1].
|
||||
|
||||
remote.<name>.refmap::
|
||||
The default value of the `--refmap` option for linkgit:git-fetch[1].
|
||||
Used to map remote refs being fetched to remote-tracking refs to
|
||||
store. If `remote.<name>.fetch` is not set either, a refspec-less
|
||||
fetch infers what to fetch from local branches built on this
|
||||
remote, instead of fetching every branch it has. See the
|
||||
`--refmap` entry in linkgit:git-fetch[1].
|
||||
|
||||
remote.<name>.push::
|
||||
The default set of "refspec" for linkgit:git-push[1]. See
|
||||
linkgit:git-push[1].
|
||||
|
|
|
|||
|
|
@ -244,6 +244,11 @@ endif::git-pull[]
|
|||
refspecs and rely entirely on the refspecs supplied as
|
||||
command-line arguments. See section on "Configured Remote-tracking
|
||||
Branches" for details.
|
||||
+
|
||||
When a refmap is active (from `--refmap` or `remote.<name>.refmap`) but
|
||||
nothing to fetch is specified on the command line, nor is there a
|
||||
`remote.<name>.fetch`, branches from the remote that are used as the
|
||||
`@{upstream}` of our local branches are fetched.
|
||||
|
||||
`-t`::
|
||||
`--tags`::
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ SYNOPSIS
|
|||
--------
|
||||
[synopsis]
|
||||
git remote [-v | --verbose]
|
||||
git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <URL>
|
||||
git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] [--[no-]limited-fetch] <name> <URL>
|
||||
git remote rename [--[no-]progress] <old> <new>
|
||||
git remote remove <name>
|
||||
git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
|
||||
|
|
@ -57,6 +57,11 @@ Add a remote named _<name>_ for the repository at
|
|||
_<URL>_. The command `git fetch <name>` can then be used to create and
|
||||
update remote-tracking branches `<name>/<branch>`.
|
||||
+
|
||||
If the repository is already a shallow repository (see linkgit:git-clone[1]
|
||||
`--depth`) and neither `-t`, `--mirror` nor `--no-limited-fetch` is given,
|
||||
`--limited-fetch` is turned on by default, so that `git fetch <name>` does
|
||||
not need to negotiate history for every branch the remote has.
|
||||
+
|
||||
With `-f` option, `git fetch <name>` is run immediately after
|
||||
the remote information is set up.
|
||||
+
|
||||
|
|
@ -75,6 +80,13 @@ the `refs/remotes/<name>/` namespace, a refspec to track only _<branch>_
|
|||
is created. You can give more than one `-t <branch>` to track
|
||||
multiple branches without grabbing all branches.
|
||||
+
|
||||
With `--limited-fetch` option, instead of a `remote.<name>.fetch` refspec
|
||||
that tracks all branches, `remote.<name>.refmap` is set up so that a
|
||||
refspec-less `git fetch <name>` only fetches branches our local branches
|
||||
are built on. See the `--refmap` entry in linkgit:git-fetch[1] for
|
||||
details. `--no-limited-fetch` explicitly disables this, overriding the
|
||||
shallow-repository default described above.
|
||||
+
|
||||
With `-m <master>` option, a symbolic-ref `refs/remotes/<name>/HEAD` is set
|
||||
up to point at remote's _<master>_ branch. See also the set-head command.
|
||||
+
|
||||
|
|
|
|||
117
builtin/fetch.c
117
builtin/fetch.c
|
|
@ -553,6 +553,14 @@ static struct ref *get_ref_map(struct remote *remote,
|
|||
struct ref *rm;
|
||||
struct ref *ref_map = NULL;
|
||||
struct ref **tail = &ref_map;
|
||||
/*
|
||||
* The --refmap command line option, if given, takes precedence
|
||||
* over remote.<name>.refmap.
|
||||
*/
|
||||
struct refspec *effective_refmap =
|
||||
refmap.nr ? &refmap : remote ? &remote->refmap : NULL;
|
||||
struct refspec inferred_rs;
|
||||
int infer_from_refmap = 0;
|
||||
|
||||
/* opportunistically-updated references: */
|
||||
struct ref *orefs = NULL, **oref_tail = &orefs;
|
||||
|
|
@ -560,15 +568,32 @@ static struct ref *get_ref_map(struct remote *remote,
|
|||
struct hashmap existing_refs;
|
||||
int existing_refs_populated = 0;
|
||||
|
||||
refspec_init_fetch(&inferred_rs, the_hash_algo);
|
||||
|
||||
filter_prefetch_refspec(rs);
|
||||
if (remote)
|
||||
filter_prefetch_refspec(&remote->fetch);
|
||||
|
||||
if (!rs->nr && remote && !remote->fetch.nr &&
|
||||
effective_refmap && effective_refmap->nr) {
|
||||
struct string_list tracked = STRING_LIST_INIT_DUP;
|
||||
struct string_list_item *item;
|
||||
|
||||
branches_tracking_remote(the_repository, remote, &tracked);
|
||||
for_each_string_list_item(item, &tracked)
|
||||
refspec_append(&inferred_rs, item->string);
|
||||
string_list_clear(&tracked, 0);
|
||||
|
||||
rs = &inferred_rs;
|
||||
infer_from_refmap = 1;
|
||||
}
|
||||
|
||||
if (rs->nr) {
|
||||
struct refspec *fetch_refspec;
|
||||
|
||||
for (i = 0; i < rs->nr; i++) {
|
||||
get_fetch_map(remote_refs, &rs->items[i], &tail, 0);
|
||||
get_fetch_map(remote_refs, &rs->items[i], &tail,
|
||||
infer_from_refmap);
|
||||
if (rs->items[i].dst && rs->items[i].dst[0])
|
||||
*autotags = 1;
|
||||
}
|
||||
|
|
@ -596,13 +621,15 @@ static struct ref *get_ref_map(struct remote *remote,
|
|||
* by ref_remove_duplicates() in favor of one of these
|
||||
* opportunistic entries with FETCH_HEAD_IGNORE.
|
||||
*/
|
||||
if (refmap.nr)
|
||||
fetch_refspec = &refmap;
|
||||
if (effective_refmap && effective_refmap->nr)
|
||||
fetch_refspec = effective_refmap;
|
||||
else
|
||||
fetch_refspec = &remote->fetch;
|
||||
|
||||
for (i = 0; i < fetch_refspec->nr; i++)
|
||||
get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
|
||||
} else if (infer_from_refmap) {
|
||||
/* Already fully handled above. */
|
||||
} else if (refmap.nr) {
|
||||
die("--refmap option is only meaningful with command-line refspec(s)");
|
||||
} else {
|
||||
|
|
@ -698,6 +725,7 @@ static struct ref *get_ref_map(struct remote *remote,
|
|||
if (existing_refs_populated)
|
||||
hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent);
|
||||
|
||||
refspec_clear(&inferred_rs);
|
||||
return ref_map;
|
||||
}
|
||||
|
||||
|
|
@ -1970,6 +1998,26 @@ static void collect_updated_tips(struct oidset *tips, struct ref *ref_map)
|
|||
}
|
||||
}
|
||||
|
||||
static int late_resolve_frh(const struct transport *transport,
|
||||
const struct fetch_config *config)
|
||||
{
|
||||
/*
|
||||
* See remote.c's handling of remote.<name>.followRemoteHEAD
|
||||
* for the analogous, still-unresolved case.
|
||||
*/
|
||||
|
||||
int follow_remote_head = FOLLOW_REMOTE_NEVER;
|
||||
|
||||
if (transport->remote->follow_remote_head)
|
||||
follow_remote_head = transport->remote->follow_remote_head;
|
||||
else if (config->follow_remote_head_seen)
|
||||
follow_remote_head =
|
||||
get_follow_remote_head(config->follow_remote_head_raw);
|
||||
else
|
||||
follow_remote_head = BUILTIN_FOLLOW_REMOTE_HEAD_DFLT;
|
||||
return follow_remote_head;
|
||||
}
|
||||
|
||||
static int do_fetch(struct transport *transport,
|
||||
struct refspec *rs,
|
||||
const struct fetch_config *config,
|
||||
|
|
@ -2007,31 +2055,48 @@ static int do_fetch(struct transport *transport,
|
|||
|
||||
if (rs->nr) {
|
||||
refspec_ref_prefixes(rs, &transport_ls_refs_options.ref_prefixes);
|
||||
} else {
|
||||
struct branch *branch = branch_get(NULL);
|
||||
} else if (transport->remote->fetch.nr) {
|
||||
struct string_list tracked = STRING_LIST_INIT_DUP;
|
||||
struct string_list_item *item;
|
||||
|
||||
if (transport->remote->fetch.nr) {
|
||||
refspec_ref_prefixes(&transport->remote->fetch,
|
||||
&transport_ls_refs_options.ref_prefixes);
|
||||
/*
|
||||
* See remote.c's handling of remote.<name>.followRemoteHEAD
|
||||
* for the analogous, still-unresolved case.
|
||||
*/
|
||||
if (transport->remote->follow_remote_head)
|
||||
follow_remote_head = transport->remote->follow_remote_head;
|
||||
else if (config->follow_remote_head_seen)
|
||||
follow_remote_head = get_follow_remote_head(config->follow_remote_head_raw);
|
||||
else
|
||||
follow_remote_head = BUILTIN_FOLLOW_REMOTE_HEAD_DFLT;
|
||||
}
|
||||
if (branch && branch_has_merge_config(branch) &&
|
||||
!strcmp(branch->remote_name, transport->remote->name)) {
|
||||
int i;
|
||||
for (i = 0; i < branch->merge_nr; i++) {
|
||||
strvec_push(&transport_ls_refs_options.ref_prefixes,
|
||||
branch->merge[i]->src);
|
||||
}
|
||||
refspec_ref_prefixes(&transport->remote->fetch,
|
||||
&transport_ls_refs_options.ref_prefixes);
|
||||
|
||||
follow_remote_head = late_resolve_frh(transport, config);
|
||||
|
||||
/*
|
||||
* The configured refspec may not cover the current
|
||||
* branch's upstream (e.g. a narrowed -t refspec), so
|
||||
* make sure we can still fetch it regardless.
|
||||
*/
|
||||
collect_upstream_from_remote(the_repository, &tracked,
|
||||
transport->remote, NULL);
|
||||
for_each_string_list_item(item, &tracked)
|
||||
strvec_push(&transport_ls_refs_options.ref_prefixes,
|
||||
item->string);
|
||||
string_list_clear(&tracked, 0);
|
||||
} else {
|
||||
/*
|
||||
* The --refmap command line option, if given, takes
|
||||
* precedence over remote.<name>.refmap.
|
||||
*/
|
||||
struct refspec *effective_refmap = refmap.nr ? &refmap :
|
||||
&transport->remote->refmap;
|
||||
struct string_list tracked = STRING_LIST_INIT_DUP;
|
||||
struct string_list_item *item;
|
||||
|
||||
if (effective_refmap->nr) {
|
||||
branches_tracking_remote(the_repository,
|
||||
transport->remote, &tracked);
|
||||
follow_remote_head = late_resolve_frh(transport, config);
|
||||
} else {
|
||||
collect_upstream_from_remote(the_repository, &tracked,
|
||||
transport->remote, NULL);
|
||||
}
|
||||
for_each_string_list_item(item, &tracked)
|
||||
strvec_push(&transport_ls_refs_options.ref_prefixes,
|
||||
item->string);
|
||||
string_list_clear(&tracked, 0);
|
||||
|
||||
/*
|
||||
* If there are no refs specified to fetch, then we just
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "rebase.h"
|
||||
#include "refs.h"
|
||||
#include "refspec.h"
|
||||
#include "shallow.h"
|
||||
#include "odb.h"
|
||||
#include "strvec.h"
|
||||
#include "commit-reach.h"
|
||||
|
|
@ -179,6 +180,7 @@ static int add(int argc, const char **argv, const char *prefix,
|
|||
{
|
||||
int fetch = 0, fetch_tags = TAGS_DEFAULT;
|
||||
unsigned mirror = MIRROR_NONE;
|
||||
int limited_fetch = -1; /* unspecified */
|
||||
struct string_list track = STRING_LIST_INIT_NODUP;
|
||||
const char *master = NULL;
|
||||
struct remote *remote;
|
||||
|
|
@ -198,6 +200,8 @@ static int add(int argc, const char **argv, const char *prefix,
|
|||
OPT_CALLBACK_F(0, "mirror", &mirror, "(push|fetch)",
|
||||
N_("set up remote as a mirror to push to or fetch from"),
|
||||
PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt),
|
||||
OPT_BOOL(0, "limited-fetch", &limited_fetch,
|
||||
N_("fetch only the branches we build on, instead of every branch")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
|
@ -211,6 +215,10 @@ static int add(int argc, const char **argv, const char *prefix,
|
|||
die(_("specifying a master branch makes no sense with --mirror"));
|
||||
if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
|
||||
die(_("specifying branches to track makes sense only with fetch mirrors"));
|
||||
if (limited_fetch == 1 && track.nr)
|
||||
die(_("--limited-fetch does not make sense with -t/--track"));
|
||||
if (limited_fetch == 1 && mirror)
|
||||
die(_("--limited-fetch does not make sense with --mirror"));
|
||||
|
||||
name = argv[0];
|
||||
url = argv[1];
|
||||
|
|
@ -230,13 +238,24 @@ static int add(int argc, const char **argv, const char *prefix,
|
|||
repo_config_set(the_repository, buf.buf, url);
|
||||
|
||||
if (!mirror || mirror & MIRROR_FETCH) {
|
||||
int use_limited_fetch = mirror == MIRROR_NONE && track.nr == 0 &&
|
||||
(limited_fetch == 1 ||
|
||||
(limited_fetch == -1 &&
|
||||
is_repository_shallow(the_repository)));
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s.fetch", name);
|
||||
if (track.nr == 0)
|
||||
string_list_append(&track, "*");
|
||||
for (size_t i = 0; i < track.nr; i++) {
|
||||
add_branch(buf.buf, track.items[i].string,
|
||||
name, mirror, &buf2);
|
||||
if (use_limited_fetch) {
|
||||
strbuf_addf(&buf, "remote.%s.refmap", name);
|
||||
strbuf_reset(&buf2);
|
||||
strbuf_addf(&buf2, "+refs/heads/*:refs/remotes/%s/*", name);
|
||||
repo_config_set(the_repository, buf.buf, buf2.buf);
|
||||
} else {
|
||||
strbuf_addf(&buf, "remote.%s.fetch", name);
|
||||
if (track.nr == 0)
|
||||
string_list_append(&track, "*");
|
||||
for (size_t i = 0; i < track.nr; i++)
|
||||
add_branch(buf.buf, track.items[i].string,
|
||||
name, mirror, &buf2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
51
remote.c
51
remote.c
|
|
@ -153,6 +153,7 @@ static struct remote *make_remote(struct remote_state *remote_state,
|
|||
ret->name = xstrndup(name, len);
|
||||
refspec_init_push(&ret->push, the_hash_algo);
|
||||
refspec_init_fetch(&ret->fetch, the_hash_algo);
|
||||
refspec_init_fetch(&ret->refmap, the_hash_algo);
|
||||
string_list_init_dup(&ret->server_options);
|
||||
string_list_init_dup(&ret->negotiation_restrict);
|
||||
string_list_init_dup(&ret->negotiation_include);
|
||||
|
|
@ -177,6 +178,7 @@ static void remote_clear(struct remote *remote)
|
|||
|
||||
refspec_clear(&remote->push);
|
||||
refspec_clear(&remote->fetch);
|
||||
refspec_clear(&remote->refmap);
|
||||
|
||||
free((char *)remote->receivepack);
|
||||
free((char *)remote->uploadpack);
|
||||
|
|
@ -540,6 +542,12 @@ static int handle_config(const char *key, const char *value,
|
|||
return -1;
|
||||
refspec_append(&remote->fetch, v);
|
||||
free(v);
|
||||
} else if (!strcmp(subkey, "refmap")) {
|
||||
char *v;
|
||||
if (git_config_string(&v, key, value))
|
||||
return -1;
|
||||
refspec_append(&remote->refmap, v);
|
||||
free(v);
|
||||
} else if (!strcmp(subkey, "receivepack")) {
|
||||
char *v;
|
||||
if (git_config_string(&v, key, value))
|
||||
|
|
@ -996,7 +1004,9 @@ void ref_push_report_free(struct ref_push_report *report)
|
|||
|
||||
int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)
|
||||
{
|
||||
return refspec_find_match(&remote->fetch, refspec);
|
||||
if (remote->fetch.nr)
|
||||
return refspec_find_match(&remote->fetch, refspec);
|
||||
return refspec_find_match(&remote->refmap, refspec);
|
||||
}
|
||||
|
||||
static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
|
||||
|
|
@ -1962,6 +1972,45 @@ int branch_merge_matches(struct branch *branch,
|
|||
return refname_match(branch->merge[i]->src, refname);
|
||||
}
|
||||
|
||||
void collect_upstream_from_remote(struct repository *repo,
|
||||
struct string_list *tracked,
|
||||
struct remote *remote,
|
||||
const char *refname)
|
||||
{
|
||||
struct branch *branch = repo_branch_get(repo, refname);
|
||||
|
||||
if (!branch_has_merge_config(branch) ||
|
||||
strcmp(branch->remote_name, remote->name))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < branch->merge_nr; i++)
|
||||
string_list_insert(tracked, branch->merge[i]->src);
|
||||
}
|
||||
|
||||
struct branches_tracking_remote_cb_data {
|
||||
struct repository *repo;
|
||||
struct remote *remote;
|
||||
struct string_list *tracked;
|
||||
};
|
||||
|
||||
static int add_if_tracking_remote(const struct reference *ref, void *cb_data)
|
||||
{
|
||||
struct branches_tracking_remote_cb_data *data = cb_data;
|
||||
|
||||
collect_upstream_from_remote(data->repo, data->tracked, data->remote,
|
||||
ref->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void branches_tracking_remote(struct repository *repo, struct remote *remote,
|
||||
struct string_list *tracked)
|
||||
{
|
||||
struct branches_tracking_remote_cb_data data = { repo, remote, tracked };
|
||||
|
||||
refs_for_each_branch_ref(get_main_ref_store(repo),
|
||||
add_if_tracking_remote, &data);
|
||||
}
|
||||
|
||||
__attribute__((format (printf,2,3)))
|
||||
static char *error_buf(struct strbuf *err, const char *fmt, ...)
|
||||
{
|
||||
|
|
|
|||
19
remote.h
19
remote.h
|
|
@ -90,6 +90,12 @@ struct remote {
|
|||
|
||||
struct refspec fetch;
|
||||
|
||||
/*
|
||||
* How to map refs fetched without an explicit destination into our
|
||||
* own namespace, the same as the --refmap command line option.
|
||||
*/
|
||||
struct refspec refmap;
|
||||
|
||||
/*
|
||||
* The setting for whether to fetch tags (as a separate rule from the
|
||||
* configured refspecs);
|
||||
|
|
@ -357,6 +363,19 @@ int branch_has_merge_config(struct branch *branch);
|
|||
|
||||
int branch_merge_matches(struct branch *, int n, const char *);
|
||||
|
||||
/*
|
||||
* If refname's branch builds on remote, add its upstream on remote to
|
||||
* tracked. A NULL refname means the current branch.
|
||||
*/
|
||||
void collect_upstream_from_remote(struct repository *repo,
|
||||
struct string_list *tracked,
|
||||
struct remote *remote,
|
||||
const char *refname);
|
||||
|
||||
/* fills tracked with the refname of every local branch's upstream on remote */
|
||||
void branches_tracking_remote(struct repository *repo, struct remote *remote,
|
||||
struct string_list *tracked);
|
||||
|
||||
/* list of the remote in a group as configured */
|
||||
struct remote_group_data {
|
||||
const char *name;
|
||||
|
|
|
|||
|
|
@ -738,6 +738,7 @@ integration_tests = [
|
|||
't5582-fetch-negative-refspec.sh',
|
||||
't5583-push-branches.sh',
|
||||
't5584-http-429-retry.sh',
|
||||
't5586-fetch-refmap.sh',
|
||||
't5600-clone-fail-cleanup.sh',
|
||||
't5601-clone.sh',
|
||||
't5602-clone-remote-exec.sh',
|
||||
|
|
|
|||
|
|
@ -137,6 +137,82 @@ test_expect_success 'filters are listed by git remote -v only' '
|
|||
test_grep ! "\[blob:none\]" out
|
||||
'
|
||||
|
||||
test_expect_success 'add remote -t keeps an explicit refspec in a shallow repository' '
|
||||
test_when_finished "rm -rf shallow-add" &&
|
||||
git clone --no-local --depth=1 --branch main --single-branch \
|
||||
one shallow-add &&
|
||||
(
|
||||
cd shallow-add &&
|
||||
git remote add -t main upstream ../two &&
|
||||
test_cmp_config "+refs/heads/main:refs/remotes/upstream/main" \
|
||||
remote.upstream.fetch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'add remote keeps the wildcard refspec in a full repository' '
|
||||
test_when_finished "rm -rf full-add" &&
|
||||
git clone --no-local one full-add &&
|
||||
(
|
||||
cd full-add &&
|
||||
git remote add upstream ../two &&
|
||||
test_cmp_config "+refs/heads/*:refs/remotes/upstream/*" \
|
||||
remote.upstream.fetch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'a remote added in a shallow repository defaults to --limited-fetch' '
|
||||
test_when_finished "rm -rf shallow-add" &&
|
||||
git clone --no-local --depth=1 --branch main --single-branch \
|
||||
one shallow-add &&
|
||||
(
|
||||
cd shallow-add &&
|
||||
git remote add upstream ../two &&
|
||||
test_cmp_config "+refs/heads/*:refs/remotes/upstream/*" \
|
||||
remote.upstream.refmap &&
|
||||
test_must_fail git config get remote.upstream.fetch &&
|
||||
git fetch upstream main &&
|
||||
git branch --set-upstream-to=upstream/main &&
|
||||
test_cmp_config upstream branch.main.remote &&
|
||||
test_cmp_config refs/heads/main branch.main.merge &&
|
||||
git fetch upstream &&
|
||||
git for-each-ref --format="%(refname)" refs/remotes/upstream >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/remotes/upstream/HEAD
|
||||
refs/remotes/upstream/main
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success '--no-limited-fetch overrides the shallow-repository default' '
|
||||
test_when_finished "rm -rf shallow-add" &&
|
||||
git clone --no-local --depth=1 --branch main --single-branch \
|
||||
one shallow-add &&
|
||||
(
|
||||
cd shallow-add &&
|
||||
git remote add --no-limited-fetch upstream ../two &&
|
||||
test_cmp_config "+refs/heads/*:refs/remotes/upstream/*" \
|
||||
remote.upstream.fetch &&
|
||||
test_must_fail git config get remote.upstream.refmap
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success '--limited-fetch works in a full repository too' '
|
||||
test_when_finished "rm -rf full-add" &&
|
||||
git clone --no-local one full-add &&
|
||||
(
|
||||
cd full-add &&
|
||||
git remote add --limited-fetch upstream ../two &&
|
||||
test_cmp_config "+refs/heads/*:refs/remotes/upstream/*" \
|
||||
remote.upstream.refmap &&
|
||||
test_must_fail git config get remote.upstream.fetch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success '--limited-fetch conflicts with -t' '
|
||||
test_must_fail git remote add --limited-fetch -t main upstream ../two
|
||||
'
|
||||
|
||||
test_expect_success 'check remote-tracking' '
|
||||
(
|
||||
cd test &&
|
||||
|
|
|
|||
|
|
@ -927,6 +927,69 @@ test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
|
|||
)
|
||||
'
|
||||
|
||||
test_expect_success 'remote.*.refmap acts like --refmap on the command line' '
|
||||
test_when_finished "git -C three config --unset remote.origin.refmap" &&
|
||||
git branch -f side &&
|
||||
git -C three config remote.origin.refmap \
|
||||
"refs/heads/*:refs/remotes/other/*" &&
|
||||
(
|
||||
cd three &&
|
||||
git update-ref refs/remotes/origin/main base-origin-main &&
|
||||
o=$(git rev-parse --verify refs/remotes/origin/main) &&
|
||||
git fetch origin main &&
|
||||
n=$(git rev-parse --verify refs/remotes/origin/main) &&
|
||||
test "$o" = "$n" &&
|
||||
test_must_fail git rev-parse --verify refs/remotes/origin/side &&
|
||||
git rev-parse --verify refs/remotes/other/main
|
||||
)
|
||||
'
|
||||
|
||||
check_fetched_refs () {
|
||||
git for-each-ref --format="%(refname)" refs/remotes/ >actual &&
|
||||
cat >expect &&
|
||||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_expect_success 'remote.<name>.refmap without tracking (baseline)' '
|
||||
test_when_finished "rm -fr fetch-refmap-baseline fetch-refmap-upstream" &&
|
||||
git init -b main fetch-refmap-upstream &&
|
||||
test_commit -C fetch-refmap-upstream base &&
|
||||
git -C fetch-refmap-upstream branch other &&
|
||||
git init fetch-refmap-baseline &&
|
||||
(
|
||||
cd fetch-refmap-baseline &&
|
||||
git remote add origin ../fetch-refmap-upstream &&
|
||||
|
||||
# Without fetch refspec, but with fetch refmap.
|
||||
git config --unset-all remote.origin.fetch &&
|
||||
git config remote.origin.refmap "+refs/heads/*:refs/remotes/origin/*" &&
|
||||
|
||||
# Nothing tracked, nothing fetched, no error.
|
||||
git fetch origin &&
|
||||
check_fetched_refs <<-\EOF &&
|
||||
EOF
|
||||
|
||||
# Nothing tracked, explicit ref on the command line.
|
||||
git fetch origin main &&
|
||||
check_fetched_refs <<-\EOF &&
|
||||
refs/remotes/origin/main
|
||||
EOF
|
||||
|
||||
# With both refmap and fetch configured, remote.<name>.fetch
|
||||
# wins: a refspec-less fetch follows it as usual, and
|
||||
# remote.<name>.refmap plays no part in deciding what to
|
||||
# fetch, only in remapping something that is already being
|
||||
# fetched by name.
|
||||
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
|
||||
git fetch origin &&
|
||||
check_fetched_refs <<-\EOF
|
||||
refs/remotes/origin/HEAD
|
||||
refs/remotes/origin/main
|
||||
refs/remotes/origin/other
|
||||
EOF
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
|
||||
git branch -f side &&
|
||||
(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='"git fetch" with a remote.<name>.refmap but no remote.<name>.fetch
|
||||
|
||||
When a remote has a refmap configured but no fetch refspec, a
|
||||
refspec-less fetch infers what to fetch from the local branches whose
|
||||
@{upstream} is on that remote.
|
||||
'
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_commit main-1 &&
|
||||
test_commit main-2 &&
|
||||
git checkout -b side main-1 &&
|
||||
test_commit side-1 &&
|
||||
git checkout -b next main-1 &&
|
||||
test_commit next-1 &&
|
||||
git checkout main
|
||||
'
|
||||
|
||||
test_expect_success 'clone shallow and single-branch, then add a second remote' '
|
||||
git clone --no-local --depth=1 --branch main --single-branch . client &&
|
||||
(
|
||||
cd client &&
|
||||
git remote add upstream .. &&
|
||||
test_might_fail git config unset remote.upstream.fetch &&
|
||||
git config remote.upstream.refmap \
|
||||
"+refs/heads/*:refs/remotes/upstream/*"
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'a bare fetch needs nothing until a branch is tracked' '
|
||||
(
|
||||
cd client &&
|
||||
git fetch upstream &&
|
||||
git for-each-ref --format="%(refname)" refs/remotes/upstream >actual &&
|
||||
test_must_be_empty actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'an explicit one-time fetch lets a branch be tracked' '
|
||||
(
|
||||
cd client &&
|
||||
git fetch upstream main &&
|
||||
git branch --set-upstream-to=upstream/main &&
|
||||
test_cmp_config upstream branch.main.remote &&
|
||||
test_cmp_config refs/heads/main branch.main.merge
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'a branch checked out from a one-time fetch is kept updated by later plain fetches' '
|
||||
(
|
||||
cd client &&
|
||||
git fetch upstream side:refs/remotes/upstream/side &&
|
||||
git branch side-topic upstream/side
|
||||
) &&
|
||||
git checkout side &&
|
||||
test_commit side-2 &&
|
||||
git checkout main &&
|
||||
(
|
||||
cd client &&
|
||||
git fetch upstream &&
|
||||
git for-each-ref --format="%(refname)" refs/remotes/upstream >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/remotes/upstream/HEAD
|
||||
refs/remotes/upstream/main
|
||||
refs/remotes/upstream/side
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
git rev-parse refs/remotes/upstream/side >actual-oid &&
|
||||
git -C .. rev-parse side >expect-oid &&
|
||||
test_cmp expect-oid actual-oid
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'a second branch tracking the same upstream branch does not fetch it twice' '
|
||||
(
|
||||
cd client &&
|
||||
git branch side-topic-2 upstream/side &&
|
||||
git fetch upstream &&
|
||||
git for-each-ref --format="%(refname)" refs/remotes/upstream >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/remotes/upstream/HEAD
|
||||
refs/remotes/upstream/main
|
||||
refs/remotes/upstream/side
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'a branch tracking a different remote is not fetched from upstream' '
|
||||
(
|
||||
cd client &&
|
||||
git remote add other .. &&
|
||||
git fetch other next:refs/remotes/other/next &&
|
||||
git branch next-topic other/next &&
|
||||
git fetch upstream &&
|
||||
git for-each-ref --format="%(refname)" refs/remotes/upstream >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/remotes/upstream/HEAD
|
||||
refs/remotes/upstream/main
|
||||
refs/remotes/upstream/side
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'git remote show does not choke on a refmap-only remote' '
|
||||
(
|
||||
cd client &&
|
||||
git remote show upstream
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
Loading…
Reference in New Issue