Skip to content

Commit 20419de

Browse files
committed
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
2 parents 3b30ba5 + 0f73f8b commit 20419de

File tree

4 files changed

+136
-33
lines changed

4 files changed

+136
-33
lines changed

builtin/fetch.c

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ static int tags = TAGS_DEFAULT, unshallow;
4040
static const char *depth;
4141
static const char *upload_pack;
4242
static struct strbuf default_rla = STRBUF_INIT;
43-
static struct transport *transport;
43+
static struct transport *gtransport;
44+
static struct transport *gsecondary;
4445
static const char *submodule_prefix = "";
4546
static const char *recurse_submodules_default;
4647

@@ -108,8 +109,10 @@ static struct option builtin_fetch_options[] = {
108109

109110
static void unlock_pack(void)
110111
{
111-
if (transport)
112-
transport_unlock_pack(transport);
112+
if (gtransport)
113+
transport_unlock_pack(gtransport);
114+
if (gsecondary)
115+
transport_unlock_pack(gsecondary);
113116
}
114117

115118
static void unlock_pack_on_signal(int signo)
@@ -733,6 +736,48 @@ static int truncate_fetch_head(void)
733736
return 0;
734737
}
735738

739+
static void set_option(struct transport *transport, const char *name, const char *value)
740+
{
741+
int r = transport_set_option(transport, name, value);
742+
if (r < 0)
743+
die(_("Option \"%s\" value \"%s\" is not valid for %s"),
744+
name, value, transport->url);
745+
if (r > 0)
746+
warning(_("Option \"%s\" is ignored for %s\n"),
747+
name, transport->url);
748+
}
749+
750+
static struct transport *prepare_transport(struct remote *remote)
751+
{
752+
struct transport *transport;
753+
transport = transport_get(remote, NULL);
754+
transport_set_verbosity(transport, verbosity, progress);
755+
if (upload_pack)
756+
set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
757+
if (keep)
758+
set_option(transport, TRANS_OPT_KEEP, "yes");
759+
if (depth)
760+
set_option(transport, TRANS_OPT_DEPTH, depth);
761+
return transport;
762+
}
763+
764+
static void backfill_tags(struct transport *transport, struct ref *ref_map)
765+
{
766+
if (transport->cannot_reuse) {
767+
gsecondary = prepare_transport(transport->remote);
768+
transport = gsecondary;
769+
}
770+
771+
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
772+
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
773+
fetch_refs(transport, ref_map);
774+
775+
if (gsecondary) {
776+
transport_disconnect(gsecondary);
777+
gsecondary = NULL;
778+
}
779+
}
780+
736781
static int do_fetch(struct transport *transport,
737782
struct refspec *refs, int ref_count)
738783
{
@@ -819,11 +864,8 @@ static int do_fetch(struct transport *transport,
819864
struct ref **tail = &ref_map;
820865
ref_map = NULL;
821866
find_non_local_tags(transport, &ref_map, &tail);
822-
if (ref_map) {
823-
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
824-
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
825-
fetch_refs(transport, ref_map);
826-
}
867+
if (ref_map)
868+
backfill_tags(transport, ref_map);
827869
free_refs(ref_map);
828870
}
829871

@@ -832,17 +874,6 @@ static int do_fetch(struct transport *transport,
832874
return retcode;
833875
}
834876

835-
static void set_option(const char *name, const char *value)
836-
{
837-
int r = transport_set_option(transport, name, value);
838-
if (r < 0)
839-
die(_("Option \"%s\" value \"%s\" is not valid for %s"),
840-
name, value, transport->url);
841-
if (r > 0)
842-
warning(_("Option \"%s\" is ignored for %s\n"),
843-
name, transport->url);
844-
}
845-
846877
static int get_one_remote_for_fetch(struct remote *remote, void *priv)
847878
{
848879
struct string_list *list = priv;
@@ -965,26 +996,18 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
965996
die(_("No remote repository specified. Please, specify either a URL or a\n"
966997
"remote name from which new revisions should be fetched."));
967998

968-
transport = transport_get(remote, NULL);
999+
gtransport = prepare_transport(remote);
9691000

9701001
if (prune < 0) {
9711002
/* no command line request */
972-
if (0 <= transport->remote->prune)
973-
prune = transport->remote->prune;
1003+
if (0 <= gtransport->remote->prune)
1004+
prune = gtransport->remote->prune;
9741005
else if (0 <= fetch_prune_config)
9751006
prune = fetch_prune_config;
9761007
else
9771008
prune = PRUNE_BY_DEFAULT;
9781009
}
9791010

980-
transport_set_verbosity(transport, verbosity, progress);
981-
if (upload_pack)
982-
set_option(TRANS_OPT_UPLOADPACK, upload_pack);
983-
if (keep)
984-
set_option(TRANS_OPT_KEEP, "yes");
985-
if (depth)
986-
set_option(TRANS_OPT_DEPTH, depth);
987-
9881011
if (argc > 0) {
9891012
int j = 0;
9901013
refs = xcalloc(argc + 1, sizeof(const char *));
@@ -1010,10 +1033,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
10101033
sigchain_push_common(unlock_pack_on_signal);
10111034
atexit(unlock_pack);
10121035
refspec = parse_fetch_refspec(ref_nr, refs);
1013-
exit_code = do_fetch(transport, refspec, ref_nr);
1036+
exit_code = do_fetch(gtransport, refspec, ref_nr);
10141037
free_refspec(ref_nr, refspec);
1015-
transport_disconnect(transport);
1016-
transport = NULL;
1038+
transport_disconnect(gtransport);
1039+
gtransport = NULL;
10171040
return exit_code;
10181041
}
10191042

t/t5802-connect-helper.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/sh
2+
3+
test_description='ext::cmd remote "connect" helper'
4+
. ./test-lib.sh
5+
6+
test_expect_success setup '
7+
test_tick &&
8+
git commit --allow-empty -m initial &&
9+
test_tick &&
10+
git commit --allow-empty -m second &&
11+
test_tick &&
12+
git commit --allow-empty -m third &&
13+
test_tick &&
14+
git tag -a -m "tip three" three &&
15+
16+
test_tick &&
17+
git commit --allow-empty -m fourth
18+
'
19+
20+
test_expect_success clone '
21+
cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") &&
22+
git clone "ext::sh -c %S% ." dst &&
23+
git for-each-ref refs/heads/ refs/tags/ >expect &&
24+
(
25+
cd dst &&
26+
git config remote.origin.url "ext::sh -c $cmd" &&
27+
git for-each-ref refs/heads/ refs/tags/
28+
) >actual &&
29+
test_cmp expect actual
30+
'
31+
32+
test_expect_success 'update following tag' '
33+
test_tick &&
34+
git commit --allow-empty -m fifth &&
35+
test_tick &&
36+
git tag -a -m "tip five" five &&
37+
git for-each-ref refs/heads/ refs/tags/ >expect &&
38+
(
39+
cd dst &&
40+
git pull &&
41+
git for-each-ref refs/heads/ refs/tags/ >../actual
42+
) &&
43+
test_cmp expect actual
44+
'
45+
46+
test_expect_success 'update backfilled tag' '
47+
test_tick &&
48+
git commit --allow-empty -m sixth &&
49+
test_tick &&
50+
git tag -a -m "tip two" two three^1 &&
51+
git for-each-ref refs/heads/ refs/tags/ >expect &&
52+
(
53+
cd dst &&
54+
git pull &&
55+
git for-each-ref refs/heads/ refs/tags/ >../actual
56+
) &&
57+
test_cmp expect actual
58+
'
59+
60+
test_expect_success 'update backfilled tag without primary transfer' '
61+
test_tick &&
62+
git tag -a -m "tip one " one two^1 &&
63+
git for-each-ref refs/heads/ refs/tags/ >expect &&
64+
(
65+
cd dst &&
66+
git pull &&
67+
git for-each-ref refs/heads/ refs/tags/ >../actual
68+
) &&
69+
test_cmp expect actual
70+
'
71+
72+
test_done

transport.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,8 @@ void transport_take_over(struct transport *transport,
881881
transport->push_refs = git_transport_push;
882882
transport->disconnect = disconnect_git;
883883
transport->smart_options = &(data->options);
884+
885+
transport->cannot_reuse = 1;
884886
}
885887

886888
static int is_local(const char *url)

transport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ struct transport {
2929
*/
3030
unsigned got_remote_refs : 1;
3131

32+
/*
33+
* Transports that call take-over destroys the data specific to
34+
* the transport type while doing so, and cannot be reused.
35+
*/
36+
unsigned cannot_reuse : 1;
37+
3238
/**
3339
* Returns 0 if successful, positive if the option is not
3440
* recognized or is inapplicable, and negative if the option

0 commit comments

Comments
 (0)