Skip to content

Commit dbba85e

Browse files
committed
Merge branch 'lf/ref-is-hidden-namespace'
Extend transfer.hideRefs to work better with use of namespaces. * lf/ref-is-hidden-namespace: t5509: add basic tests for hideRefs hideRefs: add support for matching full refs upload-pack: strip refs before calling ref_is_hidden() config.txt: document the semantics of hideRefs with namespaces
2 parents 45014be + 948bfa2 commit dbba85e

File tree

6 files changed

+100
-15
lines changed

6 files changed

+100
-15
lines changed

Documentation/config.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,6 +2673,15 @@ You may also include a `!` in front of the ref name to negate the entry,
26732673
explicitly exposing it, even if an earlier entry marked it as hidden.
26742674
If you have multiple hideRefs values, later entries override earlier ones
26752675
(and entries in more-specific config files override less-specific ones).
2676+
+
2677+
If a namespace is in use, the namespace prefix is stripped from each
2678+
reference before it is matched against `transfer.hiderefs` patterns.
2679+
For example, if `refs/heads/master` is specified in `transfer.hideRefs` and
2680+
the current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master`
2681+
is omitted from the advertisements but `refs/heads/master` and
2682+
`refs/namespaces/bar/refs/heads/master` are still advertised as so-called
2683+
"have" lines. In order to match refs before stripping, add a `^` in front of
2684+
the ref name. If you combine `!` and `^`, `!` must be specified first.
26762685

26772686
transfer.unpackLimit::
26782687
When `fetch.unpackLimit` or `receive.unpackLimit` are

builtin/receive-pack.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,6 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
195195

196196
static void show_ref(const char *path, const unsigned char *sha1)
197197
{
198-
if (ref_is_hidden(path))
199-
return;
200-
201198
if (sent_capabilities) {
202199
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
203200
} else {
@@ -219,9 +216,14 @@ static void show_ref(const char *path, const unsigned char *sha1)
219216
}
220217
}
221218

222-
static int show_ref_cb(const char *path, const struct object_id *oid, int flag, void *unused)
219+
static int show_ref_cb(const char *path_full, const struct object_id *oid,
220+
int flag, void *unused)
223221
{
224-
path = strip_namespace(path);
222+
const char *path = strip_namespace(path_full);
223+
224+
if (ref_is_hidden(path, path_full))
225+
return 0;
226+
225227
/*
226228
* Advertise refs outside our current namespace as ".have"
227229
* refs, so that the client can use them to minimize data
@@ -1195,16 +1197,29 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
11951197

11961198
static void reject_updates_to_hidden(struct command *commands)
11971199
{
1200+
struct strbuf refname_full = STRBUF_INIT;
1201+
size_t prefix_len;
11981202
struct command *cmd;
11991203

1204+
strbuf_addstr(&refname_full, get_git_namespace());
1205+
prefix_len = refname_full.len;
1206+
12001207
for (cmd = commands; cmd; cmd = cmd->next) {
1201-
if (cmd->error_string || !ref_is_hidden(cmd->ref_name))
1208+
if (cmd->error_string)
1209+
continue;
1210+
1211+
strbuf_setlen(&refname_full, prefix_len);
1212+
strbuf_addstr(&refname_full, cmd->ref_name);
1213+
1214+
if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
12021215
continue;
12031216
if (is_null_sha1(cmd->new_sha1))
12041217
cmd->error_string = "deny deleting a hidden ref";
12051218
else
12061219
cmd->error_string = "deny updating a hidden ref";
12071220
}
1221+
1222+
strbuf_release(&refname_full);
12081223
}
12091224

12101225
static int should_process_cmd(struct command *cmd)

refs.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4534,14 +4534,15 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
45344534
return 0;
45354535
}
45364536

4537-
int ref_is_hidden(const char *refname)
4537+
int ref_is_hidden(const char *refname, const char *refname_full)
45384538
{
45394539
int i;
45404540

45414541
if (!hide_refs)
45424542
return 0;
45434543
for (i = hide_refs->nr - 1; i >= 0; i--) {
45444544
const char *match = hide_refs->items[i].string;
4545+
const char *subject;
45454546
int neg = 0;
45464547
int len;
45474548

@@ -4550,10 +4551,18 @@ int ref_is_hidden(const char *refname)
45504551
match++;
45514552
}
45524553

4553-
if (!starts_with(refname, match))
4554+
if (*match == '^') {
4555+
subject = refname_full;
4556+
match++;
4557+
} else {
4558+
subject = refname;
4559+
}
4560+
4561+
/* refname can be NULL when namespaces are used. */
4562+
if (!subject || !starts_with(subject, match))
45544563
continue;
45554564
len = strlen(match);
4556-
if (!refname[len] || refname[len] == '/')
4565+
if (!subject[len] || subject[len] == '/')
45574566
return !neg;
45584567
}
45594568
return 0;

refs.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,15 @@ int update_ref(const char *msg, const char *refname,
444444

445445
extern int parse_hide_refs_config(const char *var, const char *value, const char *);
446446

447-
extern int ref_is_hidden(const char *);
447+
/*
448+
* Check whether a ref is hidden. If no namespace is set, both the first and
449+
* the second parameter point to the full ref name. If a namespace is set and
450+
* the ref is inside that namespace, the first parameter is a pointer to the
451+
* name of the ref with the namespace prefix removed. If a namespace is set and
452+
* the ref is outside that namespace, the first parameter is NULL. The second
453+
* parameter always points to the full ref name.
454+
*/
455+
extern int ref_is_hidden(const char *, const char *);
448456

449457
enum ref_type {
450458
REF_TYPE_PER_WORKTREE,

t/t5509-fetch-push-namespaces.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,45 @@ test_expect_success 'mirroring a repository using a ref namespace' '
8282
)
8383
'
8484

85+
test_expect_success 'hide namespaced refs with transfer.hideRefs' '
86+
GIT_NAMESPACE=namespace \
87+
git -C pushee -c transfer.hideRefs=refs/tags \
88+
ls-remote "ext::git %s ." >actual &&
89+
printf "$commit1\trefs/heads/master\n" >expected &&
90+
test_cmp expected actual
91+
'
92+
93+
test_expect_success 'check that transfer.hideRefs does not match unstripped refs' '
94+
GIT_NAMESPACE=namespace \
95+
git -C pushee -c transfer.hideRefs=refs/namespaces/namespace/refs/tags \
96+
ls-remote "ext::git %s ." >actual &&
97+
printf "$commit1\trefs/heads/master\n" >expected &&
98+
printf "$commit0\trefs/tags/0\n" >>expected &&
99+
printf "$commit1\trefs/tags/1\n" >>expected &&
100+
test_cmp expected actual
101+
'
102+
103+
test_expect_success 'hide full refs with transfer.hideRefs' '
104+
GIT_NAMESPACE=namespace \
105+
git -C pushee -c transfer.hideRefs="^refs/namespaces/namespace/refs/tags" \
106+
ls-remote "ext::git %s ." >actual &&
107+
printf "$commit1\trefs/heads/master\n" >expected &&
108+
test_cmp expected actual
109+
'
110+
111+
test_expect_success 'try to update a hidden ref' '
112+
test_config -C pushee transfer.hideRefs refs/heads/master &&
113+
test_must_fail git -C original push pushee-namespaced master
114+
'
115+
116+
test_expect_success 'try to update a ref that is not hidden' '
117+
test_config -C pushee transfer.hideRefs refs/namespaces/namespace/refs/heads/master &&
118+
git -C original push pushee-namespaced master
119+
'
120+
121+
test_expect_success 'try to update a hidden full ref' '
122+
test_config -C pushee transfer.hideRefs "^refs/namespaces/namespace/refs/heads/master" &&
123+
test_must_fail git -C original push pushee-namespaced master
124+
'
125+
85126
test_done

upload-pack.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -688,22 +688,25 @@ static void receive_needs(void)
688688
}
689689

690690
/* return non-zero if the ref is hidden, otherwise 0 */
691-
static int mark_our_ref(const char *refname, const struct object_id *oid)
691+
static int mark_our_ref(const char *refname, const char *refname_full,
692+
const struct object_id *oid)
692693
{
693694
struct object *o = lookup_unknown_object(oid->hash);
694695

695-
if (ref_is_hidden(refname)) {
696+
if (ref_is_hidden(refname, refname_full)) {
696697
o->flags |= HIDDEN_REF;
697698
return 1;
698699
}
699700
o->flags |= OUR_REF;
700701
return 0;
701702
}
702703

703-
static int check_ref(const char *refname, const struct object_id *oid,
704+
static int check_ref(const char *refname_full, const struct object_id *oid,
704705
int flag, void *cb_data)
705706
{
706-
mark_our_ref(refname, oid);
707+
const char *refname = strip_namespace(refname_full);
708+
709+
mark_our_ref(refname, refname_full, oid);
707710
return 0;
708711
}
709712

@@ -726,7 +729,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
726729
const char *refname_nons = strip_namespace(refname);
727730
struct object_id peeled;
728731

729-
if (mark_our_ref(refname, oid))
732+
if (mark_our_ref(refname_nons, refname, oid))
730733
return 0;
731734

732735
if (capabilities) {

0 commit comments

Comments
 (0)