]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs: support negative transfer.hideRefs
authorJeff King <peff@peff.net>
Tue, 28 Jul 2015 20:23:26 +0000 (16:23 -0400)
committerJunio C Hamano <gitster@pobox.com>
Fri, 7 Aug 2015 18:47:36 +0000 (11:47 -0700)
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).

So you can now do:

  $ git config --system transfer.hideRefs refs/secret
  $ git config transfer.hideRefs '!refs/secret/not-so-secret'

to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:

  $ git clone \
      -u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
      remote:repo.git

to clone remote:repo.git, overriding any hiding it has
configured.

There are two alternatives that were considered and
rejected:

  1. A generic config mechanism for removing an item from a
     list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").

     This is nice because it could apply to other
     multi-valued config, as well. But it is not nearly as
     flexible. There is no way to say:

       [transfer]
       hideRefs = refs/secret
       hideRefs = refs/secret/not-so-secret

     Having explicit negative specifications means we can
     override previous entries, even if they are not the
     same literal string.

  2. Adding another variable to override some parts of
     hideRefs (e.g., "exposeRefs").

     This solves the problem from alternative (1), but it
     cannot easily obey the normal config precedence,
     because it would use two separate lists. For example:

       [transfer]
       hideRefs = refs/secret
       exposeRefs = refs/secret/not-so-secret
       hideRefs = refs/secret/not-so-secret/no-really-its-secret

     With two lists, we have to apply the "expose" rules
     first, and only then apply the "hide" rules. But that
     does not match what the above config intends.

     Of course we could internally parse that to a single
     list, respecting the ordering, which saves us having to
     invent the new "!" syntax. But using a single name
     communicates to the user that the ordering _is_
     important. And "!" is well-known for negation, and
     should not appear at the beginning of a ref (it is
     actually valid in a ref-name, but all entries here
     should be fully-qualified, starting with "refs/").

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config.txt
refs.c
t/t5512-ls-remote.sh

index 448eb9d30284ccbf3b1bf383b4d7e4a077fa5536..a7fbd0aac2933cc0fc8f5f71aca57b35cc5b93b1 100644 (file)
@@ -2540,6 +2540,11 @@ transfer.hideRefs::
        excluded, and is hidden when responding to `git push` or `git
        fetch`.  See `receive.hideRefs` and `uploadpack.hideRefs` for
        program-specific versions of this config.
++
+You may also include a `!` in front of the ref name to negate the entry,
+explicitly exposing it, even if an earlier entry marked it as hidden.
+If you have multiple hideRefs values, later entries override earlier ones
+(and entries in more-specific config files override less-specific ones).
 
 transfer.unpackLimit::
        When `fetch.unpackLimit` or `receive.unpackLimit` are
diff --git a/refs.c b/refs.c
index 7ac05cf21a25802f8e16d45ef75e37b7de2cda8a..68f2cb03c4031a525b99ea5a3494a9c64d786c83 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -4159,17 +4159,25 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
 
 int ref_is_hidden(const char *refname)
 {
-       struct string_list_item *item;
+       int i;
 
        if (!hide_refs)
                return 0;
-       for_each_string_list_item(item, hide_refs) {
+       for (i = hide_refs->nr - 1; i >= 0; i--) {
+               const char *match = hide_refs->items[i].string;
+               int neg = 0;
                int len;
-               if (!starts_with(refname, item->string))
+
+               if (*match == '!') {
+                       neg = 1;
+                       match++;
+               }
+
+               if (!starts_with(refname, match))
                        continue;
-               len = strlen(item->string);
+               len = strlen(match);
                if (!refname[len] || refname[len] == '/')
-                       return 1;
+                       return !neg;
        }
        return 0;
 }
index 3bd9759e0ff01af9969e1f8ae088945d9e2e34e4..aadaac515e086a7f87c6d06a0313fe7b77d11f92 100755 (executable)
@@ -128,6 +128,11 @@ test_expect_success 'Report match with --exit-code' '
        test_cmp expect actual
 '
 
+test_expect_success 'set up some extra tags for ref hiding' '
+       git tag magic/one &&
+       git tag magic/two
+'
+
 for configsection in transfer uploadpack
 do
        test_expect_success "Hide some refs with $configsection.hiderefs" '
@@ -138,6 +143,24 @@ do
                sed -e "/       refs\/tags\//d" >expect &&
                test_cmp expect actual
        '
+
+       test_expect_success "Override hiding of $configsection.hiderefs" '
+               test_when_finished "test_unconfig $configsection.hiderefs" &&
+               git config --add $configsection.hiderefs refs/tags &&
+               git config --add $configsection.hiderefs "!refs/tags/magic" &&
+               git config --add $configsection.hiderefs refs/tags/magic/one &&
+               git ls-remote . >actual &&
+               grep refs/tags/magic/two actual &&
+               ! grep refs/tags/magic/one actual
+       '
+
 done
 
+test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs' '
+       test_config uploadpack.hiderefs refs/tags &&
+       test_config transfer.hiderefs "!refs/tags/magic" &&
+       git ls-remote . >actual &&
+       grep refs/tags/magic actual
+'
+
 test_done