From: Junio C Hamano Date: Tue, 6 Mar 2018 22:54:01 +0000 (-0800) Subject: Merge branch 'ab/fetch-prune' X-Git-Tag: v2.17.0-rc0~48 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/c1a7902f9adda4c3a64de99d565cf9982f12b1d9?ds=inline;hp=-c Merge branch 'ab/fetch-prune' Clarify how configured fetch refspecs interact with the "--prune" option of "git fetch", and also add a handy short-hand for getting rid of stale tags that are locally held. * ab/fetch-prune: fetch: make the --prune-tags work with fetch: add a --prune-tags option and fetch.pruneTags config fetch tests: add scaffolding for the new fetch.pruneTags git-fetch & config doc: link to the new PRUNING section git remote doc: correct dangerous lies about what prune does git fetch doc: add a new section to explain the ins & outs of pruning fetch tests: fetch as well as fetch [] fetch tests: expand case/esac for later change fetch tests: double quote a variable for interpolation fetch tests: test --prune and refspec interaction fetch tests: add a tag to be deleted to the pruning tests fetch tests: re-arrange arguments for future readability fetch tests: refactor in preparation for testing tag pruning remote: add a macro for "refs/tags/*:refs/tags/*" fetch: stop accessing "remote" variable indirectly fetch: trivially refactor assignment to ref_nr fetch: don't redundantly NULL something calloc() gave us --- c1a7902f9adda4c3a64de99d565cf9982f12b1d9 diff --combined Documentation/config.txt index f57e9cf10c,e254bfd531..bbd66f5b98 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -1398,7 -1398,16 +1398,16 @@@ fetch.unpackLimit: fetch.prune:: If true, fetch will automatically behave as if the `--prune` - option was given on the command line. See also `remote..prune`. + option was given on the command line. See also `remote..prune` + and the PRUNING section of linkgit:git-fetch[1]. + + fetch.pruneTags:: + If true, fetch will automatically behave as if the + `refs/tags/*:refs/tags/*` refspec was provided when pruning, + if not set already. This allows for setting both this option + and `fetch.prune` to maintain a 1=1 mapping to upstream + refs. See also `remote..pruneTags` and the PRUNING + section of linkgit:git-fetch[1]. fetch.output:: Control how ref update status is printed. Valid values are @@@ -2945,6 -2954,15 +2954,15 @@@ remote..prune: remote (as if the `--prune` option was given on the command line). Overrides `fetch.prune` settings, if any. + remote..pruneTags:: + When set to true, fetching from this remote by default will also + remove any local tags that no longer exist on the remote if pruning + is activated in general via `remote..prune`, `fetch.prune` or + `--prune`. Overrides `fetch.pruneTags` settings, if any. + + + See also `remote..prune` and the PRUNING section of + linkgit:git-fetch[1]. + remotes.:: The list of remotes which are fetched by "git remote update ". See linkgit:git-remote[1]. @@@ -3343,10 -3361,6 +3361,10 @@@ uploadpack.packObjectsHook: was run. I.e., `upload-pack` will feed input intended for `pack-objects` to the hook, and expects a completed packfile on stdout. + +uploadpack.allowFilter:: + If this option is set, `upload-pack` will advertise partial + clone and partial fetch object filtering. + Note that this configuration variable is ignored if it is seen in the repository-level config (this is a safety measure against fetching from diff --combined builtin/fetch.c index 8ee998ea2e,c96f17a9a3..d32d94692c --- a/builtin/fetch.c +++ b/builtin/fetch.c @@@ -19,7 -19,6 +19,7 @@@ #include "argv-array.h" #include "utf8.h" #include "packfile.h" +#include "list-objects-filter-options.h" static const char * const builtin_fetch_usage[] = { N_("git fetch [] [ [...]]"), @@@ -39,6 -38,10 +39,10 @@@ static int fetch_prune_config = -1; /* static int prune = -1; /* unspecified */ #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ + static int fetch_prune_tags_config = -1; /* unspecified */ + static int prune_tags = -1; /* unspecified */ + #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ + static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative; static int progress = -1; static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; @@@ -57,7 -60,6 +61,7 @@@ static int recurse_submodules_default static int shown_url = 0; static int refmap_alloc, refmap_nr; static const char **refmap_array; +static struct list_objects_filter_options filter_options; static int git_fetch_config(const char *k, const char *v, void *cb) { @@@ -66,6 -68,11 +70,11 @@@ return 0; } + if (!strcmp(k, "fetch.prunetags")) { + fetch_prune_tags_config = git_config_bool(k, v); + return 0; + } + if (!strcmp(k, "submodule.recurse")) { int r = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; @@@ -128,6 -135,8 +137,8 @@@ static struct option builtin_fetch_opti N_("number of submodules fetched in parallel")), OPT_BOOL('p', "prune", &prune, N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('P', "prune-tags", &prune_tags, + N_("prune local tags no longer on remote and clobber changed tags")), { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules }, @@@ -163,7 -172,6 +174,7 @@@ TRANSPORT_FAMILY_IPV4), OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), TRANSPORT_FAMILY_IPV6), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; @@@ -1048,11 -1056,6 +1059,11 @@@ static struct transport *prepare_transp set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes"); if (update_shallow) set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); + if (filter_options.choice) { + set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, + filter_options.filter_spec); + set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); + } return transport; } @@@ -1220,6 -1223,8 +1231,8 @@@ static void add_options_to_argv(struct argv_array_push(argv, "--dry-run"); if (prune != -1) argv_array_push(argv, prune ? "--prune" : "--no-prune"); + if (prune_tags != -1) + argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); if (force) @@@ -1273,62 -1278,15 +1286,65 @@@ static int fetch_multiple(struct string return result; } +/* + * Fetching from the promisor remote should use the given filter-spec + * or inherit the default filter-spec from the config. + */ +static inline void fetch_one_setup_partial(struct remote *remote) +{ + /* + * Explicit --no-filter argument overrides everything, regardless + * of any prior partial clones and fetches. + */ + if (filter_options.no_filter) + return; + + /* + * If no prior partial clone/fetch and the current fetch DID NOT + * request a partial-fetch, do a normal fetch. + */ + if (!repository_format_partial_clone && !filter_options.choice) + return; + + /* + * If this is the FIRST partial-fetch request, we enable partial + * on this repo and remember the given filter-spec as the default + * for subsequent fetches to this remote. + */ + if (!repository_format_partial_clone && filter_options.choice) { + partial_clone_register(remote->name, &filter_options); + return; + } + + /* + * We are currently limited to only ONE promisor remote and only + * allow partial-fetches from the promisor remote. + */ + if (strcmp(remote->name, repository_format_partial_clone)) { + if (filter_options.choice) + die(_("--filter can only be used with the remote configured in core.partialClone")); + return; + } + + /* + * Do a partial-fetch from the promisor remote using either the + * explicitly given filter-spec or inherit the filter-spec from + * the config. + */ + if (!filter_options.choice) + partial_clone_get_default_filter_spec(&filter_options); + return; +} + - static int fetch_one(struct remote *remote, int argc, const char **argv) + static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok) { static const char **refs = NULL; struct refspec *refspec; int ref_nr = 0; + int j = 0; int exit_code; + int maybe_prune_tags; + int remote_via_config = remote_is_configured(remote, 0); if (!remote) die(_("No remote repository specified. Please, specify either a URL or a\n" @@@ -1338,18 -1296,39 +1354,39 @@@ if (prune < 0) { /* no command line request */ - if (0 <= gtransport->remote->prune) - prune = gtransport->remote->prune; + if (0 <= remote->prune) + prune = remote->prune; else if (0 <= fetch_prune_config) prune = fetch_prune_config; else prune = PRUNE_BY_DEFAULT; } + if (prune_tags < 0) { + /* no command line request */ + if (0 <= remote->prune_tags) + prune_tags = remote->prune_tags; + else if (0 <= fetch_prune_tags_config) + prune_tags = fetch_prune_tags_config; + else + prune_tags = PRUNE_TAGS_BY_DEFAULT; + } + + maybe_prune_tags = prune_tags_ok && prune_tags; + if (maybe_prune_tags && remote_via_config) + add_prune_tags_to_fetch_refspec(remote); + + if (argc > 0 || (maybe_prune_tags && !remote_via_config)) { + size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1); + refs = xcalloc(nr_alloc, sizeof(const char *)); + if (maybe_prune_tags) { + refs[j++] = xstrdup("refs/tags/*:refs/tags/*"); + ref_nr++; + } + } + if (argc > 0) { - int j = 0; int i; - refs = xcalloc(st_add(argc, 1), sizeof(const char *)); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "tag")) { i++; @@@ -1359,9 -1338,8 +1396,8 @@@ argv[i], argv[i]); } else refs[j++] = argv[i]; + ref_nr++; } - refs[j] = NULL; - ref_nr = j; } sigchain_push_common(unlock_pack_on_signal); @@@ -1378,14 -1356,12 +1414,15 @@@ int cmd_fetch(int argc, const char **ar { int i; struct string_list list = STRING_LIST_INIT_DUP; - struct remote *remote; + struct remote *remote = NULL; int result = 0; ++ int prune_tags_ok = 1; struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; packet_trace_identity("fetch"); + fetch_if_missing = 0; + /* Record the command line for the reflog */ strbuf_addstr(&default_rla, "fetch"); for (i = 1; i < argc; i++) @@@ -1419,23 -1395,23 +1456,23 @@@ if (depth || deepen_since || deepen_not.nr) deepen = 1; + if (filter_options.choice && !repository_format_partial_clone) + die("--filter can only be used when extensions.partialClone is set"); + if (all) { if (argc == 1) die(_("fetch --all does not take a repository argument")); else if (argc > 1) die(_("fetch --all does not make sense with refspecs")); (void) for_each_remote(get_one_remote_for_fetch, &list); - result = fetch_multiple(&list); } else if (argc == 0) { /* No arguments -- use default remote */ remote = remote_get(NULL); - result = fetch_one(remote, argc, argv, 1); } else if (multiple) { /* All arguments are assumed to be remotes or groups */ for (i = 0; i < argc; i++) if (!add_remote_or_group(argv[i], &list)) die(_("No such remote or remote group: %s"), argv[i]); - result = fetch_multiple(&list); } else { /* Single remote or group */ (void) add_remote_or_group(argv[0], &list); @@@ -1443,25 -1419,14 +1480,26 @@@ /* More than one remote */ if (argc > 1) die(_("Fetching a group and specifying refspecs does not make sense")); - result = fetch_multiple(&list); } else { /* Zero or one remotes */ remote = remote_get(argv[0]); - result = fetch_one(remote, argc-1, argv+1, argc == 1); ++ prune_tags_ok = (argc == 1); + argc--; + argv++; } } + if (remote) { + if (filter_options.choice || repository_format_partial_clone) + fetch_one_setup_partial(remote); - result = fetch_one(remote, argc, argv); ++ result = fetch_one(remote, argc, argv, prune_tags_ok); + } else { + if (filter_options.choice) + die(_("--filter can only be used with the remote configured in core.partialClone")); + /* TODO should this also die if we have a previous partial-clone? */ + result = fetch_multiple(&list); + } + if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) { struct argv_array options = ARGV_ARRAY_INIT; diff --combined contrib/completion/git-completion.bash index c7d5c7af29,4ecd0d4d7a..4c86ab66e2 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@@ -594,7 -594,7 +594,7 @@@ __git_is_configured_remote ( __git_list_merge_strategies () { - git merge -s help 2>&1 | + LANG=C LC_ALL=C git merge -s help 2>&1 | sed -n -e '/[Aa]vailable strategies are: /,/^$/{ s/\.$// s/.*:// @@@ -1077,7 -1077,7 +1077,7 @@@ _git_am ( { __git_find_repo_path if [ -d "$__git_repo_path"/rebase-apply ]; then - __gitcomp "--skip --continue --resolved --abort" + __gitcomp "--skip --continue --resolved --abort --quit" return fi case "$cur" in @@@ -1468,7 -1468,7 +1468,7 @@@ __git_fetch_recurse_submodules="yes on- __git_fetch_options=" --quiet --verbose --append --upload-pack --force --keep --depth= --tags --no-tags --all --prune --dry-run --recurse-submodules= - --unshallow --update-shallow + --unshallow --update-shallow --prune-tags " _git_fetch () diff --combined t/t5510-fetch.sh index 3debc87d4a,dce2371302..da9ac00557 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@@ -222,9 -222,12 +222,9 @@@ test_expect_success 'fetch uses remote ( cd descriptive && git fetch o 2>actual && - grep " -> refs/crazyheads/descriptive-branch$" actual | - test_i18ngrep "new branch" && - grep " -> descriptive-tag$" actual | - test_i18ngrep "new tag" && - grep " -> crazy$" actual | - test_i18ngrep "new ref" + test_i18ngrep "new branch.* -> refs/crazyheads/descriptive-branch$" actual && + test_i18ngrep "new tag.* -> descriptive-tag$" actual && + test_i18ngrep "new ref.* -> crazy$" actual ) && git checkout master ' @@@ -540,82 -543,232 +540,232 @@@ test_expect_success "should be able to set_config_tristate () { # var=$1 val=$2 case "$2" in - unset) test_unconfig "$1" ;; - *) git config "$1" "$2" ;; + unset) + test_unconfig "$1" + ;; + *) + git config "$1" "$2" + key=$(echo $1 | sed -e 's/^remote\.origin/fetch/') + git_fetch_c="$git_fetch_c -c $key=$2" + ;; esac } test_configured_prune () { - fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4 + test_configured_prune_type "$@" "name" + test_configured_prune_type "$@" "link" + } - test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" ' + test_configured_prune_type () { + fetch_prune=$1 + remote_origin_prune=$2 + fetch_prune_tags=$3 + remote_origin_prune_tags=$4 + expected_branch=$5 + expected_tag=$6 + cmdline=$7 + mode=$8 + + if test -z "$cmdline_setup" + then + test_expect_success 'setup cmdline_setup variable for subsequent test' ' + remote_url="file://$(git -C one config remote.origin.url)" && + remote_fetch="$(git -C one config remote.origin.fetch)" && + cmdline_setup="\"$remote_url\" \"$remote_fetch\"" + ' + fi + + if test "$mode" = 'link' + then + new_cmdline="" + + if test "$cmdline" = "" + then + new_cmdline=$cmdline_setup + else + new_cmdline=$(printf "%s" "$cmdline" | perl -pe 's[origin(?!/)]["'"$remote_url"'"]g') + fi + + if test "$fetch_prune_tags" = 'true' || + test "$remote_origin_prune_tags" = 'true' + then + if ! printf '%s' "$cmdline\n" | grep -q refs/remotes/origin/ + then + new_cmdline="$new_cmdline refs/tags/*:refs/tags/*" + fi + fi + + cmdline="$new_cmdline" + fi + + test_expect_success "$mode prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6" ' # make sure a newbranch is there in . and also in one git branch -f newbranch && + git tag -f newtag && ( cd one && test_unconfig fetch.prune && + test_unconfig fetch.pruneTags && test_unconfig remote.origin.prune && - git fetch && - git rev-parse --verify refs/remotes/origin/newbranch + test_unconfig remote.origin.pruneTags && + git fetch '"$cmdline_setup"' && + git rev-parse --verify refs/remotes/origin/newbranch && + git rev-parse --verify refs/tags/newtag ) && # now remove it git branch -d newbranch && + git tag -d newtag && # then test ( cd one && + git_fetch_c="" && set_config_tristate fetch.prune $fetch_prune && + set_config_tristate fetch.pruneTags $fetch_prune_tags && set_config_tristate remote.origin.prune $remote_origin_prune && - - git fetch $cmdline && - case "$expected" in + set_config_tristate remote.origin.pruneTags $remote_origin_prune_tags && + + if test "$mode" != "link" + then + git_fetch_c="" + fi && + git$git_fetch_c fetch '"$cmdline"' && + case "$expected_branch" in pruned) test_must_fail git rev-parse --verify refs/remotes/origin/newbranch ;; kept) git rev-parse --verify refs/remotes/origin/newbranch ;; + esac && + case "$expected_tag" in + pruned) + test_must_fail git rev-parse --verify refs/tags/newtag + ;; + kept) + git rev-parse --verify refs/tags/newtag + ;; esac ) ' } - test_configured_prune unset unset "" kept - test_configured_prune unset unset "--no-prune" kept - test_configured_prune unset unset "--prune" pruned - - test_configured_prune false unset "" kept - test_configured_prune false unset "--no-prune" kept - test_configured_prune false unset "--prune" pruned - - test_configured_prune true unset "" pruned - test_configured_prune true unset "--prune" pruned - test_configured_prune true unset "--no-prune" kept - - test_configured_prune unset false "" kept - test_configured_prune unset false "--no-prune" kept - test_configured_prune unset false "--prune" pruned - - test_configured_prune false false "" kept - test_configured_prune false false "--no-prune" kept - test_configured_prune false false "--prune" pruned - - test_configured_prune true false "" kept - test_configured_prune true false "--prune" pruned - test_configured_prune true false "--no-prune" kept - - test_configured_prune unset true "" pruned - test_configured_prune unset true "--no-prune" kept - test_configured_prune unset true "--prune" pruned - - test_configured_prune false true "" pruned - test_configured_prune false true "--no-prune" kept - test_configured_prune false true "--prune" pruned - - test_configured_prune true true "" pruned - test_configured_prune true true "--prune" pruned - test_configured_prune true true "--no-prune" kept + # $1 config: fetch.prune + # $2 config: remote..prune + # $3 config: fetch.pruneTags + # $4 config: remote..pruneTags + # $5 expect: branch to be pruned? + # $6 expect: tag to be pruned? + # $7 git-fetch $cmdline: + # + # $1 $2 $3 $4 $5 $6 $7 + test_configured_prune unset unset unset unset kept kept "" + test_configured_prune unset unset unset unset kept kept "--no-prune" + test_configured_prune unset unset unset unset pruned kept "--prune" + test_configured_prune unset unset unset unset kept pruned \ + "--prune origin refs/tags/*:refs/tags/*" + test_configured_prune unset unset unset unset pruned pruned \ + "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" + + test_configured_prune false unset unset unset kept kept "" + test_configured_prune false unset unset unset kept kept "--no-prune" + test_configured_prune false unset unset unset pruned kept "--prune" + + test_configured_prune true unset unset unset pruned kept "" + test_configured_prune true unset unset unset pruned kept "--prune" + test_configured_prune true unset unset unset kept kept "--no-prune" + + test_configured_prune unset false unset unset kept kept "" + test_configured_prune unset false unset unset kept kept "--no-prune" + test_configured_prune unset false unset unset pruned kept "--prune" + + test_configured_prune false false unset unset kept kept "" + test_configured_prune false false unset unset kept kept "--no-prune" + test_configured_prune false false unset unset pruned kept "--prune" + test_configured_prune false false unset unset kept pruned \ + "--prune origin refs/tags/*:refs/tags/*" + test_configured_prune false false unset unset pruned pruned \ + "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" + + test_configured_prune true false unset unset kept kept "" + test_configured_prune true false unset unset pruned kept "--prune" + test_configured_prune true false unset unset kept kept "--no-prune" + + test_configured_prune unset true unset unset pruned kept "" + test_configured_prune unset true unset unset kept kept "--no-prune" + test_configured_prune unset true unset unset pruned kept "--prune" + + test_configured_prune false true unset unset pruned kept "" + test_configured_prune false true unset unset kept kept "--no-prune" + test_configured_prune false true unset unset pruned kept "--prune" + + test_configured_prune true true unset unset pruned kept "" + test_configured_prune true true unset unset pruned kept "--prune" + test_configured_prune true true unset unset kept kept "--no-prune" + test_configured_prune true true unset unset kept pruned \ + "--prune origin refs/tags/*:refs/tags/*" + test_configured_prune true true unset unset pruned pruned \ + "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" + + # --prune-tags on its own does nothing, needs --prune as well, same + # for for fetch.pruneTags without fetch.prune + test_configured_prune unset unset unset unset kept kept "--prune-tags" + test_configured_prune unset unset true unset kept kept "" + test_configured_prune unset unset unset true kept kept "" + + # These will prune the tags + test_configured_prune unset unset unset unset pruned pruned "--prune --prune-tags" + test_configured_prune true unset true unset pruned pruned "" + test_configured_prune unset true unset true pruned pruned "" + + # remote..pruneTags overrides fetch.pruneTags, just like + # remote..prune overrides fetch.prune if set. + test_configured_prune true unset true unset pruned pruned "" + test_configured_prune false true false true pruned pruned "" + test_configured_prune true false true false kept kept "" + + # When --prune-tags is supplied it's ignored if an explicit refspec is + # given, same for the configuration options. + test_configured_prune unset unset unset unset pruned kept \ + "--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*" + test_configured_prune unset unset true unset pruned kept \ + "--prune origin +refs/heads/*:refs/remotes/origin/*" + test_configured_prune unset unset unset true pruned kept \ + "--prune origin +refs/heads/*:refs/remotes/origin/*" + + # Pruning that also takes place if a file:// url replaces a named + # remote. However, because there's no implicit + # +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the + # command-line negates --prune-tags, the branches will not be pruned. + test_configured_prune_type unset unset unset unset kept kept "origin --prune-tags" "name" + test_configured_prune_type unset unset unset unset kept kept "origin --prune-tags" "link" + test_configured_prune_type unset unset unset unset pruned pruned "origin --prune --prune-tags" "name" + test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "link" + test_configured_prune_type unset unset unset unset pruned pruned "--prune --prune-tags origin" "name" + test_configured_prune_type unset unset unset unset kept pruned "--prune --prune-tags origin" "link" + test_configured_prune_type unset unset true unset pruned pruned "--prune origin" "name" + test_configured_prune_type unset unset true unset kept pruned "--prune origin" "link" + test_configured_prune_type unset unset unset true pruned pruned "--prune origin" "name" + test_configured_prune_type unset unset unset true kept pruned "--prune origin" "link" + test_configured_prune_type true unset true unset pruned pruned "origin" "name" + test_configured_prune_type true unset true unset kept pruned "origin" "link" + test_configured_prune_type unset true true unset pruned pruned "origin" "name" + test_configured_prune_type unset true true unset kept pruned "origin" "link" + test_configured_prune_type unset true unset true pruned pruned "origin" "name" + test_configured_prune_type unset true unset true kept pruned "origin" "link" + + # When all remote.origin.fetch settings are deleted a --prune + # --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so + # tags, but not tracking branches, will be deleted. + test_expect_success 'remove remote.origin.fetch "one"' ' + ( + cd one && + git config --unset-all remote.origin.fetch + ) + ' + test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "name" + test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "link" test_expect_success 'all boundary commits are excluded' ' test_commit base &&