Merge branch 'jc/transport-do-not-use-connect-twice-in-fetch'

The auto-tag-following code in "git fetch" tries to reuse the same
transport twice when the serving end does not cooperate and does
not give tags that point to commits that are asked for as part of
the primary transfer.  Unfortunately, Git-aware transport helper
interface is not designed to be used more than once, hence this
does not work over smart-http transfer.

* jc/transport-do-not-use-connect-twice-in-fetch:
  builtin/fetch.c: Fix a sparse warning
  fetch: work around "transport-take-over" hack
  fetch: refactor code that fetches leftover tags
  fetch: refactor code that prepares a transport
  fetch: rename file-scope global "transport" to "gtransport"
  t5802: add test for connect helper
This commit is contained in:
Junio C Hamano 2013-09-09 14:50:37 -07:00
commit 20419de969
4 changed files with 136 additions and 33 deletions

View File

@ -40,7 +40,8 @@ static int tags = TAGS_DEFAULT, unshallow;
static const char *depth; static const char *depth;
static const char *upload_pack; static const char *upload_pack;
static struct strbuf default_rla = STRBUF_INIT; static struct strbuf default_rla = STRBUF_INIT;
static struct transport *transport; static struct transport *gtransport;
static struct transport *gsecondary;
static const char *submodule_prefix = ""; static const char *submodule_prefix = "";
static const char *recurse_submodules_default; static const char *recurse_submodules_default;
@ -108,8 +109,10 @@ static struct option builtin_fetch_options[] = {
static void unlock_pack(void) static void unlock_pack(void)
{ {
if (transport) if (gtransport)
transport_unlock_pack(transport); transport_unlock_pack(gtransport);
if (gsecondary)
transport_unlock_pack(gsecondary);
} }
static void unlock_pack_on_signal(int signo) static void unlock_pack_on_signal(int signo)
@ -733,6 +736,48 @@ static int truncate_fetch_head(void)
return 0; return 0;
} }
static void set_option(struct transport *transport, const char *name, const char *value)
{
int r = transport_set_option(transport, name, value);
if (r < 0)
die(_("Option \"%s\" value \"%s\" is not valid for %s"),
name, value, transport->url);
if (r > 0)
warning(_("Option \"%s\" is ignored for %s\n"),
name, transport->url);
}
static struct transport *prepare_transport(struct remote *remote)
{
struct transport *transport;
transport = transport_get(remote, NULL);
transport_set_verbosity(transport, verbosity, progress);
if (upload_pack)
set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
if (keep)
set_option(transport, TRANS_OPT_KEEP, "yes");
if (depth)
set_option(transport, TRANS_OPT_DEPTH, depth);
return transport;
}
static void backfill_tags(struct transport *transport, struct ref *ref_map)
{
if (transport->cannot_reuse) {
gsecondary = prepare_transport(transport->remote);
transport = gsecondary;
}
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
fetch_refs(transport, ref_map);
if (gsecondary) {
transport_disconnect(gsecondary);
gsecondary = NULL;
}
}
static int do_fetch(struct transport *transport, static int do_fetch(struct transport *transport,
struct refspec *refs, int ref_count) struct refspec *refs, int ref_count)
{ {
@ -819,11 +864,8 @@ static int do_fetch(struct transport *transport,
struct ref **tail = &ref_map; struct ref **tail = &ref_map;
ref_map = NULL; ref_map = NULL;
find_non_local_tags(transport, &ref_map, &tail); find_non_local_tags(transport, &ref_map, &tail);
if (ref_map) { if (ref_map)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); backfill_tags(transport, ref_map);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
fetch_refs(transport, ref_map);
}
free_refs(ref_map); free_refs(ref_map);
} }
@ -832,17 +874,6 @@ static int do_fetch(struct transport *transport,
return retcode; return retcode;
} }
static void set_option(const char *name, const char *value)
{
int r = transport_set_option(transport, name, value);
if (r < 0)
die(_("Option \"%s\" value \"%s\" is not valid for %s"),
name, value, transport->url);
if (r > 0)
warning(_("Option \"%s\" is ignored for %s\n"),
name, transport->url);
}
static int get_one_remote_for_fetch(struct remote *remote, void *priv) static int get_one_remote_for_fetch(struct remote *remote, void *priv)
{ {
struct string_list *list = priv; struct string_list *list = priv;
@ -965,26 +996,18 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
die(_("No remote repository specified. Please, specify either a URL or a\n" die(_("No remote repository specified. Please, specify either a URL or a\n"
"remote name from which new revisions should be fetched.")); "remote name from which new revisions should be fetched."));
transport = transport_get(remote, NULL); gtransport = prepare_transport(remote);
if (prune < 0) { if (prune < 0) {
/* no command line request */ /* no command line request */
if (0 <= transport->remote->prune) if (0 <= gtransport->remote->prune)
prune = transport->remote->prune; prune = gtransport->remote->prune;
else if (0 <= fetch_prune_config) else if (0 <= fetch_prune_config)
prune = fetch_prune_config; prune = fetch_prune_config;
else else
prune = PRUNE_BY_DEFAULT; prune = PRUNE_BY_DEFAULT;
} }
transport_set_verbosity(transport, verbosity, progress);
if (upload_pack)
set_option(TRANS_OPT_UPLOADPACK, upload_pack);
if (keep)
set_option(TRANS_OPT_KEEP, "yes");
if (depth)
set_option(TRANS_OPT_DEPTH, depth);
if (argc > 0) { if (argc > 0) {
int j = 0; int j = 0;
refs = xcalloc(argc + 1, sizeof(const char *)); refs = xcalloc(argc + 1, sizeof(const char *));
@ -1010,10 +1033,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
sigchain_push_common(unlock_pack_on_signal); sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack); atexit(unlock_pack);
refspec = parse_fetch_refspec(ref_nr, refs); refspec = parse_fetch_refspec(ref_nr, refs);
exit_code = do_fetch(transport, refspec, ref_nr); exit_code = do_fetch(gtransport, refspec, ref_nr);
free_refspec(ref_nr, refspec); free_refspec(ref_nr, refspec);
transport_disconnect(transport); transport_disconnect(gtransport);
transport = NULL; gtransport = NULL;
return exit_code; return exit_code;
} }

72
t/t5802-connect-helper.sh Executable file
View File

@ -0,0 +1,72 @@
#!/bin/sh
test_description='ext::cmd remote "connect" helper'
. ./test-lib.sh
test_expect_success setup '
test_tick &&
git commit --allow-empty -m initial &&
test_tick &&
git commit --allow-empty -m second &&
test_tick &&
git commit --allow-empty -m third &&
test_tick &&
git tag -a -m "tip three" three &&
test_tick &&
git commit --allow-empty -m fourth
'
test_expect_success clone '
cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") &&
git clone "ext::sh -c %S% ." dst &&
git for-each-ref refs/heads/ refs/tags/ >expect &&
(
cd dst &&
git config remote.origin.url "ext::sh -c $cmd" &&
git for-each-ref refs/heads/ refs/tags/
) >actual &&
test_cmp expect actual
'
test_expect_success 'update following tag' '
test_tick &&
git commit --allow-empty -m fifth &&
test_tick &&
git tag -a -m "tip five" five &&
git for-each-ref refs/heads/ refs/tags/ >expect &&
(
cd dst &&
git pull &&
git for-each-ref refs/heads/ refs/tags/ >../actual
) &&
test_cmp expect actual
'
test_expect_success 'update backfilled tag' '
test_tick &&
git commit --allow-empty -m sixth &&
test_tick &&
git tag -a -m "tip two" two three^1 &&
git for-each-ref refs/heads/ refs/tags/ >expect &&
(
cd dst &&
git pull &&
git for-each-ref refs/heads/ refs/tags/ >../actual
) &&
test_cmp expect actual
'
test_expect_success 'update backfilled tag without primary transfer' '
test_tick &&
git tag -a -m "tip one " one two^1 &&
git for-each-ref refs/heads/ refs/tags/ >expect &&
(
cd dst &&
git pull &&
git for-each-ref refs/heads/ refs/tags/ >../actual
) &&
test_cmp expect actual
'
test_done

View File

@ -881,6 +881,8 @@ void transport_take_over(struct transport *transport,
transport->push_refs = git_transport_push; transport->push_refs = git_transport_push;
transport->disconnect = disconnect_git; transport->disconnect = disconnect_git;
transport->smart_options = &(data->options); transport->smart_options = &(data->options);
transport->cannot_reuse = 1;
} }
static int is_local(const char *url) static int is_local(const char *url)

View File

@ -29,6 +29,12 @@ struct transport {
*/ */
unsigned got_remote_refs : 1; unsigned got_remote_refs : 1;
/*
* Transports that call take-over destroys the data specific to
* the transport type while doing so, and cannot be reused.
*/
unsigned cannot_reuse : 1;
/** /**
* Returns 0 if successful, positive if the option is not * Returns 0 if successful, positive if the option is not
* recognized or is inapplicable, and negative if the option * recognized or is inapplicable, and negative if the option