From 737c5a9cde708d6995c765b7c2e95033edd0a896 Mon Sep 17 00:00:00 2001 From: Michael Schubert Date: Sat, 13 Jul 2013 11:36:24 +0200 Subject: fetch: make --prune configurable Without "git fetch --prune", remote-tracking branches for a branch the other side already has removed will stay forever. Some people want to always run "git fetch --prune". To accommodate users who want to either prune always or when fetching from a particular remote, add two new configuration variables "fetch.prune" and "remote..prune": - "fetch.prune" allows to enable prune for all fetch operations. - "remote..prune" allows to change the behaviour per remote. The latter will naturally override the former, and the --[no-]prune option from the command line will override the configured default. Since --prune is a potentially destructive operation (Git doesn't keep reflogs for deleted references yet), we don't want to prune without users consent, so this configuration will not be on by default. Helped-by: Junio C Hamano Signed-off-by: Michael Schubert Signed-off-by: Junio C Hamano --- builtin/fetch.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 4b6b1dfe66..08ab948022 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -30,7 +30,11 @@ enum { TAGS_SET = 2 }; -static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity; +static int fetch_prune_config = -1; /* unspecified */ +static int prune = -1; /* unspecified */ +#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ + +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int tags = TAGS_DEFAULT, unshallow; static const char *depth; @@ -54,6 +58,15 @@ static int option_parse_recurse_submodules(const struct option *opt, return 0; } +static int git_fetch_config(const char *k, const char *v, void *cb) +{ + if (!strcmp(k, "fetch.prune")) { + fetch_prune_config = git_config_bool(k, v); + return 0; + } + return 0; +} + static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), OPT_BOOLEAN(0, "all", &all, @@ -69,8 +82,8 @@ static struct option builtin_fetch_options[] = { N_("fetch all tags and associated objects"), TAGS_SET), OPT_SET_INT('n', NULL, &tags, N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), - OPT_BOOLEAN('p', "prune", &prune, - N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('p', "prune", &prune, + N_("prune remote-tracking branches no longer on remote")), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, @@ -739,7 +752,10 @@ static int do_fetch(struct transport *transport, return 1; } if (prune) { - /* If --tags was specified, pretend the user gave us the canonical tags refspec */ + /* + * If --tags was specified, pretend that the user gave us + * the canonical tags refspec + */ if (tags == TAGS_SET) { const char *tags_str = "refs/tags/*:refs/tags/*"; struct refspec *tags_refspec, *refspec; @@ -848,7 +864,7 @@ static void add_options_to_argv(struct argv_array *argv) { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune) + if (prune > 0) argv_array_push(argv, "--prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); @@ -916,6 +932,17 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) "remote name from which new revisions should be fetched.")); transport = transport_get(remote, NULL); + + if (prune < 0) { + /* no command line request */ + if (0 <= transport->remote->prune) + prune = transport->remote->prune; + else if (0 <= fetch_prune_config) + prune = fetch_prune_config; + else + prune = PRUNE_BY_DEFAULT; + } + transport_set_verbosity(transport, verbosity, progress); if (upload_pack) set_option(TRANS_OPT_UPLOADPACK, upload_pack); @@ -973,6 +1000,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; i++) strbuf_addf(&default_rla, " %s", argv[i]); + git_config(git_fetch_config, NULL); + argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); -- cgit v1.2.1 From d5d09d475440c24016ec52a0bcc8477d9a7b5c71 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Sat, 3 Aug 2013 13:51:19 +0200 Subject: Replace deprecated OPT_BOOLEAN by OPT_BOOL This task emerged from b04ba2bb (parse-options: deprecate OPT_BOOLEAN, 2011-09-27). All occurrences of the respective variables have been reviewed and none of them relied on the counting up mechanism, but all of them were using the variable as a true boolean. This patch does not change semantics of any command intentionally. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/fetch.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index d784b2e694..99afed03d4 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -56,28 +56,28 @@ static int option_parse_recurse_submodules(const struct option *opt, static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), - OPT_BOOLEAN(0, "all", &all, - N_("fetch from all remotes")), - OPT_BOOLEAN('a', "append", &append, - N_("append to .git/FETCH_HEAD instead of overwriting")), + OPT_BOOL(0, "all", &all, + N_("fetch from all remotes")), + OPT_BOOL('a', "append", &append, + N_("append to .git/FETCH_HEAD instead of overwriting")), OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), N_("path to upload pack on remote end")), OPT__FORCE(&force, N_("force overwrite of local branch")), - OPT_BOOLEAN('m', "multiple", &multiple, - N_("fetch from multiple remotes")), + OPT_BOOL('m', "multiple", &multiple, + N_("fetch from multiple remotes")), OPT_SET_INT('t', "tags", &tags, N_("fetch all tags and associated objects"), TAGS_SET), OPT_SET_INT('n', NULL, &tags, N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), - OPT_BOOLEAN('p', "prune", &prune, - N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('p', "prune", &prune, + N_("prune remote-tracking branches no longer on remote")), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, - OPT_BOOLEAN(0, "dry-run", &dry_run, - N_("dry run")), - OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")), - OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, + OPT_BOOL(0, "dry-run", &dry_run, + N_("dry run")), + OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), + OPT_BOOL('u', "update-head-ok", &update_head_ok, N_("allow updating of HEAD ref")), OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), OPT_STRING(0, "depth", &depth, N_("depth"), -- cgit v1.2.1 From af23445925464b74b5f038271b6cfeaeb217481f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 7 Aug 2013 15:38:45 -0700 Subject: fetch: rename file-scope global "transport" to "gtransport" Although many functions in this file take a "struct transport" as a parameter, "fetch_one()" assigns to the global singleton instance which is a file-scope static, in order to allow a parameterless signal handler unlock_pack() to access it. Rename the variable to gtransport to make sure these uses stand out. Signed-off-by: Junio C Hamano --- builtin/fetch.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index d784b2e694..636b47ffb7 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -36,7 +36,7 @@ static int tags = TAGS_DEFAULT, unshallow; static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; -static struct transport *transport; +static struct transport *gtransport; static const char *submodule_prefix = ""; static const char *recurse_submodules_default; @@ -95,8 +95,8 @@ static struct option builtin_fetch_options[] = { static void unlock_pack(void) { - if (transport) - transport_unlock_pack(transport); + if (gtransport) + transport_unlock_pack(gtransport); } static void unlock_pack_on_signal(int signo) @@ -818,13 +818,13 @@ static int do_fetch(struct transport *transport, static void set_option(const char *name, const char *value) { - int r = transport_set_option(transport, name, value); + int r = transport_set_option(gtransport, name, value); if (r < 0) die(_("Option \"%s\" value \"%s\" is not valid for %s"), - name, value, transport->url); + name, value, gtransport->url); if (r > 0) warning(_("Option \"%s\" is ignored for %s\n"), - name, transport->url); + name, gtransport->url); } static int get_one_remote_for_fetch(struct remote *remote, void *priv) @@ -949,8 +949,8 @@ 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" "remote name from which new revisions should be fetched.")); - transport = transport_get(remote, NULL); - transport_set_verbosity(transport, verbosity, progress); + gtransport = transport_get(remote, NULL); + transport_set_verbosity(gtransport, verbosity, progress); if (upload_pack) set_option(TRANS_OPT_UPLOADPACK, upload_pack); if (keep) @@ -983,10 +983,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); 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); - transport_disconnect(transport); - transport = NULL; + transport_disconnect(gtransport); + gtransport = NULL; return exit_code; } -- cgit v1.2.1 From db5723c6283d9a8dff3397c432af80cf5e2f7766 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 7 Aug 2013 14:43:20 -0700 Subject: fetch: refactor code that prepares a transport Make a helper function prepare_transport() that returns a transport to talk to a given remote. The set_option() helper that used to always affect the file-scope global "gtransport" now takes a transport as its parameter. Signed-off-by: Junio C Hamano --- builtin/fetch.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 636b47ffb7..39a3fc8dea 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -720,6 +720,31 @@ static int truncate_fetch_head(void) 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); +} + +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 int do_fetch(struct transport *transport, struct refspec *refs, int ref_count) { @@ -816,17 +841,6 @@ static int do_fetch(struct transport *transport, return retcode; } -static void set_option(const char *name, const char *value) -{ - int r = transport_set_option(gtransport, name, value); - if (r < 0) - die(_("Option \"%s\" value \"%s\" is not valid for %s"), - name, value, gtransport->url); - if (r > 0) - warning(_("Option \"%s\" is ignored for %s\n"), - name, gtransport->url); -} - static int get_one_remote_for_fetch(struct remote *remote, void *priv) { struct string_list *list = priv; @@ -949,15 +963,7 @@ 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" "remote name from which new revisions should be fetched.")); - gtransport = transport_get(remote, NULL); - transport_set_verbosity(gtransport, 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); - + gtransport = prepare_transport(remote); if (argc > 0) { int j = 0; refs = xcalloc(argc + 1, sizeof(const char *)); -- cgit v1.2.1 From 069d50320257b76504921a7bc77696a6d858bfec Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 7 Aug 2013 15:14:45 -0700 Subject: fetch: refactor code that fetches leftover tags Usually the upload-pack process running on the other side will give us all the reachable tags we need during the primary object transfer in do_fetch(). If that does not happen (e.g. the other side may be running a third-party implementation of upload-pack), we will run another fetch to pick up leftover tags that we know point at the commits reachable from our updated tips. Separate out the code to run this second fetch into a helper function. Signed-off-by: Junio C Hamano --- builtin/fetch.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 39a3fc8dea..0b21f071b3 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -745,6 +745,13 @@ struct transport *prepare_transport(struct remote *remote) return transport; } +static void backfill_tags(struct transport *transport, struct ref *ref_map) +{ + transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); + transport_set_option(transport, TRANS_OPT_DEPTH, "0"); + fetch_refs(transport, ref_map); +} + static int do_fetch(struct transport *transport, struct refspec *refs, int ref_count) { @@ -828,11 +835,8 @@ static int do_fetch(struct transport *transport, struct ref **tail = &ref_map; ref_map = NULL; find_non_local_tags(transport, &ref_map, &tail); - if (ref_map) { - transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); - transport_set_option(transport, TRANS_OPT_DEPTH, "0"); - fetch_refs(transport, ref_map); - } + if (ref_map) + backfill_tags(transport, ref_map); free_refs(ref_map); } -- cgit v1.2.1 From b26ed4305f9e7043133e52990897afbbf1808d6d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 7 Aug 2013 15:47:18 -0700 Subject: 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 --- builtin/fetch.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 0b21f071b3..57ab7e4d63 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -37,6 +37,7 @@ static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; static struct transport *gtransport; +static struct transport *gsecondary; static const char *submodule_prefix = ""; static const char *recurse_submodules_default; @@ -97,6 +98,8 @@ static void unlock_pack(void) { if (gtransport) transport_unlock_pack(gtransport); + if (gsecondary) + transport_unlock_pack(gsecondary); } static void unlock_pack_on_signal(int signo) @@ -747,9 +750,19 @@ struct transport *prepare_transport(struct remote *remote) 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, -- cgit v1.2.1 From 0f73f8bd7974fcf7f9e4608875323c96c6159829 Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Wed, 28 Aug 2013 19:56:17 +0100 Subject: builtin/fetch.c: Fix a sparse warning Sparse issues an "'prepare_transport' was not declared. Should it be static?" warning. In order to suppress the warning, since this symbol only requires file scope, we simply add the static modifier to it's declaration. Signed-off-by: Ramsay Jones Signed-off-by: Junio C Hamano --- builtin/fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 57ab7e4d63..564705555b 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -734,7 +734,7 @@ static void set_option(struct transport *transport, const char *name, const char name, transport->url); } -struct transport *prepare_transport(struct remote *remote) +static struct transport *prepare_transport(struct remote *remote) { struct transport *transport; transport = transport_get(remote, NULL); -- cgit v1.2.1 From 9bbb0fa1fdc6c413b1f35c26e090515e5d0096aa Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 30 Aug 2013 14:12:00 -0400 Subject: refs: report ref type from lock_any_ref_for_update Expose lock_ref_sha1_basic's type_p argument to callers of lock_any_ref_for_update. Update all call sites to ignore it by passing NULL for now. Signed-off-by: Brad King Signed-off-by: Junio C Hamano --- builtin/fetch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index d784b2e694..28e40255be 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -246,7 +246,8 @@ static int s_update_ref(const char *action, rla = default_rla.buf; snprintf(msg, sizeof(msg), "%s: %s", rla, action); lock = lock_any_ref_for_update(ref->name, - check_old ? ref->old_sha1 : NULL, 0); + check_old ? ref->old_sha1 : NULL, + 0, NULL); if (!lock) return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT : STORE_REF_ERROR_OTHER; -- cgit v1.2.1 From f137a45e0db9823d386b18a7a9f5e0fc06c0b67d Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 23 Oct 2013 17:50:38 +0200 Subject: get_ref_map(): rename local variables Rename "refs" -> "refspecs" and "ref_count" -> "refspec_count" to reduce confusion, because they describe an array of "struct refspec", as opposed to the "struct ref" objects that are also used in this function. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index bd7a10164f..2248abfd33 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -165,8 +165,8 @@ static void find_non_local_tags(struct transport *transport, struct ref ***tail); static struct ref *get_ref_map(struct transport *transport, - struct refspec *refs, int ref_count, int tags, - int *autotags) + struct refspec *refspecs, int refspec_count, + int tags, int *autotags) { int i; struct ref *rm; @@ -175,12 +175,12 @@ static struct ref *get_ref_map(struct transport *transport, const struct ref *remote_refs = transport_get_remote_refs(transport); - if (ref_count || tags == TAGS_SET) { + if (refspec_count || tags == TAGS_SET) { struct ref **old_tail; - for (i = 0; i < ref_count; i++) { - get_fetch_map(remote_refs, &refs[i], &tail, 0); - if (refs[i].dst && refs[i].dst[0]) + for (i = 0; i < refspec_count; i++) { + get_fetch_map(remote_refs, &refspecs[i], &tail, 0); + if (refspecs[i].dst && refspecs[i].dst[0]) *autotags = 1; } /* Merge everything on the command line, but not --tags */ -- cgit v1.2.1 From a0fbb5a329ea138e02698312f14ac1aea4b9b4c9 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 06:32:55 +0100 Subject: builtin/fetch.c: reorder function definitions Reorder function definitions to avoid the need for a forward declaration of function find_non_local_tags(). Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 198 +++++++++++++++++++++++++++----------------------------- 1 file changed, 97 insertions(+), 101 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 2248abfd33..61e8117c4a 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -160,9 +160,105 @@ static void add_merge_config(struct ref **head, } } +static int add_existing(const char *refname, const unsigned char *sha1, + int flag, void *cbdata) +{ + struct string_list *list = (struct string_list *)cbdata; + struct string_list_item *item = string_list_insert(list, refname); + item->util = xmalloc(20); + hashcpy(item->util, sha1); + return 0; +} + +static int will_fetch(struct ref **head, const unsigned char *sha1) +{ + struct ref *rm = *head; + while (rm) { + if (!hashcmp(rm->old_sha1, sha1)) + return 1; + rm = rm->next; + } + return 0; +} + static void find_non_local_tags(struct transport *transport, struct ref **head, - struct ref ***tail); + struct ref ***tail) +{ + struct string_list existing_refs = STRING_LIST_INIT_DUP; + struct string_list remote_refs = STRING_LIST_INIT_NODUP; + const struct ref *ref; + struct string_list_item *item = NULL; + + for_each_ref(add_existing, &existing_refs); + for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { + if (prefixcmp(ref->name, "refs/tags/")) + continue; + + /* + * The peeled ref always follows the matching base + * ref, so if we see a peeled ref that we don't want + * to fetch then we can mark the ref entry in the list + * as one to ignore by setting util to NULL. + */ + if (!suffixcmp(ref->name, "^{}")) { + if (item && !has_sha1_file(ref->old_sha1) && + !will_fetch(head, ref->old_sha1) && + !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + item = NULL; + continue; + } + + /* + * If item is non-NULL here, then we previously saw a + * ref not followed by a peeled reference, so we need + * to check if it is a lightweight tag that we want to + * fetch. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + item = NULL; + + /* skip duplicates and refs that we already have */ + if (string_list_has_string(&remote_refs, ref->name) || + string_list_has_string(&existing_refs, ref->name)) + continue; + + item = string_list_insert(&remote_refs, ref->name); + item->util = (void *)ref->old_sha1; + } + string_list_clear(&existing_refs, 1); + + /* + * We may have a final lightweight tag that needs to be + * checked to see if it needs fetching. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + /* + * For all the tags in the remote_refs string list, + * add them to the list of refs to be fetched + */ + for_each_string_list_item(item, &remote_refs) { + /* Unless we have already decided to ignore this item... */ + if (item->util) + { + struct ref *rm = alloc_ref(item->string); + rm->peer_ref = alloc_ref(item->string); + hashcpy(rm->old_sha1, item->util); + **tail = rm; + *tail = &rm->next; + } + } + + string_list_clear(&remote_refs, 0); +} static struct ref *get_ref_map(struct transport *transport, struct refspec *refspecs, int refspec_count, @@ -612,106 +708,6 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) return result; } -static int add_existing(const char *refname, const unsigned char *sha1, - int flag, void *cbdata) -{ - struct string_list *list = (struct string_list *)cbdata; - struct string_list_item *item = string_list_insert(list, refname); - item->util = xmalloc(20); - hashcpy(item->util, sha1); - return 0; -} - -static int will_fetch(struct ref **head, const unsigned char *sha1) -{ - struct ref *rm = *head; - while (rm) { - if (!hashcmp(rm->old_sha1, sha1)) - return 1; - rm = rm->next; - } - return 0; -} - -static void find_non_local_tags(struct transport *transport, - struct ref **head, - struct ref ***tail) -{ - struct string_list existing_refs = STRING_LIST_INIT_DUP; - struct string_list remote_refs = STRING_LIST_INIT_NODUP; - const struct ref *ref; - struct string_list_item *item = NULL; - - for_each_ref(add_existing, &existing_refs); - for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) - continue; - - /* - * The peeled ref always follows the matching base - * ref, so if we see a peeled ref that we don't want - * to fetch then we can mark the ref entry in the list - * as one to ignore by setting util to NULL. - */ - if (!suffixcmp(ref->name, "^{}")) { - if (item && !has_sha1_file(ref->old_sha1) && - !will_fetch(head, ref->old_sha1) && - !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - item = NULL; - continue; - } - - /* - * If item is non-NULL here, then we previously saw a - * ref not followed by a peeled reference, so we need - * to check if it is a lightweight tag that we want to - * fetch. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - item = NULL; - - /* skip duplicates and refs that we already have */ - if (string_list_has_string(&remote_refs, ref->name) || - string_list_has_string(&existing_refs, ref->name)) - continue; - - item = string_list_insert(&remote_refs, ref->name); - item->util = (void *)ref->old_sha1; - } - string_list_clear(&existing_refs, 1); - - /* - * We may have a final lightweight tag that needs to be - * checked to see if it needs fetching. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - /* - * For all the tags in the remote_refs string list, - * add them to the list of refs to be fetched - */ - for_each_string_list_item(item, &remote_refs) { - /* Unless we have already decided to ignore this item... */ - if (item->util) - { - struct ref *rm = alloc_ref(item->string); - rm->peer_ref = alloc_ref(item->string); - hashcpy(rm->old_sha1, item->util); - **tail = rm; - *tail = &rm->next; - } - } - - string_list_clear(&remote_refs, 0); -} - static void check_not_current_branch(struct ref *ref_map) { struct branch *current_branch = branch_get(NULL); -- cgit v1.2.1 From 0281c930f17042473e973f39810c8f7c93955d7d Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 06:32:58 +0100 Subject: fetch: only opportunistically update references based on command line The old code processed (tags == TAGS_SET) before adding the entries used to opportunistically update references mentioned on the command line. The result was that all tags were also considered candidates for opportunistic updating. This is harmless for two reasons: (a) because it would only add entries if there is a configured refspec that covers tags *and* both --tags and another refspec appear on the command-line; (b) because any extra entries would be deleted later by the call to ref_remove_duplicates() anyway. But, to avoid extra work and extra memory usage, and to make the implementation better match the intention, change the algorithm slightly: compute the opportunistic refspecs based only on the command-line arguments, storing the results into a separate temporary list. Then add the tags (which have to come earlier in the list so that they are not de-duped in favor of an opportunistic entry). Then concatenate the temporary list onto the main list. This change will also make later changes easier. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 61e8117c4a..0849f09de1 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -272,32 +272,50 @@ static struct ref *get_ref_map(struct transport *transport, const struct ref *remote_refs = transport_get_remote_refs(transport); if (refspec_count || tags == TAGS_SET) { - struct ref **old_tail; + /* opportunistically-updated references: */ + struct ref *orefs = NULL, **oref_tail = &orefs; for (i = 0; i < refspec_count; i++) { get_fetch_map(remote_refs, &refspecs[i], &tail, 0); if (refspecs[i].dst && refspecs[i].dst[0]) *autotags = 1; } - /* Merge everything on the command line, but not --tags */ + /* Merge everything on the command line (but not --tags) */ for (rm = ref_map; rm; rm = rm->next) rm->fetch_head_status = FETCH_HEAD_MERGE; - if (tags == TAGS_SET) - get_fetch_map(remote_refs, tag_refspec, &tail, 0); /* - * For any refs that we happen to be fetching via command-line - * arguments, take the opportunity to update their configured - * counterparts. However, we do not want to mention these - * entries in FETCH_HEAD at all, as they would simply be - * duplicates of existing entries. + * For any refs that we happen to be fetching via + * command-line arguments, the destination ref might + * have been missing or have been different than the + * remote-tracking ref that would be derived from the + * configured refspec. In these cases, we want to + * take the opportunity to update their configured + * remote-tracking reference. However, we do not want + * to mention these entries in FETCH_HEAD at all, as + * they would simply be duplicates of existing + * entries, so we set them FETCH_HEAD_IGNORE below. + * + * We compute these entries now, based only on the + * refspecs specified on the command line. But we add + * them to the list following the refspecs resulting + * from the tags option so that one of the latter, + * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed + * by ref_remove_duplicates() in favor of one of these + * opportunistic entries with FETCH_HEAD_IGNORE. */ - old_tail = tail; for (i = 0; i < transport->remote->fetch_refspec_nr; i++) get_fetch_map(ref_map, &transport->remote->fetch[i], - &tail, 1); - for (rm = *old_tail; rm; rm = rm->next) + &oref_tail, 1); + + if (tags == TAGS_SET) + get_fetch_map(remote_refs, tag_refspec, &tail, 0); + + *tail = orefs; + for (rm = orefs; rm; rm = rm->next) { rm->fetch_head_status = FETCH_HEAD_IGNORE; + tail = &rm->next; + } } else { /* Use the defaults */ struct remote *remote = transport->remote; @@ -334,8 +352,10 @@ static struct ref *get_ref_map(struct transport *transport, tail = &ref_map->next; } } + if (tags == TAGS_DEFAULT && *autotags) find_non_local_tags(transport, &ref_map, &tail); + ref_remove_duplicates(ref_map); return ref_map; -- cgit v1.2.1 From c5a84e92a2fe9e8748e32341c344d7a6c0f52a50 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 06:32:59 +0100 Subject: fetch --tags: fetch tags *in addition to* other stuff Previously, fetch's "--tags" option was considered equivalent to specifying the refspec "refs/tags/*:refs/tags/*" on the command line; in particular, it caused the remote..refspec configuration to be ignored. But it is not very useful to fetch tags without also fetching other references, whereas it *is* quite useful to be able to fetch tags *in addition to* other references. So change the semantics of this option to do the latter. If a user wants to fetch *only* tags, then it is still possible to specifying an explicit refspec: git fetch 'refs/tags/*:refs/tags/*' Please note that the documentation prior to 1.8.0.3 was ambiguous about this aspect of "fetch --tags" behavior. Commit f0cb2f137c 2012-12-14 fetch --tags: clarify documentation made the documentation match the old behavior. This commit changes the documentation to match the new behavior. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 59 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 24 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 0849f09de1..887fa3e581 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -269,12 +269,12 @@ static struct ref *get_ref_map(struct transport *transport, struct ref *ref_map = NULL; struct ref **tail = &ref_map; - const struct ref *remote_refs = transport_get_remote_refs(transport); + /* opportunistically-updated references: */ + struct ref *orefs = NULL, **oref_tail = &orefs; - if (refspec_count || tags == TAGS_SET) { - /* opportunistically-updated references: */ - struct ref *orefs = NULL, **oref_tail = &orefs; + const struct ref *remote_refs = transport_get_remote_refs(transport); + if (refspec_count) { for (i = 0; i < refspec_count; i++) { get_fetch_map(remote_refs, &refspecs[i], &tail, 0); if (refspecs[i].dst && refspecs[i].dst[0]) @@ -310,12 +310,6 @@ static struct ref *get_ref_map(struct transport *transport, if (tags == TAGS_SET) get_fetch_map(remote_refs, tag_refspec, &tail, 0); - - *tail = orefs; - for (rm = orefs; rm; rm = rm->next) { - rm->fetch_head_status = FETCH_HEAD_IGNORE; - tail = &rm->next; - } } else { /* Use the defaults */ struct remote *remote = transport->remote; @@ -353,9 +347,19 @@ static struct ref *get_ref_map(struct transport *transport, } } - if (tags == TAGS_DEFAULT && *autotags) + if (tags == TAGS_SET) + /* also fetch all tags */ + get_fetch_map(remote_refs, tag_refspec, &tail, 0); + else if (tags == TAGS_DEFAULT && *autotags) find_non_local_tags(transport, &ref_map, &tail); + /* Now append any refs to be updated opportunistically: */ + *tail = orefs; + for (rm = orefs; rm; rm = rm->next) { + rm->fetch_head_status = FETCH_HEAD_IGNORE; + tail = &rm->next; + } + ref_remove_duplicates(ref_map); return ref_map; @@ -846,31 +850,38 @@ static int do_fetch(struct transport *transport, goto cleanup; } if (prune) { - /* - * If --tags was specified, pretend that the user gave us - * the canonical tags refspec - */ + struct refspec *prune_refspecs; + int prune_refspec_count; + + if (ref_count) { + prune_refspecs = refs; + prune_refspec_count = ref_count; + } else { + prune_refspecs = transport->remote->fetch; + prune_refspec_count = transport->remote->fetch_refspec_nr; + } + if (tags == TAGS_SET) { + /* + * --tags was specified. Pretend that the user also + * gave us the canonical tags refspec + */ const char *tags_str = "refs/tags/*:refs/tags/*"; struct refspec *tags_refspec, *refspec; /* Copy the refspec and add the tags to it */ - refspec = xcalloc(ref_count + 1, sizeof(struct refspec)); + refspec = xcalloc(prune_refspec_count + 1, sizeof(*refspec)); tags_refspec = parse_fetch_refspec(1, &tags_str); - memcpy(refspec, refs, ref_count * sizeof(struct refspec)); - memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec)); - ref_count++; + memcpy(refspec, prune_refspecs, prune_refspec_count * sizeof(*refspec)); + memcpy(&refspec[prune_refspec_count], tags_refspec, sizeof(*refspec)); - prune_refs(refspec, ref_count, ref_map); + prune_refs(refspec, prune_refspec_count + 1, ref_map); - ref_count--; /* The rest of the strings belong to fetch_one */ free_refspec(1, tags_refspec); free(refspec); - } else if (ref_count) { - prune_refs(refs, ref_count, ref_map); } else { - prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map); + prune_refs(prune_refspecs, prune_refspec_count, ref_map); } } free_refs(ref_map); -- cgit v1.2.1 From 0838bf47b3b37f6ff13b8de183316483958bf3bb Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 06:33:00 +0100 Subject: fetch --prune: prune only based on explicit refspecs The old behavior of "fetch --prune" was to prune whatever was being fetched. In particular, "fetch --prune --tags" caused tags not only to be fetched, but also to be pruned. This is inappropriate because there is only one tags namespace that is shared among the local repository and all remotes. Therefore, if the user defines a local tag and then runs "git fetch --prune --tags", then the local tag is deleted. Moreover, "--prune" and "--tags" can also be configured via fetch.prune / remote..prune and remote..tagopt, making it even less obvious that an invocation of "git fetch" could result in tag lossage. Since the command "git remote update" invokes "git fetch", it had the same problem. The command "git remote prune", on the other hand, disregarded the setting of remote..tagopt, and so its behavior was inconsistent with that of the other commands. So the old behavior made it too easy to lose tags. To fix this problem, change "fetch --prune" to prune references based only on refspecs specified explicitly by the user, either on the command line or via remote..fetch. Thus, tags are no longer made subject to pruning by the --tags option or the remote..tagopt setting. However, tags *are* still subject to pruning if they are fetched as part of a refspec, and that is good. For example: * On the command line, git fetch --prune 'refs/tags/*:refs/tags/*' causes tags, and only tags, to be fetched and pruned, and is therefore a simple way for the user to get the equivalent of the old behavior of "--prune --tag". * For a remote that was configured with the "--mirror" option, the configuration is set to include [remote "name"] fetch = +refs/*:refs/* , which causes tags to be subject to pruning along with all other references. This is the behavior that will typically be desired for a mirror. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 887fa3e581..1514b908d8 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -850,38 +850,17 @@ static int do_fetch(struct transport *transport, goto cleanup; } if (prune) { - struct refspec *prune_refspecs; - int prune_refspec_count; - + /* + * We only prune based on refspecs specified + * explicitly (via command line or configuration); we + * don't care whether --tags was specified. + */ if (ref_count) { - prune_refspecs = refs; - prune_refspec_count = ref_count; - } else { - prune_refspecs = transport->remote->fetch; - prune_refspec_count = transport->remote->fetch_refspec_nr; - } - - if (tags == TAGS_SET) { - /* - * --tags was specified. Pretend that the user also - * gave us the canonical tags refspec - */ - const char *tags_str = "refs/tags/*:refs/tags/*"; - struct refspec *tags_refspec, *refspec; - - /* Copy the refspec and add the tags to it */ - refspec = xcalloc(prune_refspec_count + 1, sizeof(*refspec)); - tags_refspec = parse_fetch_refspec(1, &tags_str); - memcpy(refspec, prune_refspecs, prune_refspec_count * sizeof(*refspec)); - memcpy(&refspec[prune_refspec_count], tags_refspec, sizeof(*refspec)); - - prune_refs(refspec, prune_refspec_count + 1, ref_map); - - /* The rest of the strings belong to fetch_one */ - free_refspec(1, tags_refspec); - free(refspec); + prune_refs(refs, ref_count, ref_map); } else { - prune_refs(prune_refspecs, prune_refspec_count, ref_map); + prune_refs(transport->remote->fetch, + transport->remote->fetch_refspec_nr, + ref_map); } } free_refs(ref_map); -- cgit v1.2.1 From 90765fa3e01658ac2fb75a4eb740cee7dad4a2dc Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 06:33:04 +0100 Subject: fetch, remote: properly convey --no-prune options to subprocesses If --no-prune is passed to one of the following commands: git fetch --all git fetch --multiple git fetch --recurse-submodules git remote update then it must also be passed to the "fetch" subprocesses that those commands use to do their work. Otherwise there might be a fetch.prune or remote..prune configuration setting that causes pruning to occur, contrary to the user's express wish. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 1514b908d8..5ddb9af05c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -936,8 +936,8 @@ static void add_options_to_argv(struct argv_array *argv) { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune > 0) - argv_array_push(argv, "--prune"); + if (prune != -1) + argv_array_push(argv, prune ? "--prune" : "--no-prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); if (force) -- cgit v1.2.1 From b9afe6654db2bfb776db933f832e7e03052adf98 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 06:33:09 +0100 Subject: ref_remove_duplicates(): simplify loop logic Change the loop body into the more straightforward * remove item from the front of the old list * if necessary, add it to the tail of the new list and return a pointer to the new list (even though it is currently always the same as the input argument, because the first element in the list is currently never deleted). Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 5ddb9af05c..3d978eb58e 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -360,9 +360,7 @@ static struct ref *get_ref_map(struct transport *transport, tail = &rm->next; } - ref_remove_duplicates(ref_map); - - return ref_map; + return ref_remove_duplicates(ref_map); } #define STORE_REF_ERROR_OTHER 1 -- cgit v1.2.1 From 59556548230e617b837343c2c07e357e688e2ca4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 30 Nov 2013 21:55:40 +0100 Subject: replace {pre,suf}fixcmp() with {starts,ends}_with() Leaving only the function definitions and declarations so that any new topic in flight can still make use of the old functions, replace existing uses of the prefixcmp() and suffixcmp() with new API functions. The change can be recreated by mechanically applying this: $ git grep -l -e prefixcmp -e suffixcmp -- \*.c | grep -v strbuf\\.c | xargs perl -pi -e ' s|!prefixcmp\(|starts_with\(|g; s|prefixcmp\(|!starts_with\(|g; s|!suffixcmp\(|ends_with\(|g; s|suffixcmp\(|!ends_with\(|g; ' on the result of preparatory changes in this series. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/fetch.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index bd7a10164f..4dd82504c7 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -313,7 +313,7 @@ static int update_local_ref(struct ref *ref, } if (!is_null_sha1(ref->old_sha1) && - !prefixcmp(ref->name, "refs/tags/")) { + starts_with(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); strbuf_addf(display, "%c %-*s %-*s -> %s%s", @@ -336,10 +336,10 @@ static int update_local_ref(struct ref *ref, * more likely to follow a standard layout. */ const char *name = remote_ref ? remote_ref->name : ""; - if (!prefixcmp(name, "refs/tags/")) { + if (starts_with(name, "refs/tags/")) { msg = "storing tag"; what = _("[new tag]"); - } else if (!prefixcmp(name, "refs/heads/")) { + } else if (starts_with(name, "refs/heads/")) { msg = "storing head"; what = _("[new branch]"); } else { @@ -471,15 +471,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, kind = ""; what = ""; } - else if (!prefixcmp(rm->name, "refs/heads/")) { + else if (starts_with(rm->name, "refs/heads/")) { kind = "branch"; what = rm->name + 11; } - else if (!prefixcmp(rm->name, "refs/tags/")) { + else if (starts_with(rm->name, "refs/tags/")) { kind = "tag"; what = rm->name + 10; } - else if (!prefixcmp(rm->name, "refs/remotes/")) { + else if (starts_with(rm->name, "refs/remotes/")) { kind = "remote-tracking branch"; what = rm->name + 13; } @@ -644,7 +644,7 @@ static void find_non_local_tags(struct transport *transport, for_each_ref(add_existing, &existing_refs); for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) + if (!starts_with(ref->name, "refs/tags/")) continue; /* @@ -653,7 +653,7 @@ static void find_non_local_tags(struct transport *transport, * to fetch then we can mark the ref entry in the list * as one to ignore by setting util to NULL. */ - if (!suffixcmp(ref->name, "^{}")) { + if (ends_with(ref->name, "^{}")) { if (item && !has_sha1_file(ref->old_sha1) && !will_fetch(head, ref->old_sha1) && !has_sha1_file(item->util) && @@ -892,7 +892,7 @@ static int get_remote_group(const char *key, const char *value, void *priv) { struct remote_group_data *g = priv; - if (!prefixcmp(key, "remotes.") && + if (starts_with(key, "remotes.") && !strcmp(key + 8, g->name)) { /* split list by white space */ int space = strcspn(value, " \t\n"); -- cgit v1.2.1 From 5594bcad21da70d2e9704cf96baa91b8d99dc27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Thu, 5 Dec 2013 10:31:11 +0700 Subject: clone,fetch: catch non positive --depth option value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of simply ignoring the value passed to --depth option when it is zero or negative, catch and report it as an error to let people know that they were using the option incorrectly. Original-patch-by: Andrés G. Aragoneses Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/fetch.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 564705555b..72930fcfdf 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1045,6 +1045,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } } + /* no need to be strict, transport_set_option() will validate it again */ + if (depth && atoi(depth) < 1) + die(_("depth %s is not a positive number"), depth); + if (recurse_submodules != RECURSE_SUBMODULES_OFF) { if (recurse_submodules_default) { int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default); -- cgit v1.2.1 From 4820a33baa963c4559736d7a1c4c35f8dcb37293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Thu, 5 Dec 2013 20:02:40 +0700 Subject: fetch: support fetching from a shallow repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch just put together pieces from the 8 steps patch. We stop at step 7 and reject refs that require new shallow commits. Note that, by rejecting refs that require new shallow commits, we leave dangling objects in the repo, which become "object islands" by the next "git fetch" of the same source. If the first fetch our "ours" set is zero and we do practically nothing at step 7, "ours" is full at the next fetch and we may need to walk through commits for reachability test. Room for improvement. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/fetch.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index bd7a10164f..7b41a7e388 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -405,6 +405,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) struct ref **rm = cb_data; struct ref *ref = *rm; + while (ref && ref->status == REF_STATUS_REJECT_SHALLOW) + ref = ref->next; if (!ref) return -1; /* end of the list */ *rm = ref->next; @@ -451,6 +453,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, struct ref *ref = NULL; const char *merge_status_marker = ""; + if (rm->status == REF_STATUS_REJECT_SHALLOW) { + if (want_status == FETCH_HEAD_MERGE) + warning(_("reject %s because shallow roots are not allowed to be updated"), + rm->peer_ref ? rm->peer_ref->name : rm->name); + continue; + } + commit = lookup_commit_reference_gently(rm->old_sha1, 1); if (!commit) rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE; -- cgit v1.2.1 From 48d25cae22667dfc2c31ad620172c0f0a3ac1490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Thu, 5 Dec 2013 20:02:42 +0700 Subject: fetch: add --update-shallow to accept refs that update .git/shallow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same steps are done as in when --update-shallow is not given. The only difference is we now add all shallow commits in "ours" and "theirs" to .git/shallow (aka "step 8"). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/fetch.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 7b41a7e388..d2e4fc03d8 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -36,7 +36,7 @@ static int prune = -1; /* unspecified */ static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int tags = TAGS_DEFAULT, unshallow; +static int tags = TAGS_DEFAULT, unshallow, update_shallow; static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; @@ -104,6 +104,8 @@ static struct option builtin_fetch_options[] = { { OPTION_STRING, 0, "recurse-submodules-default", &recurse_submodules_default, NULL, N_("default mode for recursion"), PARSE_OPT_HIDDEN }, + OPT_BOOL(0, "update-shallow", &update_shallow, + N_("accept refs that update .git/shallow")), OPT_END() }; @@ -768,6 +770,8 @@ static struct transport *prepare_transport(struct remote *remote) set_option(transport, TRANS_OPT_KEEP, "yes"); if (depth) set_option(transport, TRANS_OPT_DEPTH, depth); + if (update_shallow) + set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); return transport; } -- cgit v1.2.1 From 4b3b33a747c325f76b1f6eef89c231609dd4d361 Mon Sep 17 00:00:00 2001 From: Tom Miller Date: Thu, 2 Jan 2014 20:28:51 -0600 Subject: fetch --prune: always print header url If "fetch --prune" is run with no new refs to fetch, but it has refs to prune. Then, the header url is not printed as it would if there were new refs to fetch. Output before this patch: $ git fetch --prune remote-with-no-new-refs x [deleted] (none) -> origin/world Output after this patch: $ git fetch --prune remote-with-no-new-refs From https://github.com/git/git x [deleted] (none) -> origin/test Signed-off-by: Tom Miller Signed-off-by: Junio C Hamano --- builtin/fetch.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 1e7d617f46..1b81cf9077 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -44,6 +44,7 @@ static struct transport *gtransport; static struct transport *gsecondary; static const char *submodule_prefix = ""; static const char *recurse_submodules_default; +static int shown_url = 0; static int option_parse_recurse_submodules(const struct option *opt, const char *arg, int unset) @@ -535,7 +536,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, { FILE *fp; struct commit *commit; - int url_len, i, shown_url = 0, rc = 0; + int url_len, i, rc = 0; struct strbuf note = STRBUF_INIT; const char *what, *kind; struct ref *rm; @@ -708,17 +709,36 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map) return ret; } -static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) +static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, + const char *raw_url) { - int result = 0; + int url_len, i, result = 0; struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map); + char *url; const char *dangling_msg = dry_run ? _(" (%s will become dangling)") : _(" (%s has become dangling)"); + if (raw_url) + url = transport_anonymize_url(raw_url); + else + url = xstrdup("foreign"); + + url_len = strlen(url); + for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) + ; + + url_len = i + 1; + if (4 < i && !strncmp(".git", url + i - 3, 4)) + url_len = i - 3; + for (ref = stale_refs; ref; ref = ref->next) { if (!dry_run) result |= delete_ref(ref->name, NULL, 0); + if (verbosity >= 0 && !shown_url) { + fprintf(stderr, _("From %.*s\n"), url_len, url); + shown_url = 1; + } if (verbosity >= 0) { fprintf(stderr, " x %-*s %-*s -> %s\n", TRANSPORT_SUMMARY(_("[deleted]")), @@ -726,6 +746,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) warn_dangling_symref(stderr, dangling_msg, ref->name); } } + free(url); free_refs(stale_refs); return result; } @@ -854,11 +875,12 @@ static int do_fetch(struct transport *transport, * don't care whether --tags was specified. */ if (ref_count) { - prune_refs(refs, ref_count, ref_map); + prune_refs(refs, ref_count, ref_map, transport->url); } else { prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, - ref_map); + ref_map, + transport->url); } } free_refs(ref_map); -- cgit v1.2.1 From 10a6cc8890ec1e5459c05ddeb28a671acdc37d60 Mon Sep 17 00:00:00 2001 From: Tom Miller Date: Thu, 2 Jan 2014 20:28:52 -0600 Subject: fetch --prune: Run prune before fetching When we have a remote-tracking branch named "frotz/nitfol" from a previous fetch, and the upstream now has a branch named "frotz", fetch would fail to remove "frotz/nitfol" with a "git fetch --prune" from the upstream. git would inform the user to use "git remote prune" to fix the problem. Change the way "fetch --prune" works by moving the pruning operation before the fetching operation. This way, instead of warning the user of a conflict, it autmatically fixes it. Signed-off-by: Tom Miller Tested-by: Thomas Rast Signed-off-by: Junio C Hamano --- builtin/fetch.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'builtin/fetch.c') diff --git a/builtin/fetch.c b/builtin/fetch.c index 1b81cf9077..09825c84d7 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -863,11 +863,6 @@ static int do_fetch(struct transport *transport, if (tags == TAGS_DEFAULT && autotags) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); - if (fetch_refs(transport, ref_map)) { - free_refs(ref_map); - retcode = 1; - goto cleanup; - } if (prune) { /* * We only prune based on refspecs specified @@ -883,6 +878,11 @@ static int do_fetch(struct transport *transport, transport->url); } } + if (fetch_refs(transport, ref_map)) { + free_refs(ref_map); + retcode = 1; + goto cleanup; + } free_refs(ref_map); /* if neither --no-tags nor --tags was specified, do automated tag -- cgit v1.2.1