* Client-side mirroring patches (v0)
@ 2009-11-25 10:06 Sam Vilain
2009-11-25 10:06 ` [PATCH 1/4] remote: allow mirroring to be specified, and document settings Sam Vilain
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Sam Vilain @ 2009-11-25 10:06 UTC (permalink / raw)
To: git
Hey folks, this is the first stage of git mirroring - making the
client support it in the face of a completely ignorant server. I
intended to make the clean-up better, but I've been sitting on these
for a couple of weeks so I thought it would be better to have them out
there for people to have a squiz at.
The next stage would be for the mirror list to be communicated to
clients over the network protocol and updated in the git config.
Also there is the matter of falling over to the next mirror should one
not be reachable, but then we're getting into C weaknesses really.
Should I plan to do exception recovery using 'longjmp' ? Also the
process should be interruptible and provide a user menu. Again this
seems like it would be very tedious and clumsy in C. How do people
manage? Anyway, enjoy... Sam
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/4] remote: allow mirroring to be specified, and document settings 2009-11-25 10:06 Client-side mirroring patches (v0) Sam Vilain @ 2009-11-25 10:06 ` Sam Vilain 2009-11-25 10:06 ` [PATCH 2/4] fetch: try mirrors if selected Sam Vilain 2009-11-26 0:58 ` Client-side mirroring patches (v0) Shawn O. Pearce 2010-01-01 0:05 ` Nanako Shiraishi 2 siblings, 1 reply; 8+ messages in thread From: Sam Vilain @ 2009-11-25 10:06 UTC (permalink / raw) To: git; +Cc: Sam Vilain Add a per-remote setting that can list alternate URLs that the same repository (or a close fork) can be found, a fetch command-line switch, a config flag to enable it by default, and finally a method for selecting a default mirror. If the preferred mirror does not exist in the list of mirrors it is considered invalid, in preparation for the time when the mirror list can be supplied or added to by the upload-pack protocol response. Signed-off-by: Sam Vilain <sam@vilain.net> --- Documentation/config.txt | 13 +++++++++++++ Documentation/fetch-options.txt | 24 ++++++++++++++++++++++++ builtin-fetch.c | 4 +++- remote.c | 17 +++++++++++++++++ remote.h | 6 ++++++ 5 files changed, 63 insertions(+), 1 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index d1e2120..edde0e4 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1375,6 +1375,19 @@ remote.<name>.url:: remote.<name>.pushurl:: The push URL of a remote repository. See linkgit:git-push[1]. +remote.<name>.mirror-url:: + An alternate URL which should have many similar refs to the + real remote at least most of the time. This option can be + specified multiple times. See linkgit:git-fetch[1]. + +remote.<name>.use-mirror:: + Prefer to contact mirrors first (boolean). See + linkgit:git-fetch[1]. + +remote.<name>.preferred-mirror:: + Specify which mirror to try first (full URL). May be updated + by user interaction during 'fetch'. See linkgit:git-fetch[1]. + remote.<name>.proxy:: For remotes that require curl (http, https and ftp), the URL to the proxy to use for that remote. Set to the empty string to diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 2886874..8f07a56 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -62,6 +62,30 @@ ifndef::git-pull[] Pass --quiet to git-fetch-pack and silence any other internally used git commands. +-M:: +--use-mirror:: + Try to use a configured mirror for the bulk of the transfer + rather than the main upstream. See gitlink:git-config[1] for + the appropriate options to set. ++ +The preferred mirror (or the first if no preferred URL is defined) is +first contacted and fetched from, and any refs it presents which match +the source <refspec> (including the rules for fetching tags) are saved +under `refs/mirrors/`<remote>`/hostname`. If there is a network +timeout, or the user interrupts the fetch process, the next mirror +will be tried. ++ +Once the fetch from the mirror is complete, the central host is +contacted and fetched from. If the mirror was correct and up to date, +then no more data will be required from the central host. At this +point, all of the refs under `refs/mirrors/`<remote> which are +reachable from the real `refs/remotes/`<remote> tracking branches will +be removed. Extra refs which were not present on the real source will +be left behind so they are not fetched again the next time around. ++ +This can be made the default for a remote using the +`remote.`<remote>.`use-mirror` configuration option. + -v:: --verbose:: Be verbose. diff --git a/builtin-fetch.c b/builtin-fetch.c index a35a6f8..209f502 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -23,7 +23,7 @@ enum { TAGS_SET = 2 }; -static int append, force, keep, update_head_ok, verbosity; +static int append, force, keep, update_head_ok, verbosity, use_mirror; static int tags = TAGS_DEFAULT; static const char *depth; static const char *upload_pack; @@ -45,6 +45,8 @@ static struct option builtin_fetch_options[] = { OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"), OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, "allow updating of HEAD ref"), + OPT_BOOLEAN('M', "mirror", &use_mirror, + "use mirror if available"), OPT_STRING(0, "depth", &depth, "DEPTH", "deepen history of shallow clone"), OPT_END() diff --git a/remote.c b/remote.c index 73d33f2..65df03d 100644 --- a/remote.c +++ b/remote.c @@ -111,6 +111,13 @@ static void add_pushurl(struct remote *remote, const char *pushurl) remote->pushurl[remote->pushurl_nr++] = pushurl; } +static void add_mirror_url(struct remote *remote, const char *mirror_url) +{ + ALLOC_GROW(remote->mirror_url, remote->mirror_url_nr + 1, + remote->mirror_url_alloc); + remote->mirror_url[remote->mirror_url_nr++] = mirror_url; +} + static void add_pushurl_alias(struct remote *remote, const char *url) { const char *pushurl = alias_url(url, &rewrites_push); @@ -407,6 +414,16 @@ static int handle_config(const char *key, const char *value, void *cb) if (git_config_string(&v, key, value)) return -1; add_pushurl(remote, v); + } else if (!strcmp(subkey, ".mirror-url")) { + const char *v; + if (git_config_string(&v, key, value)) + return -1; + add_mirror_url(remote, v); + } else if (!strcmp(subkey, ".use-mirror")) { + remote->use_mirror = git_config_bool(key, value); + } else if (!strcmp(subkey, ".preferred-mirror")) { + if (git_config_string(&remote->preferred_mirror, key, value)) + return -1; } else if (!strcmp(subkey, ".push")) { const char *v; if (git_config_string(&v, key, value)) diff --git a/remote.h b/remote.h index 5db8420..c720b9a 100644 --- a/remote.h +++ b/remote.h @@ -19,6 +19,12 @@ struct remote { int pushurl_nr; int pushurl_alloc; + const char **mirror_url; + int mirror_url_nr; + int mirror_url_alloc; + int use_mirror; + const char *preferred_mirror; + const char **push_refspec; struct refspec *push; int push_refspec_nr; -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/4] fetch: try mirrors if selected 2009-11-25 10:06 ` [PATCH 1/4] remote: allow mirroring to be specified, and document settings Sam Vilain @ 2009-11-25 10:06 ` Sam Vilain 2009-11-25 10:06 ` [PATCH 3/4] fetch --use-mirror: don't fetch with 'autotags' for actual fetch Sam Vilain 2009-11-26 1:20 ` [PATCH 2/4] fetch: try mirrors if selected Shawn O. Pearce 0 siblings, 2 replies; 8+ messages in thread From: Sam Vilain @ 2009-11-25 10:06 UTC (permalink / raw) To: git; +Cc: Sam Vilain If configured and selected, the mirrors are tried in turn until one succeeds, re-writing the refs to a refs/mirrors/<remote>/<hostname>/ space. No refs from the mirrors are ever written to the real refs/heads or refs/tags spaces, but their being available locally will speed up fetching from the real remote if they are more up to date than the local version. Signed-off-by: Sam Vilain <sam@vilain.net> --- builtin-fetch.c | 161 ++++++++++++++++++++++++++++++++++++++++++++-- remote.c | 14 ++++- remote.h | 1 + t/t5560-mirror-fetch.sh | 46 +++++++++++++ transport.c | 41 ++++++++++++ transport.h | 5 ++ 6 files changed, 260 insertions(+), 8 deletions(-) create mode 100644 t/t5560-mirror-fetch.sh diff --git a/builtin-fetch.c b/builtin-fetch.c index 209f502..b3b8766 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -45,8 +45,8 @@ static struct option builtin_fetch_options[] = { OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"), OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, "allow updating of HEAD ref"), - OPT_BOOLEAN('M', "mirror", &use_mirror, - "use mirror if available"), + OPT_SET_INT('M', "use-mirror", &use_mirror, + "use mirror if available", 1), OPT_STRING(0, "depth", &depth, "DEPTH", "deepen history of shallow clone"), OPT_END() @@ -109,6 +109,109 @@ static void find_non_local_tags(struct transport *transport, struct ref **head, struct ref ***tail); +char* get_url_hostname(const char *url) +{ + char *scratch = xstrdup(url); + char *host = strstr(url, "://"); + char c; + char *end, *rh; + if (host) { + host += 3; + c = '/'; + } + else { + host = scratch; + c = ':'; + } + + if (host[0] == '[') { + end = strchr(host + 1, ']'); + if (end) { + *end = 0; + host++; + } + } + else { + end = strchr(host, c); + if (end && !has_dos_drive_prefix(url) ) { + *end = 0; + } + else { + host = "localhost"; + } + } + rh = xstrdup(host); + free(scratch); + return rh; +} + +const char *mirror_ref(const char* remote_name, const char* mirror_hostname, + const char* refname) +{ + int has_refs, new_sz; + char *rv, *dst; + + // *rs[i] = *refspec[i]; ? + has_refs = ( strstr(refname, "refs/") == refname ); + /* "refs/"(0 or 5) "mirrors/"(8) remote "/"(1) hostname "/"(1) */ + new_sz = (has_refs ? 0 : 5) + 8 + + strlen(remote_name) + 1 + + strlen(mirror_hostname) + 1 + + strlen(refname) + 1; + rv = xmalloc( new_sz ); + strcpy(rv, "refs/mirrors/"); + dst = rv + 13; + strcpy(dst, remote_name); + dst += strlen(remote_name); + *dst++ = '/'; + strcpy(dst, mirror_hostname); + dst += strlen(mirror_hostname); + *dst++ = '/'; + strcpy(dst, refname+(has_refs?5:0)); + return rv; +} + +struct ref *mirror_refmap(struct transport* transport, + struct ref* ref_map) +{ + struct ref *rm, *mirror_refmap, *last, *rv, *peer_ref; + + const char* remote_name = transport->remote->name; + const char* mirror_hostname = get_url_hostname(transport->url); + int c = 0; + + last = NULL; + rv = NULL; + for (rm = ref_map; rm; rm = rm->next) { + const char *new_dst; + + // skip refs we already have locally, to avoid ref churn + if (has_sha1_file(rm->old_sha1)) + continue; + + mirror_refmap = alloc_ref(rm->name); + mirror_refmap->remote_status = rm->remote_status; + hashcpy(mirror_refmap->old_sha1, rm->old_sha1); + hashcpy(mirror_refmap->new_sha1, rm->new_sha1); + + if (last) + last->next = mirror_refmap; + else + rv = mirror_refmap; + c++; + + new_dst = mirror_ref(remote_name, mirror_hostname, rm->name); + + peer_ref = alloc_ref(new_dst); + mirror_refmap->peer_ref = peer_ref; + peer_ref->force = 1; + last = mirror_refmap; + } + + return rv; +} + + static struct ref *get_ref_map(struct transport *transport, struct refspec *refs, int ref_count, int tags, int *autotags) @@ -165,10 +268,14 @@ static struct ref *get_ref_map(struct transport *transport, if (tags == TAGS_DEFAULT && *autotags) find_non_local_tags(transport, &ref_map, &tail); ref_remove_duplicates(ref_map); + if (strcmp(transport->url, transport->remote->url[0]) != 0) { + return mirror_refmap(transport, ref_map); + } return ref_map; } + #define STORE_REF_ERROR_OTHER 1 #define STORE_REF_ERROR_DF_CONFLICT 2 @@ -638,6 +745,7 @@ static int do_fetch(struct transport *transport, } ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); + if (!update_head_ok) check_not_current_branch(ref_map); @@ -688,13 +796,18 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) int i; static const char **refs = NULL; int ref_nr = 0; - int exit_code; + int exit_code = 0; + int urls_remaining = 1; + struct transport *real_transport = NULL; + const char *mirror = NULL; + struct refspec *refspec; /* Record the command line for the reflog */ strbuf_addstr(&default_rla, "fetch"); for (i = 1; i < argc; i++) strbuf_addf(&default_rla, " %s", argv[i]); + use_mirror = -1; argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); @@ -706,6 +819,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (!remote) die("Where do you want to fetch from today?"); + if (use_mirror == -1) { + use_mirror = remote->use_mirror ? 1 : 0; + } + transport = transport_get(remote, remote->url[0]); if (verbosity >= 2) transport->verbose = 1; @@ -742,9 +859,39 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); - exit_code = do_fetch(transport, - parse_fetch_refspec(ref_nr, refs), ref_nr); - transport_disconnect(transport); - transport = NULL; + refspec = parse_fetch_refspec(ref_nr, refs); + if (use_mirror) { + real_transport = transport; + urls_remaining = remote->mirror_url_nr + 1; + } + while (urls_remaining) { + if (use_mirror && (urls_remaining > 1) ) { + transport = transport_next_mirror(real_transport, mirror); + mirror = transport->url; + warning("trying mirror: %s", mirror); + // real_transport may not have these options - re-set them. + 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); + + } + exit_code = do_fetch(transport, refspec, ref_nr); + transport_disconnect(transport); + transport = NULL; + urls_remaining--; + if (use_mirror) { + if (!exit_code && urls_remaining >= 1) { + warning("successful fetch from mirror"); + urls_remaining = 1; + } + if (urls_remaining == 1) { + transport = real_transport; + warning("trying master: %s", transport->url); + } + } + } return exit_code; } diff --git a/remote.c b/remote.c index 65df03d..5f08e10 100644 --- a/remote.c +++ b/remote.c @@ -139,8 +139,9 @@ static struct remote *make_remote(const char *name, int len) for (i = 0; i < remotes_nr; i++) { if (len ? (!strncmp(name, remotes[i]->name, len) && !remotes[i]->name[len]) : - !strcmp(name, remotes[i]->name)) + !strcmp(name, remotes[i]->name)) { return remotes[i]; + } } ret = xcalloc(1, sizeof(struct remote)); @@ -683,6 +684,7 @@ static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec) return parse_refspec_internal(nr_refspec, refspec, 0, 0); } + static int valid_remote_nick(const char *name) { if (!name[0] || is_dot_or_dotdot(name)) @@ -786,6 +788,16 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } +int remote_mirror_idx(struct remote *remote, const char *mirror_url) +{ + int i; + for (i = 0; i < remote->mirror_url_nr; i++) { + if (!strcmp(remote->mirror_url[i], mirror_url)) + return i; + } + return -1; +} + static int match_name_with_pattern(const char *key, const char *name, const char *value, char **result) { diff --git a/remote.h b/remote.h index c720b9a..da208ff 100644 --- a/remote.h +++ b/remote.h @@ -61,6 +61,7 @@ typedef int each_remote_fn(struct remote *remote, void *priv); int for_each_remote(each_remote_fn fn, void *priv); int remote_has_url(struct remote *remote, const char *url); +int remote_mirror_idx(struct remote *remote, const char *mirror_url); struct refspec { unsigned force : 1; diff --git a/t/t5560-mirror-fetch.sh b/t/t5560-mirror-fetch.sh new file mode 100644 index 0000000..940dc0e --- /dev/null +++ b/t/t5560-mirror-fetch.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# +# Copyright (c) 2009 Sam Vilain +# + +test_description='mirror fetch test' + +. ./test-lib.sh + +test_expect_success setup ' + echo >file master initial && + git add file && + git commit -a -m "Master initial" && + git clone . master && + git clone master mirror && + cd master && + echo >file master update && + git commit -a -m "Master update" && + cd .. && + mkdir clone && + cd clone && + git init && + git remote add origin ../master && + git config remote.origin.mirror-url ../mirror +' + +# in later iterations we'll expect these mirror tracking refs to be +# cleaned up once they are confirmed reachable from the master, but +# for now they leave a sufficient breadcrumb of the operation + +test_expect_success 'fetch using mirror - explicit' ' + git fetch --use-mirror origin refs/heads/*:refs/remotes/origin/* && + git rev-parse refs/mirrors/origin/localhost/heads/master +' + +test_expect_success 'fetch using mirror - default' ' + cd .. && + mkdir clone2 && + cd clone2 && + git init && + git remote add origin ../master && + git config remote.origin.mirror-url ../mirror + git fetch --use-mirror && + git rev-parse refs/mirrors/origin/localhost/heads/master +' +test_done diff --git a/transport.c b/transport.c index 644a30a..0dc0185 100644 --- a/transport.c +++ b/transport.c @@ -859,6 +859,47 @@ struct transport *transport_get(struct remote *remote, const char *url) return ret; } +struct transport *transport_next_mirror(struct transport *transport, + const char *last_mirror) +{ + struct transport *ret; + struct remote* remote = transport->remote; + int mirror_idx = -1; + const char* url; + + if (!last_mirror) { + if (remote->preferred_mirror) { + mirror_idx = remote_mirror_idx( + remote, + remote->preferred_mirror + ); + if (mirror_idx == -1) { + warning("preferred mirror '%s' not listed " + "in remote.%s.mirror-url", + remote->preferred_mirror, + remote->name); + } + } + else { + mirror_idx = 0; + } + } + else { + mirror_idx = remote_mirror_idx(remote, last_mirror) + 1; + // caller must check that we are not looping indefinitely + mirror_idx %= remote->mirror_url_nr; + } + + url = remote->mirror_url[mirror_idx]; + ret = transport_get(remote, url); + + // copy settings - caller must re-set options + ret->verbose = transport->verbose; + ret->progress = transport->progress; + + return ret; +} + int transport_set_option(struct transport *transport, const char *name, const char *value) { diff --git a/transport.h b/transport.h index c14da6f..9890157 100644 --- a/transport.h +++ b/transport.h @@ -41,6 +41,9 @@ struct transport { /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); +/* Returns a transport for a mirror */ +struct transport *transport_next_mirror(struct transport *transport, const char *last_mirror); + /* Transport options which apply to git:// and scp-style URLs */ /* The program to use on the remote side to send a pack */ @@ -78,6 +81,8 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs); void transport_unlock_pack(struct transport *transport); int transport_disconnect(struct transport *transport); char *transport_anonymize_url(const char *url); +struct refspec *mirror_refspec(struct transport* transport, + struct refspec *refspec, int refspec_nr); /* Transport methods defined outside transport.c */ int transport_helper_init(struct transport *transport, const char *name); -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/4] fetch --use-mirror: don't fetch with 'autotags' for actual fetch 2009-11-25 10:06 ` [PATCH 2/4] fetch: try mirrors if selected Sam Vilain @ 2009-11-25 10:06 ` Sam Vilain 2009-11-25 10:06 ` [PATCH 4/4] fetch: cleanup refs with --use-mirror Sam Vilain 2009-11-26 1:20 ` [PATCH 2/4] fetch: try mirrors if selected Shawn O. Pearce 1 sibling, 1 reply; 8+ messages in thread From: Sam Vilain @ 2009-11-25 10:06 UTC (permalink / raw) To: git; +Cc: Sam Vilain Unsetting 'autotags' at a late stage during the fetch process has the useful behaviour of figuring out which refs to fetch where according to the regular autotags rules, building a refspec (struct ref* linked list), and then we turn them off for mirror fetch and no real tags are actually changed, just the re-written ones under refs/mirrors/. The final fetch will re-set autotags again, and uncannily the exact behaviour we are after springs up: we get all the tags for the refs that are now changing, even though we got the data from a mirror. All from one line of code. Win! Signed-off-by: Sam Vilain <sam@vilain.net> --- builtin-fetch.c | 1 + t/t5560-mirror-fetch.sh | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index b3b8766..daa287a 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -269,6 +269,7 @@ static struct ref *get_ref_map(struct transport *transport, find_non_local_tags(transport, &ref_map, &tail); ref_remove_duplicates(ref_map); if (strcmp(transport->url, transport->remote->url[0]) != 0) { + *autotags = 0; return mirror_refmap(transport, ref_map); } diff --git a/t/t5560-mirror-fetch.sh b/t/t5560-mirror-fetch.sh index 940dc0e..58d5f3c 100644 --- a/t/t5560-mirror-fetch.sh +++ b/t/t5560-mirror-fetch.sh @@ -11,11 +11,13 @@ test_expect_success setup ' echo >file master initial && git add file && git commit -a -m "Master initial" && + git tag -m "SEEN" initial && git clone . master && git clone master mirror && cd master && echo >file master update && git commit -a -m "Master update" && + git tag -m "SEEN" update && cd .. && mkdir clone && cd clone && @@ -35,12 +37,20 @@ test_expect_success 'fetch using mirror - explicit' ' test_expect_success 'fetch using mirror - default' ' cd .. && + cd mirror && + git tag -m "badtag" badtag && + cd .. && mkdir clone2 && cd clone2 && git init && git remote add origin ../master && git config remote.origin.mirror-url ../mirror git fetch --use-mirror && - git rev-parse refs/mirrors/origin/localhost/heads/master + git rev-parse refs/mirrors/origin/localhost/heads/master && + git rev-parse refs/mirrors/origin/localhost/tags/initial && + ! git rev-parse refs/tags/badtag && + git rev-parse refs/tags/initial && + git rev-parse refs/tags/update ' + test_done -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/4] fetch: cleanup refs with --use-mirror 2009-11-25 10:06 ` [PATCH 3/4] fetch --use-mirror: don't fetch with 'autotags' for actual fetch Sam Vilain @ 2009-11-25 10:06 ` Sam Vilain 0 siblings, 0 replies; 8+ messages in thread From: Sam Vilain @ 2009-11-25 10:06 UTC (permalink / raw) To: git; +Cc: Sam Vilain Remove identical refs after a successful fetch. The ref under 'refs/mirrors/HOST/XXX' is compared with 'refs/XXX', and if matched, then the 'refs/mirrors/' version is removed. Signed-off-by: Sam Vilain <sam@vilain.net> --- This is a simple mechanism for removing stale mirror refs; a more sophisticated approach would use the revision walker. builtin-fetch.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 59 insertions(+), 3 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index daa287a..0c52f23 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -211,6 +211,57 @@ struct ref *mirror_refmap(struct transport* transport, return rv; } +int clean_up_mirror_ref(const char *refname, + const unsigned char *sha1, + int flags, + void *leading) +{ + char *orig_refname; + char *target_refname; + char *x; + unsigned char found_sha1[20]; + + orig_refname = xmalloc(strlen(refname)+strlen(leading)+1); + x = strchr(refname, '/'); + if (!x) + return 0; + target_refname = xmalloc(strlen(x)+strlen("refs/remotes/")+1); + + strcpy(orig_refname, leading); + x = orig_refname + strlen(leading); + strcpy(x, refname); + + warning("cleaning up mirror ref: %s (%s)", + orig_refname, sha1_to_hex(sha1)); + + strcpy(target_refname, "refs/remotes/"); + strcpy(target_refname+5, strchr(refname, '/')+1); + + warning("target ref is %s", target_refname); + + if (resolve_ref(target_refname, found_sha1, 1, NULL)) { + if (!hashcmp(found_sha1, sha1)) { + warning("deleting ref %s", orig_refname); + delete_ref(orig_refname, sha1, REF_NODEREF); + } + } +} + +void clean_up_mirror_refs(struct remote* remote) +{ + int rem_l = strlen(remote->name); + char *dst_name = xmalloc(rem_l+14); + char *x; + strcpy(dst_name, "refs/mirrors/"); + x = dst_name + 13; + strcpy(x, remote->name); + x += rem_l; + *x++ = '/'; + + warning("cleaning up mirror refs for remote %s", remote->name); + for_each_ref_in(dst_name, clean_up_mirror_ref, + (void *)dst_name); +} static struct ref *get_ref_map(struct transport *transport, struct refspec *refs, int ref_count, int tags, @@ -884,9 +935,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) transport = NULL; urls_remaining--; if (use_mirror) { - if (!exit_code && urls_remaining >= 1) { - warning("successful fetch from mirror"); - urls_remaining = 1; + if (!exit_code) { + if (urls_remaining >= 1) { + warning("successful fetch from mirror"); + urls_remaining = 1; + } + else { + clean_up_mirror_refs(remote); + } } if (urls_remaining == 1) { transport = real_transport; -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/4] fetch: try mirrors if selected 2009-11-25 10:06 ` [PATCH 2/4] fetch: try mirrors if selected Sam Vilain 2009-11-25 10:06 ` [PATCH 3/4] fetch --use-mirror: don't fetch with 'autotags' for actual fetch Sam Vilain @ 2009-11-26 1:20 ` Shawn O. Pearce 1 sibling, 0 replies; 8+ messages in thread From: Shawn O. Pearce @ 2009-11-26 1:20 UTC (permalink / raw) To: Sam Vilain; +Cc: git Sam Vilain <sam@vilain.net> wrote: > diff --git a/builtin-fetch.c b/builtin-fetch.c > index 209f502..b3b8766 100644 > @@ -109,6 +109,109 @@ static void find_non_local_tags(struct transport *transport, > struct ref **head, > struct ref ***tail); > > +char* get_url_hostname(const char *url) Minor nit, but we mark any function not used outside of the module as static. Especially in a builtin-*.c since they all link into the same namespace. If this is meant to be reused, it belongs in connect.c most likely, that's where we already have code like this to get the SSH hostname out of a URL for SSH connections. I don't have time right now to read the rest of this series, but the general approach of fetching to a temporary mirror space before checking if you really are current is a good one. I'm not sure that storing the list of mirrors inside of the remote makes much sense, I would think the user would want to store only a handful of "fast" URLs. And even then I wonder why this can't just be the url[1]..url[n-1] entries in the configuration file. push pushes to all of the URLs at once, "seeding the mirrors". Why can't fetch use the same configuration? -- Shawn. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Client-side mirroring patches (v0) 2009-11-25 10:06 Client-side mirroring patches (v0) Sam Vilain 2009-11-25 10:06 ` [PATCH 1/4] remote: allow mirroring to be specified, and document settings Sam Vilain @ 2009-11-26 0:58 ` Shawn O. Pearce 2010-01-01 0:05 ` Nanako Shiraishi 2 siblings, 0 replies; 8+ messages in thread From: Shawn O. Pearce @ 2009-11-26 0:58 UTC (permalink / raw) To: Sam Vilain; +Cc: git Sam Vilain <sam@vilain.net> wrote: > Hey folks, this is the first stage of git mirroring ... > Also there is the matter of falling over to the next mirror should one > not be reachable, but then we're getting into C weaknesses really. > Should I plan to do exception recovery using 'longjmp' ? Please don't use longjmp. You'll have to change the code to not die() upon connection failure, but instead return an error code to the higher level which can locate another mirror and retry. > Also the > process should be interruptible and provide a user menu. Again this > seems like it would be very tedious and clumsy in C. How do people > manage? With great pain. :-) To do a user menu you can do a simple interface like `git add -i` does, which just dumps the choices to stdout and a prompt for the user to enter their selection. If you want something more complex you need to link to curses or ncurses, which IIRC opens some issues with portablity, but lets you do a bit nicer interface on the tty. -- Shawn. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Client-side mirroring patches (v0) 2009-11-25 10:06 Client-side mirroring patches (v0) Sam Vilain 2009-11-25 10:06 ` [PATCH 1/4] remote: allow mirroring to be specified, and document settings Sam Vilain 2009-11-26 0:58 ` Client-side mirroring patches (v0) Shawn O. Pearce @ 2010-01-01 0:05 ` Nanako Shiraishi 2 siblings, 0 replies; 8+ messages in thread From: Nanako Shiraishi @ 2010-01-01 0:05 UTC (permalink / raw) To: Junio C Hamano; +Cc: Sam Vilain, git Junio, could you tell us what happened to this thread? ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2010-01-01 0:06 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-11-25 10:06 Client-side mirroring patches (v0) Sam Vilain 2009-11-25 10:06 ` [PATCH 1/4] remote: allow mirroring to be specified, and document settings Sam Vilain 2009-11-25 10:06 ` [PATCH 2/4] fetch: try mirrors if selected Sam Vilain 2009-11-25 10:06 ` [PATCH 3/4] fetch --use-mirror: don't fetch with 'autotags' for actual fetch Sam Vilain 2009-11-25 10:06 ` [PATCH 4/4] fetch: cleanup refs with --use-mirror Sam Vilain 2009-11-26 1:20 ` [PATCH 2/4] fetch: try mirrors if selected Shawn O. Pearce 2009-11-26 0:58 ` Client-side mirroring patches (v0) Shawn O. Pearce 2010-01-01 0:05 ` Nanako Shiraishi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox