Skip to content

Commit 2bc31d1

Browse files
peffgitster
authored andcommitted
refs: support negative transfer.hideRefs
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cc118a6 commit 2bc31d1

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

Documentation/config.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,11 @@ transfer.hideRefs::
25402540
excluded, and is hidden when responding to `git push` or `git
25412541
fetch`. See `receive.hideRefs` and `uploadpack.hideRefs` for
25422542
program-specific versions of this config.
2543+
+
2544+
You may also include a `!` in front of the ref name to negate the entry,
2545+
explicitly exposing it, even if an earlier entry marked it as hidden.
2546+
If you have multiple hideRefs values, later entries override earlier ones
2547+
(and entries in more-specific config files override less-specific ones).
25432548

25442549
transfer.unpackLimit::
25452550
When `fetch.unpackLimit` or `receive.unpackLimit` are

refs.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4159,17 +4159,25 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
41594159

41604160
int ref_is_hidden(const char *refname)
41614161
{
4162-
struct string_list_item *item;
4162+
int i;
41634163

41644164
if (!hide_refs)
41654165
return 0;
4166-
for_each_string_list_item(item, hide_refs) {
4166+
for (i = hide_refs->nr - 1; i >= 0; i--) {
4167+
const char *match = hide_refs->items[i].string;
4168+
int neg = 0;
41674169
int len;
4168-
if (!starts_with(refname, item->string))
4170+
4171+
if (*match == '!') {
4172+
neg = 1;
4173+
match++;
4174+
}
4175+
4176+
if (!starts_with(refname, match))
41694177
continue;
4170-
len = strlen(item->string);
4178+
len = strlen(match);
41714179
if (!refname[len] || refname[len] == '/')
4172-
return 1;
4180+
return !neg;
41734181
}
41744182
return 0;
41754183
}

t/t5512-ls-remote.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ test_expect_success 'Report match with --exit-code' '
128128
test_cmp expect actual
129129
'
130130

131+
test_expect_success 'set up some extra tags for ref hiding' '
132+
git tag magic/one &&
133+
git tag magic/two
134+
'
135+
131136
for configsection in transfer uploadpack
132137
do
133138
test_expect_success "Hide some refs with $configsection.hiderefs" '
@@ -138,6 +143,24 @@ do
138143
sed -e "/ refs\/tags\//d" >expect &&
139144
test_cmp expect actual
140145
'
146+
147+
test_expect_success "Override hiding of $configsection.hiderefs" '
148+
test_when_finished "test_unconfig $configsection.hiderefs" &&
149+
git config --add $configsection.hiderefs refs/tags &&
150+
git config --add $configsection.hiderefs "!refs/tags/magic" &&
151+
git config --add $configsection.hiderefs refs/tags/magic/one &&
152+
git ls-remote . >actual &&
153+
grep refs/tags/magic/two actual &&
154+
! grep refs/tags/magic/one actual
155+
'
156+
141157
done
142158

159+
test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs' '
160+
test_config uploadpack.hiderefs refs/tags &&
161+
test_config transfer.hiderefs "!refs/tags/magic" &&
162+
git ls-remote . >actual &&
163+
grep refs/tags/magic actual
164+
'
165+
143166
test_done

0 commit comments

Comments
 (0)