Skip to content

Commit b26ed43

Browse files
committed
fetch: work around "transport-take-over" hack
A Git-aware "connect" transport allows the "transport_take_over" to redirect generic transport requests like fetch(), push_refs() and get_refs_list() to the native Git transport handling methods. The take-over process replaces transport->data with a fake data that these method implementations understand. While this hack works OK for a single request, it breaks when the transport needs to make more than one requests. transport->data that used to hold necessary information for the specific helper to work correctly is destroyed during the take-over process. One codepath that this matters is "git fetch" in auto-follow mode; when it does not get all the tags that ought to point at the history it got (which can be determined by looking at the peeled tags in the initial advertisement) from the primary transfer, it internally makes a second request to complete the fetch. Because "take-over" hack has already destroyed the data necessary to talk to the transport helper by the time this happens, the second request cannot make a request to the helper to make another connection to fetch these additional tags. Mark such a transport as "cannot_reuse", and use a separate transport to perform the backfill fetch in order to work around this breakage. Note that this problem does not manifest itself when running t5802, because our upload-pack gives you all the necessary auto-followed tags during the primary transfer. You would need to step through "git fetch" in a debugger, stop immediately after the primary transfer finishes and writes these auto-followed tags, remove the tag references and repack/prune the repository to convince the "find-non-local-tags" procedure that the primary transfer failed to give us all the necessary tags, and then let it continue, in order to trigger the bug in the secondary transfer this patch fixes. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 069d503 commit b26ed43

File tree

3 files changed

+21
-0
lines changed

3 files changed

+21
-0
lines changed

builtin/fetch.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static const char *depth;
3737
static const char *upload_pack;
3838
static struct strbuf default_rla = STRBUF_INIT;
3939
static struct transport *gtransport;
40+
static struct transport *gsecondary;
4041
static const char *submodule_prefix = "";
4142
static const char *recurse_submodules_default;
4243

@@ -97,6 +98,8 @@ static void unlock_pack(void)
9798
{
9899
if (gtransport)
99100
transport_unlock_pack(gtransport);
101+
if (gsecondary)
102+
transport_unlock_pack(gsecondary);
100103
}
101104

102105
static void unlock_pack_on_signal(int signo)
@@ -747,9 +750,19 @@ struct transport *prepare_transport(struct remote *remote)
747750

748751
static void backfill_tags(struct transport *transport, struct ref *ref_map)
749752
{
753+
if (transport->cannot_reuse) {
754+
gsecondary = prepare_transport(transport->remote);
755+
transport = gsecondary;
756+
}
757+
750758
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
751759
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
752760
fetch_refs(transport, ref_map);
761+
762+
if (gsecondary) {
763+
transport_disconnect(gsecondary);
764+
gsecondary = NULL;
765+
}
753766
}
754767

755768
static int do_fetch(struct transport *transport,

transport.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,8 @@ void transport_take_over(struct transport *transport,
875875
transport->push_refs = git_transport_push;
876876
transport->disconnect = disconnect_git;
877877
transport->smart_options = &(data->options);
878+
879+
transport->cannot_reuse = 1;
878880
}
879881

880882
static int is_local(const char *url)

transport.h

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

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

0 commit comments

Comments
 (0)