From aa59e0eaf65b1e535248f67822053e3b2f5fcc26 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:00 +0000 Subject: fetch: don't redundantly NULL something calloc() gave us MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop redundantly NULL-ing the last element of the refs structure, which was retrieved via calloc(), and is thus guaranteed to be pre-NULL'd. This code dates back to b888d61c83 ("Make fetch a builtin", 2007-09-10), where wasn't any reason to do this back then either, it's just boilerplate left over from when git-fetch was initially introduced. The motivation for this change was to make a subsequent change which would also modify the refs variable smaller, since it won't have to copy this redundant "NULL the last + 1 item" pattern. We may not end up keeping that change, but as this pattern is still pointless, so let's fix it. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/fetch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 7bbcd26faf..b34665db9e 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) } else refs[j++] = argv[i]; } - refs[j] = NULL; ref_nr = j; } -- cgit v1.3-5-g9baa From ce3ab21b0c179559621fb7450a6f8c24e0a28ddb Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:01 +0000 Subject: fetch: trivially refactor assignment to ref_nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivially refactor an assignment to make a subsequent patch smaller. The "ref_nr" variable is initialized to 0 earlier, just as "j" is, and "j" is only incremented in that loop, so this change isn't a logic error. This change simplifies a subsequent change, which will split the incrementing of "ref_nr" into two blocks. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index b34665db9e..72085e30b9 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1301,8 +1301,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); -- cgit v1.3-5-g9baa From 0711883218039750f940d8febf05a81aa46d2ab9 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:02 +0000 Subject: fetch: stop accessing "remote" variable indirectly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Access the "remote" variable passed to the fetch_one() directly rather than through the gtransport wrapper struct constructed in this function for other purposes. This makes the code more readable, as it's now obvious that the remote struct doesn't somehow get munged by the prepare_transport() function above, which takes the "remote" struct as an argument and constructs the "gtransport" struct, containing among other things the "remote" struct. A subsequent change will copy this pattern to access a new remote->prune_tags field, but without the use of the gtransport variable. It's useful once that change lands to see that the two pieces of code behave exactly the same. This pattern of accessing the container struct was added in 737c5a9cde ("fetch: make --prune configurable", 2013-07-13) when this code was initially introduced. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/fetch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 72085e30b9..a7705bc150 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1280,8 +1280,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) 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 -- cgit v1.3-5-g9baa From 750d0da9cfbc2519f1ef478d50de0ce549c41f05 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:03 +0000 Subject: remote: add a macro for "refs/tags/*:refs/tags/*" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a macro with the refspec string "refs/tags/*:refs/tags/*". There's been a pre-defined struct version of this since e0aaa29ff3 ("Have a constant extern refspec for "--tags"", 2008-04-17), but nothing that could be passed to e.g. add_fetch_refspec(). This will be used in subsequent commits to avoid hardcoding this string in multiple places. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- remote.c | 1 + remote.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/remote.c b/remote.c index 4e93753e19..356c123e3e 100644 --- a/remote.c +++ b/remote.c @@ -22,6 +22,7 @@ static struct refspec s_tag_refspec = { "refs/tags/*" }; +/* See TAG_REFSPEC for the string version */ const struct refspec *tag_refspec = &s_tag_refspec; struct counted_string { diff --git a/remote.h b/remote.h index 1f6611be21..80fea6dd11 100644 --- a/remote.h +++ b/remote.h @@ -297,4 +297,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int extern int is_empty_cas(const struct push_cas_option *); void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *); +#define TAG_REFSPEC "refs/tags/*:refs/tags/*" + #endif -- cgit v1.3-5-g9baa From eca142d30818bd9cfc0d3a6b38d6ae4dc4c500a0 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:04 +0000 Subject: fetch tests: refactor in preparation for testing tag pruning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a subsequent commit this function will learn to test for tag pruning, prepare for that by making space for more variables, and making it clear that "expected" here refers to branches. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 668c54be41..11da97f9b7 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -549,9 +549,12 @@ set_config_tristate () { } test_configured_prune () { - fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4 + fetch_prune=$1 + remote_origin_prune=$2 + cmdline=$3 + expected_branch=$4 - test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" ' + test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4" ' # make sure a newbranch is there in . and also in one git branch -f newbranch && ( @@ -572,7 +575,7 @@ test_configured_prune () { set_config_tristate remote.origin.prune $remote_origin_prune && git fetch $cmdline && - case "$expected" in + case "$expected_branch" in pruned) test_must_fail git rev-parse --verify refs/remotes/origin/newbranch ;; -- cgit v1.3-5-g9baa From bf16ab795574c27ffa0fe180cede2d355de41619 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:05 +0000 Subject: fetch tests: re-arrange arguments for future readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-arrange the arguments to the test_configured_prune() function used in this test to pass the arguments to --fetch last. A subsequent change will test for more elaborate fetch arguments, including long refspecs. It'll be more readable to be able to wrap those on a new line of their own. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 82 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 11da97f9b7..ab8b25344d 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -551,10 +551,10 @@ set_config_tristate () { test_configured_prune () { fetch_prune=$1 remote_origin_prune=$2 - cmdline=$3 - expected_branch=$4 + expected_branch=$3 + cmdline=$4 - test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4" ' + test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3" ' # make sure a newbranch is there in . and also in one git branch -f newbranch && ( @@ -587,41 +587,47 @@ test_configured_prune () { ' } -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 expect: branch to be pruned? +# $4 git-fetch $cmdline: +# +# $1 $2 $3 $4 +test_configured_prune unset unset kept "" +test_configured_prune unset unset kept "--no-prune" +test_configured_prune unset unset pruned "--prune" + +test_configured_prune false unset kept "" +test_configured_prune false unset kept "--no-prune" +test_configured_prune false unset pruned "--prune" + +test_configured_prune true unset pruned "" +test_configured_prune true unset pruned "--prune" +test_configured_prune true unset kept "--no-prune" + +test_configured_prune unset false kept "" +test_configured_prune unset false kept "--no-prune" +test_configured_prune unset false pruned "--prune" + +test_configured_prune false false kept "" +test_configured_prune false false kept "--no-prune" +test_configured_prune false false pruned "--prune" + +test_configured_prune true false kept "" +test_configured_prune true false pruned "--prune" +test_configured_prune true false kept "--no-prune" + +test_configured_prune unset true pruned "" +test_configured_prune unset true kept "--no-prune" +test_configured_prune unset true pruned "--prune" + +test_configured_prune false true pruned "" +test_configured_prune false true kept "--no-prune" +test_configured_prune false true pruned "--prune" + +test_configured_prune true true pruned "" +test_configured_prune true true pruned "--prune" +test_configured_prune true true kept "--no-prune" test_expect_success 'all boundary commits are excluded' ' test_commit base && -- cgit v1.3-5-g9baa From ca3065e7e7bcb406043f8202ef672f884eeb925d Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:06 +0000 Subject: fetch tests: add a tag to be deleted to the pruning tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a tag to be deleted to the fetch --prune tests. The tag is always kept for now, which is the expected behavior, but now I can add a test for tag pruning in a later commit. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 93 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index ab8b25344d..fad65bd885 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -552,21 +552,25 @@ test_configured_prune () { fetch_prune=$1 remote_origin_prune=$2 expected_branch=$3 - cmdline=$4 + expected_tag=$4 + cmdline=$5 - test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3" ' + test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4" ' # 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 remote.origin.prune && git fetch && - git rev-parse --verify refs/remotes/origin/newbranch + 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 ( @@ -582,6 +586,14 @@ test_configured_prune () { 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 ) ' @@ -590,44 +602,45 @@ test_configured_prune () { # $1 config: fetch.prune # $2 config: remote..prune # $3 expect: branch to be pruned? -# $4 git-fetch $cmdline: +# $4 expect: tag to be pruned? +# $5 git-fetch $cmdline: # -# $1 $2 $3 $4 -test_configured_prune unset unset kept "" -test_configured_prune unset unset kept "--no-prune" -test_configured_prune unset unset pruned "--prune" - -test_configured_prune false unset kept "" -test_configured_prune false unset kept "--no-prune" -test_configured_prune false unset pruned "--prune" - -test_configured_prune true unset pruned "" -test_configured_prune true unset pruned "--prune" -test_configured_prune true unset kept "--no-prune" - -test_configured_prune unset false kept "" -test_configured_prune unset false kept "--no-prune" -test_configured_prune unset false pruned "--prune" - -test_configured_prune false false kept "" -test_configured_prune false false kept "--no-prune" -test_configured_prune false false pruned "--prune" - -test_configured_prune true false kept "" -test_configured_prune true false pruned "--prune" -test_configured_prune true false kept "--no-prune" - -test_configured_prune unset true pruned "" -test_configured_prune unset true kept "--no-prune" -test_configured_prune unset true pruned "--prune" - -test_configured_prune false true pruned "" -test_configured_prune false true kept "--no-prune" -test_configured_prune false true pruned "--prune" - -test_configured_prune true true pruned "" -test_configured_prune true true pruned "--prune" -test_configured_prune true true kept "--no-prune" +# $1 $2 $3 $4 $5 +test_configured_prune unset unset kept kept "" +test_configured_prune unset unset kept kept "--no-prune" +test_configured_prune unset unset pruned kept "--prune" + +test_configured_prune false unset kept kept "" +test_configured_prune false unset kept kept "--no-prune" +test_configured_prune false unset pruned kept "--prune" + +test_configured_prune true unset pruned kept "" +test_configured_prune true unset pruned kept "--prune" +test_configured_prune true unset kept kept "--no-prune" + +test_configured_prune unset false kept kept "" +test_configured_prune unset false kept kept "--no-prune" +test_configured_prune unset false pruned kept "--prune" + +test_configured_prune false false kept kept "" +test_configured_prune false false kept kept "--no-prune" +test_configured_prune false false pruned kept "--prune" + +test_configured_prune true false kept kept "" +test_configured_prune true false pruned kept "--prune" +test_configured_prune true false kept kept "--no-prune" + +test_configured_prune unset true pruned kept "" +test_configured_prune unset true kept kept "--no-prune" +test_configured_prune unset true pruned kept "--prune" + +test_configured_prune false true pruned kept "" +test_configured_prune false true kept kept "--no-prune" +test_configured_prune false true pruned kept "--prune" + +test_configured_prune true true pruned kept "" +test_configured_prune true true pruned kept "--prune" +test_configured_prune true true kept kept "--no-prune" test_expect_success 'all boundary commits are excluded' ' test_commit base && -- cgit v1.3-5-g9baa From 6fb23f56c16c0981a2f7b46f53ac3af12b247996 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:07 +0000 Subject: fetch tests: test --prune and refspec interaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test for the interaction between explicitly provided refspecs and fetch.prune. There's no point in adding this boilerplate to every combination of unset/false/true, it's instructive and sufficient to show that no matter if the variable is unset, false or true the refspec on the command-line overrides any configuration variable. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index fad65bd885..dacdb8759c 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -609,6 +609,10 @@ test_configured_prune () { test_configured_prune unset unset kept kept "" test_configured_prune unset unset kept kept "--no-prune" test_configured_prune unset unset pruned kept "--prune" +test_configured_prune unset unset kept pruned \ + "--prune origin refs/tags/*:refs/tags/*" +test_configured_prune unset unset pruned pruned \ + "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" test_configured_prune false unset kept kept "" test_configured_prune false unset kept kept "--no-prune" @@ -625,6 +629,10 @@ test_configured_prune unset false pruned kept "--prune" test_configured_prune false false kept kept "" test_configured_prune false false kept kept "--no-prune" test_configured_prune false false pruned kept "--prune" +test_configured_prune false false kept pruned \ + "--prune origin refs/tags/*:refs/tags/*" +test_configured_prune false false pruned pruned \ + "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" test_configured_prune true false kept kept "" test_configured_prune true false pruned kept "--prune" @@ -641,6 +649,10 @@ test_configured_prune false true pruned kept "--prune" test_configured_prune true true pruned kept "" test_configured_prune true true pruned kept "--prune" test_configured_prune true true kept kept "--no-prune" +test_configured_prune true true kept pruned \ + "--prune origin refs/tags/*:refs/tags/*" +test_configured_prune true true pruned pruned \ + "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" test_expect_success 'all boundary commits are excluded' ' test_commit base && -- cgit v1.3-5-g9baa From 82f34e03e91af42a020f15a1d248bc75785c565a Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:08 +0000 Subject: fetch tests: double quote a variable for interpolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the $cmdline variable contains arguments with spaces they won't be interpolated correctly, since the body of the test is single quoted, and because test-lib.sh does its own eval(). This will be used in a subsequent commit to pass arguments that need to be quoted to git-fetch, i.e. a file:// path to fetch, which will have a space in it. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index dacdb8759c..88d38e0819 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -578,7 +578,7 @@ test_configured_prune () { set_config_tristate fetch.prune $fetch_prune && set_config_tristate remote.origin.prune $remote_origin_prune && - git fetch $cmdline && + git fetch '"$cmdline"' && case "$expected_branch" in pruned) test_must_fail git rev-parse --verify refs/remotes/origin/newbranch -- cgit v1.3-5-g9baa From 59caf52d094494a8c02dd7f15302891678533182 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:09 +0000 Subject: fetch tests: expand case/esac for later change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand a compact case/esac statement for a later change that'll add more logic to the body of the "*" case. This is a whitespace-only change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 88d38e0819..dfc749f576 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -543,8 +543,12 @@ test_expect_success "should be able to fetch with duplicate refspecs" ' 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" + ;; esac } -- cgit v1.3-5-g9baa From e1790f9245f120940be9333b09015e04a8e73127 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:10 +0000 Subject: fetch tests: fetch as well as fetch [] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a remote URL is supplied on the command-line the internals of the fetch are different, in particular the code in get_ref_map(). An earlier version of the subsequent fetch.pruneTags patch hid a segfault because the difference wasn't tested for. Now all the tests are run as both of the variants of: git fetch git -c [...] fetch $(git config remote.origin.url) $(git config remote.origin.fetch) I'm using -c because while the [fetch] config just set by set_config_tristate will be picked up, the remote.origin.* config won't override it as intended. Work around that and turn this into a purely command-line test by always setting the variables on the command-line, and translate any setting of remote.origin.X into fetch.X. The reason for choosing the names "name" and "link" as opposed to e.g. "named" and "url" is because they're the same length, which makes the test output easier to read as it will be aligned. Due to shellscript quoting madness it's not worthwhile to do all of this within a test_expect_success, but do the parts that can easily be done there, including the one-time setting of variables that don't change between runs to be used by subsequent runs in the 'prune_type setup' test. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index dfc749f576..9c87fa6106 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -548,18 +548,49 @@ set_config_tristate () { ;; *) 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 () { + test_configured_prune_type "$@" "name" + test_configured_prune_type "$@" "link" +} + +test_configured_prune_type () { fetch_prune=$1 remote_origin_prune=$2 expected_branch=$3 expected_tag=$4 cmdline=$5 - - test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4" ' + mode=$6 + + 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 + + cmdline="$new_cmdline" + fi + + test_expect_success "$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4" ' # make sure a newbranch is there in . and also in one git branch -f newbranch && git tag -f newtag && @@ -567,7 +598,7 @@ test_configured_prune () { cd one && test_unconfig fetch.prune && test_unconfig remote.origin.prune && - git fetch && + git fetch '"$cmdline_setup"' && git rev-parse --verify refs/remotes/origin/newbranch && git rev-parse --verify refs/tags/newtag ) && @@ -579,10 +610,15 @@ test_configured_prune () { # then test ( cd one && + git_fetch_c="" && set_config_tristate fetch.prune $fetch_prune && set_config_tristate remote.origin.prune $remote_origin_prune && - git fetch '"$cmdline"' && + 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 -- cgit v1.3-5-g9baa From 2c72ed740f302bf51e6cd0824b8e7cbde1b1e8c2 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:11 +0000 Subject: git fetch doc: add a new section to explain the ins & outs of pruning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new section to canonically explain how remote reference pruning works, and how users should be careful about using it in conjunction with tag refspecs in particular. A subsequent commit will update the git-remote documentation to refer to this section, and details the motivation for writing this in the first place. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/git-fetch.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index b153aefa68..e94bcfb8c3 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -99,6 +99,55 @@ The latter use of the `remote..fetch` values can be overridden by giving the `--refmap=` parameter(s) on the command line. +PRUNING +------- + +Git has a default disposition of keeping data unless it's explicitly +thrown away; this extends to holding onto local references to branches +on remotes that have themselves deleted those branches. + +If left to accumulate, these stale references might make performance +worse on big and busy repos that have a lot of branch churn, and +e.g. make the output of commands like `git branch -a --contains +` needlessly verbose, as well as impacting anything else +that'll work with the complete set of known references. + +These remote-tracking references can be deleted as a one-off with +either of: + +------------------------------------------------ +# While fetching +$ git fetch --prune + +# Only prune, don't fetch +$ git remote prune +------------------------------------------------ + +To prune references as part of your normal workflow without needing to +remember to run that, set `fetch.prune` globally, or +`remote..prune` per-remote in the config. See +linkgit:git-config[1]. + +Here's where things get tricky and more specific. The pruning feature +doesn't actually care about branches, instead it'll prune local <-> +remote-references as a function of the refspec of the remote (see +`` and <> above). + +Therefore if the refspec for the remote includes +e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch +--prune "refs/tags/*:refs/tags/*"` it won't be stale remote +tracking branches that are deleted, but any local tag that doesn't +exist on the remote. + +This might not be what you expect, i.e. you want to prune remote +``, but also explicitly fetch tags from it, so when you fetch +from it you delete all your local tags, most of which may not have +come from the `` remote in the first place. + +So be careful when using this with a refspec like +`refs/tags/*:refs/tags/*`, or any other refspec which might map +references from multiple remotes to the same local namespace. + OUTPUT ------ -- cgit v1.3-5-g9baa From d0e07472faa938c83e9b1f76567f16614672668c Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:12 +0000 Subject: git remote doc: correct dangerous lies about what prune does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "git remote prune " command uses the same machinery as "git fetch --prune", and shares all the same caveats, but its documentation has suggested that it'll just "delete stale remote-tracking branches under ". This isn't true, and hasn't been true since at least v1.8.5.6 (the oldest version I could be bothered to test). E.g. if "refs/tags/*:refs/tags/*" is explicitly set in the refspec of the remote, it'll delete all local tags doesn't know about. Instead, briefly give the reader just enough of a hint that this option might constitute a shotgun aimed at their foot, and point them to the new PRUNING section in the git-fetch documentation which explains all the nuances of what this facility does. See "[BUG] git remote prune removes local tags, depending on fetch config" (CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com) by Michael Giuffrida for the initial report. Reported-by: Michael Giuffrida Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 577b969c1b..4feddc0293 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with 'prune':: -Deletes all stale remote-tracking branches under . -These stale branches have already been removed from the remote repository -referenced by , but are still locally available in -"remotes/". +Deletes stale references associated with . By default, stale +remote-tracking branches under are deleted, but depending on +global configuration and the configuration of the remote we might even +prune local tags that haven't been pushed there. Equivalent to `git +fetch --prune `, except that no new references will be fetched. ++ +See the PRUNING section of linkgit:git-fetch[1] for what it'll prune +depending on various configuration. + With `--dry-run` option, report what branches will be pruned, but do not actually prune them. @@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the configuration parameter remote..skipDefaultUpdate set to true will be updated. (See linkgit:git-config[1]). + -With `--prune` option, prune all the remotes that are updated. +With `--prune` option, run pruning against all the remotes that are updated. DISCUSSION -- cgit v1.3-5-g9baa From 627a129b46530773369586f7982925fd41dc7227 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:13 +0000 Subject: git-fetch & config doc: link to the new PRUNING section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend the documentation for fetch.prune, fetch..prune and --prune to link to the recently added PRUNING section. I'd have liked to link directly to it with "<>" from fetch-options.txt, since it's included in git-fetch.txt (git-pull.txt also includes it, but doesn't include that option). However making a reference across files yields this error: [...]/Documentation/git-fetch.xml:226: element xref: validity error : IDREF attribute linkend references an unknown ID "PRUNING" Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 +++++- Documentation/fetch-options.txt | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 0e25b2c92b..0f27af5760 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1398,7 +1398,8 @@ 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.output:: Control how ref update status is printed. Valid values are @@ -2944,6 +2945,9 @@ remote..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. ++ +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 diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index fb6bebbc61..9f5c85ad96 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -74,6 +74,9 @@ ifndef::git-pull[] line or in the remote configuration, for example if the remote was cloned with the --mirror option), then they are also subject to pruning. ++ +See the PRUNING section below for more details. + endif::git-pull[] ifndef::git-pull[] -- cgit v1.3-5-g9baa From e249ce0ccdb5c57f45a88daa25d24c5b602ee6e9 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:14 +0000 Subject: fetch tests: add scaffolding for the new fetch.pruneTags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fetch.pruneTags configuration doesn't exist yet, but will be added in a subsequent commit. Since testing for it requires adding new parameters to the test_configured_prune function it's easier to review this patch first to assert that no functional changes are introduced yet. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 92 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 9c87fa6106..1713111006 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -562,10 +562,12 @@ test_configured_prune () { test_configured_prune_type () { fetch_prune=$1 remote_origin_prune=$2 - expected_branch=$3 - expected_tag=$4 - cmdline=$5 - mode=$6 + 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 @@ -590,14 +592,16 @@ test_configured_prune_type () { cmdline="$new_cmdline" fi - test_expect_success "$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4" ' + 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 && + test_unconfig remote.origin.pruneTags && git fetch '"$cmdline_setup"' && git rev-parse --verify refs/remotes/origin/newbranch && git rev-parse --verify refs/tags/newtag @@ -612,7 +616,9 @@ test_configured_prune_type () { 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 && + set_config_tristate remote.origin.pruneTags $remote_origin_prune_tags && if test "$mode" != "link" then @@ -641,57 +647,59 @@ test_configured_prune_type () { # $1 config: fetch.prune # $2 config: remote..prune -# $3 expect: branch to be pruned? -# $4 expect: tag to be pruned? -# $5 git-fetch $cmdline: +# $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 -test_configured_prune unset unset kept kept "" -test_configured_prune unset unset kept kept "--no-prune" -test_configured_prune unset unset pruned kept "--prune" -test_configured_prune unset unset kept pruned \ +# $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 pruned pruned \ +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 kept kept "" -test_configured_prune false unset kept kept "--no-prune" -test_configured_prune false unset pruned kept "--prune" +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 pruned kept "" -test_configured_prune true unset pruned kept "--prune" -test_configured_prune true unset kept kept "--no-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 kept kept "" -test_configured_prune unset false kept kept "--no-prune" -test_configured_prune unset false pruned kept "--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 kept kept "" -test_configured_prune false false kept kept "--no-prune" -test_configured_prune false false pruned kept "--prune" -test_configured_prune false false kept pruned \ +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 pruned pruned \ +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 kept kept "" -test_configured_prune true false pruned kept "--prune" -test_configured_prune true false kept kept "--no-prune" +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 pruned kept "" -test_configured_prune unset true kept kept "--no-prune" -test_configured_prune unset true pruned kept "--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 pruned kept "" -test_configured_prune false true kept kept "--no-prune" -test_configured_prune false true 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 pruned kept "" -test_configured_prune true true pruned kept "--prune" -test_configured_prune true true kept kept "--no-prune" -test_configured_prune true true kept pruned \ +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 pruned pruned \ +test_configured_prune true true unset unset pruned pruned \ "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*" test_expect_success 'all boundary commits are excluded' ' -- cgit v1.3-5-g9baa From 97716d217c1ea00adfc64e4f6bb85c1236d661ff Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:15 +0000 Subject: fetch: add a --prune-tags option and fetch.pruneTags config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a --prune-tags option to git-fetch, along with fetch.pruneTags config option and a -P shorthand (-p is --prune). This allows for doing any of: git fetch -p -P git fetch --prune --prune-tags git fetch -p -P origin git fetch --prune --prune-tags origin Or simply: git config fetch.prune true && git config fetch.pruneTags true && git fetch Instead of the much more verbose: git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*' Before this feature it was painful to support the use-case of pulling from a repo which is having both its branches *and* tags deleted regularly, and have our local references to reflect upstream. 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 I want. Even though "git remote" has corresponding "prune" and "update --prune" subcommands I'm intentionally not adding a corresponding prune-tags or "update --prune --prune-tags" mode to that command. It's advertised (as noted in my recent "git remote doc: correct dangerous lies about what prune does") as only modifying remote tracking references, whereas any --prune-tags option is always going to modify what from the user's perspective is a local copy of the tag, since there's no such thing as a remote tracking tag. Ideally add_prune_tags_to_fetch_refspec() would be something that would use ALLOC_GROW() to grow the 'fetch` member of the 'remote' struct. Instead I'm realloc-ing remote->fetch and adding the tag_refspec to the end. The reason is that parse_{fetch,push}_refspec which allocate the refspec (ultimately remote->fetch) struct are called many places that don't have access to a 'remote' struct. It would be hard to change all their callsites to be amenable to carry around the bookkeeping variables required for dynamic allocation. All the other callers of the API first incrementally construct the string version of the refspec in remote->fetch_refspec via add_fetch_refspec(), before finally calling parse_fetch_refspec() via some variation of remote_get(). It's less of a pain to deal with the one special case that needs to modify already constructed refspecs than to chase down and change all the other callsites. The API I'm adding is intentionally not generalized because if we add more of these we'd probably want to re-visit how this is done. See my "Re: [BUG] git remote prune removes local tags, depending on fetch config" (87po6ahx87.fsf@evledraar.gmail.com; https://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for more background info. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 14 +++++++ Documentation/fetch-options.txt | 14 ++++++- Documentation/git-fetch.txt | 47 +++++++++++++++++++++++ builtin/fetch.c | 32 ++++++++++++++-- contrib/completion/git-completion.bash | 2 +- remote.c | 14 +++++++ remote.h | 3 ++ t/t5510-fetch.sh | 70 ++++++++++++++++++++++++++++++++++ 8 files changed, 191 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 0f27af5760..e254bfd531 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..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 `full` and `compact`. Default value is `full`. See section @@ -2945,6 +2953,12 @@ remote..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..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]. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 9f5c85ad96..8631e365f4 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -73,7 +73,19 @@ 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 + 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/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index e94bcfb8c3..af12310f75 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -148,6 +148,53 @@ So be careful when using this with a refspec like `refs/tags/*:refs/tags/*`, or any other refspec which might map references from multiple remotes to the same local namespace. +Since keeping up-to-date with both branches and tags on the remote is +a common use-case the `--prune-tags` option can be supplied along with +`--prune` to prune local tags that don't exist on the remote, and +force-update those tags that differ. Tag pruning can also be enabled +with `fetch.pruneTags` or `remote..pruneTags` in the config. See +linkgit:git-config[1]. + +The `--prune-tags` option is equivalent to having +`refs/tags/*:refs/tags/*` declared in the refspecs of the remote. This +can lead to some seemingly strange interactions: + +------------------------------------------------ +# These both fetch tags +$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*' +$ git fetch --no-tags --prune-tags origin +------------------------------------------------ + +The reason it doesn't error out when provided without `--prune` or its +config versions is for flexibility of the configured versions, and to +maintain a 1=1 mapping between what the command line flags do, and +what the configuration versions do. + +It's reasonable to e.g. configure `fetch.pruneTags=true` in +`~/.gitconfig` to have tags pruned whenever `git fetch --prune` is +run, without making every invocation of `git fetch` without `--prune` +an error. + +Another special case of `--prune-tags` is that +`refs/tags/*:refs/tags/*` will not be implicitly provided if an URL is +being fetched. I.e.: + +------------------------------------------------ +$ git fetch --prune --prune-tags +------------------------------------------------ + +Will prune no tags, as opposed to: + +------------------------------------------------ +$ git fetch origin --prune --prune-tags +------------------------------------------------ + +To prune tags given a URL supply the refspec explicitly: + +------------------------------------------------ +$ git fetch --prune 'refs/tags/*:refs/tags/*' +------------------------------------------------ + OUTPUT ------ diff --git a/builtin/fetch.c b/builtin/fetch.c index a7705bc150..55a0fc37be 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 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 }, @@ -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) @@ -1265,7 +1278,7 @@ static int fetch_multiple(struct string_list *list) return result; } -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; @@ -1288,6 +1301,19 @@ 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 <= 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; + } + + if (prune_tags_ok && prune_tags && remote_is_configured(remote, 0)) + add_prune_tags_to_fetch_refspec(remote); + if (argc > 0) { int j = 0; int i; @@ -1368,7 +1394,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } else if (argc == 0) { /* No arguments -- use default remote */ remote = remote_get(NULL); - result = fetch_one(remote, argc, argv); + 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++) @@ -1386,7 +1412,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } else { /* Zero or one remotes */ remote = remote_get(argv[0]); - result = fetch_one(remote, argc-1, argv+1); + result = fetch_one(remote, argc-1, argv+1, argc == 1); } } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3683c772c5..4ecd0d4d7a 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1468,7 +1468,7 @@ __git_fetch_recurse_submodules="yes on-demand no" __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 --git a/remote.c b/remote.c index 356c123e3e..d670eed4c4 100644 --- a/remote.c +++ b/remote.c @@ -104,6 +104,17 @@ static void add_fetch_refspec(struct remote *remote, const char *ref) remote->fetch_refspec[remote->fetch_refspec_nr++] = ref; } +void add_prune_tags_to_fetch_refspec(struct remote *remote) +{ + int nr = remote->fetch_refspec_nr; + int bufsize = nr + 1; + int size = sizeof(struct refspec); + + remote->fetch = xrealloc(remote->fetch, size * bufsize); + memcpy(&remote->fetch[nr], tag_refspec, size); + add_fetch_refspec(remote, xstrdup(TAG_REFSPEC)); +} + static void add_url(struct remote *remote, const char *url) { ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc); @@ -174,6 +185,7 @@ static struct remote *make_remote(const char *name, int len) ret = xcalloc(1, sizeof(struct remote)); ret->prune = -1; /* unspecified */ + ret->prune_tags = -1; /* unspecified */ ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); remotes[remotes_nr++] = ret; ret->name = xstrndup(name, len); @@ -392,6 +404,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 80fea6dd11..271afe1bab 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; @@ -299,4 +300,6 @@ void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *); #define TAG_REFSPEC "refs/tags/*:refs/tags/*" +void add_prune_tags_to_fetch_refspec(struct remote *remote); + #endif diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 1713111006..227dd70b7b 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -589,6 +589,15 @@ test_configured_prune_type () { 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 @@ -702,6 +711,67 @@ test_configured_prune true true unset unset kept pruned \ 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, with the exception of --prune-tags on the command-line +# (arbitrary limitation). +# +# 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 kept "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 kept "--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" + +# Interaction between --prune-tags and no "fetch" config in the remote +# at all. +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 kept "origin --prune --prune-tags" "link" + test_expect_success 'all boundary commits are excluded' ' test_commit base && test_commit oneside && -- cgit v1.3-5-g9baa From 6317972cff9b4df7a6cc666b08be7133ba81617c Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 9 Feb 2018 20:32:16 +0000 Subject: fetch: make the --prune-tags work with MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the new --prune-tags option work properly when git-fetch is invoked with a parameter instead of a parameter. This change is split off from the introduction of --prune-tags due to the relative complexity of munging the incoming argv, which is easier to review as a separate change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/git-fetch.txt | 21 ++++++--------------- builtin/fetch.c | 17 ++++++++++++++--- t/t5510-fetch.sh | 16 +++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index af12310f75..e319935597 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -175,24 +175,15 @@ It's reasonable to e.g. configure `fetch.pruneTags=true` in run, without making every invocation of `git fetch` without `--prune` an error. -Another special case of `--prune-tags` is that -`refs/tags/*:refs/tags/*` will not be implicitly provided if an URL is -being fetched. I.e.: - ------------------------------------------------- -$ git fetch --prune --prune-tags ------------------------------------------------- - -Will prune no tags, as opposed to: +Pruning tags with `--prune-tags` also works when fetching a URL +instead of a named remote. These will all prune tags not found on +origin: ------------------------------------------------ $ git fetch origin --prune --prune-tags ------------------------------------------------- - -To prune tags given a URL supply the refspec explicitly: - ------------------------------------------------- -$ git fetch --prune 'refs/tags/*:refs/tags/*' +$ git fetch origin --prune 'refs/tags/*:refs/tags/*' +$ git fetch --prune --prune-tags +$ git fetch --prune 'refs/tags/*:refs/tags/*' ------------------------------------------------ OUTPUT diff --git a/builtin/fetch.c b/builtin/fetch.c index 55a0fc37be..c96f17a9a3 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1283,7 +1283,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru 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" @@ -1311,13 +1314,21 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru prune_tags = PRUNE_TAGS_BY_DEFAULT; } - if (prune_tags_ok && prune_tags && remote_is_configured(remote, 0)) + 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++; diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 227dd70b7b..dce2371302 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -738,18 +738,15 @@ 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, with the exception of --prune-tags on the command-line -# (arbitrary limitation). -# -# However, because there's no implicit +# 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 kept "origin --prune --prune-tags" "link" +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 kept "--prune --prune-tags origin" "link" +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" @@ -761,8 +758,9 @@ 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" -# Interaction between --prune-tags and no "fetch" config in the remote -# at all. +# 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 && @@ -770,7 +768,7 @@ test_expect_success 'remove remote.origin.fetch "one"' ' ) ' test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "name" -test_configured_prune_type unset unset unset unset kept kept "origin --prune --prune-tags" "link" +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 && -- cgit v1.3-5-g9baa