[WIP: This doesn't (yet) work as advertised, see further WIP note below] Add a --fetch-prune option to git-fetch along with fetch.pruneTags config option. This allows for doing: git fetch origin -p -P Or simply: git config fetch.prune true && git config fetch.pruneTags true && git fetch origin Instead of the much more verbose: git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*' A use-case of git which is a pain to support now is frequent pulling from a repo which is having both its branches *and* tags deleted regularly. At work we create deployment tags in the repo for each rollout, and there's *lots* of those, so they're archived within weeks for performance reasons. Without this change it's hard to centrally configure such repos in /etc/gitconfig (on servers that are only used for working with them). You need to set fetch.prune=true globally, and then for each repo: git -C {} config --replace-all remote.origin.fetch "refs/tags/*:refs/tags/*" "^refs/tags/*:refs/tags/*$" Now I can simply set fetch.pruneTags=true in /etc/gitconfig as well, and users running "git pull" will automatically get the pruning semantics we want. See my "Re: [BUG] git remote prune removes local tags, depending on fetch config" (87po6ahx87.fsf@xxxxxxxxxxxxxxxxxxx; https://public-inbox.org/git/87po6ahx87.fsf@xxxxxxxxxxxxxxxxxxx/) for more details. [WIP: This doesn't actually work, since the implementation is to just pretend that 'refs/tags/*:refs/tags/*' was supplied on the command-line, we'll consequently stop fetching any branches, since we don't use the default refspec found in .git/config anymore I'm not familiar enough with this code and the refspec machinery to know if this argument injection is crazy, and how to tell it to *also* do whatever it's doing by default] Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx> --- Documentation/config.txt | 15 +++++++++++++++ Documentation/fetch-options.txt | 15 ++++++++++++++- builtin/fetch.c | 37 ++++++++++++++++++++++++++++++++++--- remote.c | 2 ++ remote.h | 1 + t/t5510-fetch.sh | 1 + 6 files changed, 67 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 0f27af5760..476175ef8d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1401,6 +1401,14 @@ fetch.prune:: option was given on the command line. See also `remote.<name>.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 upstrem + refs. See also `remote.<name>.pruneTags` and the PRUNING + section of linkgit:git-fetch[1]. + fetch.output:: Control how ref update status is printed. Valid values are `full` and `compact`. Default value is `full`. See section @@ -2945,6 +2953,13 @@ remote.<name>.prune:: remove any remote-tracking references that no longer exist on the remote (as if the `--prune` option was given on the command line). Overrides `fetch.prune` settings, if any. + +remote.<name>.pruneTags:: + When set to true, fetching from this remote by default will also + remove any local tags that no longer exist on the remote (as + if the `--prune` option was given on the command line in + conjunction with the `refs/tags/*:refs/tags/*` refspec). + Overrides `fetch.prune` settings, if any. + See also `remote.<name>.prune` and the PRUNING section of linkgit:git-fetch[1]. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 9f5c85ad96..dc13bed741 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -73,7 +73,20 @@ ifndef::git-pull[] are fetched due to an explicit refspec (either on the command line or in the remote configuration, for example if the remote was cloned with the --mirror option), then they are also - subject to pruning. + subject to pruning. Supplying `--prune-tags` is a shorthand for + providing the tag refspec. ++ +See the PRUNING section below for more details. + +-P:: +--prune-tags:: + Before fetching, remove any local tags that no longer exist on + the remote if `--prune` is enabled. This option should be used + more carefully, unlike `--prune` it will remove any local + references (local tags) that have been created. This option is + merely a shorthand for providing the explicit tag refspec + along with `--prune`, see the discussion about that in its + documentation. + See the PRUNING section below for more details. diff --git a/builtin/fetch.c b/builtin/fetch.c index b34665db9e..57669a000c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -38,6 +38,10 @@ static int fetch_prune_config = -1; /* unspecified */ 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; @@ -64,6 +68,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb) 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; @@ -126,6 +135,8 @@ static struct option builtin_fetch_options[] = { 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 not found on remote")), { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules }, @@ -1212,6 +1223,8 @@ static void add_options_to_argv(struct argv_array *argv) 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) @@ -1270,6 +1283,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) static const char **refs = NULL; struct refspec *refspec; int ref_nr = 0; + int j = 0; int exit_code; if (!remote) @@ -1288,10 +1302,27 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) prune = PRUNE_BY_DEFAULT; } + if (prune_tags < 0) { + /* no command line request */ + if (0 <= gtransport->remote->prune_tags) + prune_tags = gtransport->remote->prune_tags; + else if (0 <= fetch_prune_tags_config) + prune_tags = fetch_prune_tags_config; + else + prune_tags = PRUNE_TAGS_BY_DEFAULT; + } + + if (argc > 0 || prune_tags) { + size_t nr_alloc = st_add3(argc, prune_tags, 1); + refs = xcalloc(nr_alloc, sizeof(const char *)); + if (prune_tags) { + refs[j++] = xstrfmt("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++; @@ -1301,8 +1332,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) argv[i], argv[i]); } else refs[j++] = argv[i]; + ref_nr++; } - ref_nr = j; } sigchain_push_common(unlock_pack_on_signal); diff --git a/remote.c b/remote.c index 4e93753e19..9eb6d24009 100644 --- a/remote.c +++ b/remote.c @@ -391,6 +391,8 @@ static int handle_config(const char *key, const char *value, void *cb) remote->skip_default_update = git_config_bool(key, value); else if (!strcmp(subkey, "prune")) remote->prune = git_config_bool(key, value); + else if (!strcmp(subkey, "prunetags")) + remote->prune_tags = git_config_bool(key, value); else if (!strcmp(subkey, "url")) { const char *v; if (git_config_string(&v, key, value)) diff --git a/remote.h b/remote.h index 1f6611be21..04d7994cbf 100644 --- a/remote.h +++ b/remote.h @@ -47,6 +47,7 @@ struct remote { int skip_default_update; int mirror; int prune; + int prune_tags; const char *receivepack; const char *uploadpack; diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 18280df4fc..1a822861e0 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -617,6 +617,7 @@ test_configured_prune () { 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 pruned pruned "--prune --prune-tags" # WIP: broken, see commit msg test_configured_prune unset unset unset unset kept pruned \ "--prune origin 'refs/tags/*:refs/tags/*'" test_configured_prune unset unset unset unset pruned pruned \ -- 2.15.1.424.g9478a66081