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
This commit is contained in:
commit
dbba85e46b
@ -2673,6 +2673,15 @@ 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).
|
||||
+
|
||||
If a namespace is in use, the namespace prefix is stripped from each
|
||||
reference before it is matched against `transfer.hiderefs` patterns.
|
||||
For example, if `refs/heads/master` is specified in `transfer.hideRefs` and
|
||||
the current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master`
|
||||
is omitted from the advertisements but `refs/heads/master` and
|
||||
`refs/namespaces/bar/refs/heads/master` are still advertised as so-called
|
||||
"have" lines. In order to match refs before stripping, add a `^` in front of
|
||||
the ref name. If you combine `!` and `^`, `!` must be specified first.
|
||||
|
||||
transfer.unpackLimit::
|
||||
When `fetch.unpackLimit` or `receive.unpackLimit` are
|
||||
|
@ -195,9 +195,6 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
|
||||
|
||||
static void show_ref(const char *path, const unsigned char *sha1)
|
||||
{
|
||||
if (ref_is_hidden(path))
|
||||
return;
|
||||
|
||||
if (sent_capabilities) {
|
||||
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
|
||||
} else {
|
||||
@ -219,9 +216,14 @@ static void show_ref(const char *path, const unsigned char *sha1)
|
||||
}
|
||||
}
|
||||
|
||||
static int show_ref_cb(const char *path, const struct object_id *oid, int flag, void *unused)
|
||||
static int show_ref_cb(const char *path_full, const struct object_id *oid,
|
||||
int flag, void *unused)
|
||||
{
|
||||
path = strip_namespace(path);
|
||||
const char *path = strip_namespace(path_full);
|
||||
|
||||
if (ref_is_hidden(path, path_full))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Advertise refs outside our current namespace as ".have"
|
||||
* 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])
|
||||
|
||||
static void reject_updates_to_hidden(struct command *commands)
|
||||
{
|
||||
struct strbuf refname_full = STRBUF_INIT;
|
||||
size_t prefix_len;
|
||||
struct command *cmd;
|
||||
|
||||
strbuf_addstr(&refname_full, get_git_namespace());
|
||||
prefix_len = refname_full.len;
|
||||
|
||||
for (cmd = commands; cmd; cmd = cmd->next) {
|
||||
if (cmd->error_string || !ref_is_hidden(cmd->ref_name))
|
||||
if (cmd->error_string)
|
||||
continue;
|
||||
|
||||
strbuf_setlen(&refname_full, prefix_len);
|
||||
strbuf_addstr(&refname_full, cmd->ref_name);
|
||||
|
||||
if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
|
||||
continue;
|
||||
if (is_null_sha1(cmd->new_sha1))
|
||||
cmd->error_string = "deny deleting a hidden ref";
|
||||
else
|
||||
cmd->error_string = "deny updating a hidden ref";
|
||||
}
|
||||
|
||||
strbuf_release(&refname_full);
|
||||
}
|
||||
|
||||
static int should_process_cmd(struct command *cmd)
|
||||
|
15
refs.c
15
refs.c
@ -4534,7 +4534,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ref_is_hidden(const char *refname)
|
||||
int ref_is_hidden(const char *refname, const char *refname_full)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -4542,6 +4542,7 @@ int ref_is_hidden(const char *refname)
|
||||
return 0;
|
||||
for (i = hide_refs->nr - 1; i >= 0; i--) {
|
||||
const char *match = hide_refs->items[i].string;
|
||||
const char *subject;
|
||||
int neg = 0;
|
||||
int len;
|
||||
|
||||
@ -4550,10 +4551,18 @@ int ref_is_hidden(const char *refname)
|
||||
match++;
|
||||
}
|
||||
|
||||
if (!starts_with(refname, match))
|
||||
if (*match == '^') {
|
||||
subject = refname_full;
|
||||
match++;
|
||||
} else {
|
||||
subject = refname;
|
||||
}
|
||||
|
||||
/* refname can be NULL when namespaces are used. */
|
||||
if (!subject || !starts_with(subject, match))
|
||||
continue;
|
||||
len = strlen(match);
|
||||
if (!refname[len] || refname[len] == '/')
|
||||
if (!subject[len] || subject[len] == '/')
|
||||
return !neg;
|
||||
}
|
||||
return 0;
|
||||
|
10
refs.h
10
refs.h
@ -444,7 +444,15 @@ int update_ref(const char *msg, const char *refname,
|
||||
|
||||
extern int parse_hide_refs_config(const char *var, const char *value, const char *);
|
||||
|
||||
extern int ref_is_hidden(const char *);
|
||||
/*
|
||||
* Check whether a ref is hidden. If no namespace is set, both the first and
|
||||
* the second parameter point to the full ref name. If a namespace is set and
|
||||
* the ref is inside that namespace, the first parameter is a pointer to the
|
||||
* name of the ref with the namespace prefix removed. If a namespace is set and
|
||||
* the ref is outside that namespace, the first parameter is NULL. The second
|
||||
* parameter always points to the full ref name.
|
||||
*/
|
||||
extern int ref_is_hidden(const char *, const char *);
|
||||
|
||||
enum ref_type {
|
||||
REF_TYPE_PER_WORKTREE,
|
||||
|
@ -82,4 +82,45 @@ test_expect_success 'mirroring a repository using a ref namespace' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'hide namespaced refs with transfer.hideRefs' '
|
||||
GIT_NAMESPACE=namespace \
|
||||
git -C pushee -c transfer.hideRefs=refs/tags \
|
||||
ls-remote "ext::git %s ." >actual &&
|
||||
printf "$commit1\trefs/heads/master\n" >expected &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'check that transfer.hideRefs does not match unstripped refs' '
|
||||
GIT_NAMESPACE=namespace \
|
||||
git -C pushee -c transfer.hideRefs=refs/namespaces/namespace/refs/tags \
|
||||
ls-remote "ext::git %s ." >actual &&
|
||||
printf "$commit1\trefs/heads/master\n" >expected &&
|
||||
printf "$commit0\trefs/tags/0\n" >>expected &&
|
||||
printf "$commit1\trefs/tags/1\n" >>expected &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'hide full refs with transfer.hideRefs' '
|
||||
GIT_NAMESPACE=namespace \
|
||||
git -C pushee -c transfer.hideRefs="^refs/namespaces/namespace/refs/tags" \
|
||||
ls-remote "ext::git %s ." >actual &&
|
||||
printf "$commit1\trefs/heads/master\n" >expected &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'try to update a hidden ref' '
|
||||
test_config -C pushee transfer.hideRefs refs/heads/master &&
|
||||
test_must_fail git -C original push pushee-namespaced master
|
||||
'
|
||||
|
||||
test_expect_success 'try to update a ref that is not hidden' '
|
||||
test_config -C pushee transfer.hideRefs refs/namespaces/namespace/refs/heads/master &&
|
||||
git -C original push pushee-namespaced master
|
||||
'
|
||||
|
||||
test_expect_success 'try to update a hidden full ref' '
|
||||
test_config -C pushee transfer.hideRefs "^refs/namespaces/namespace/refs/heads/master" &&
|
||||
test_must_fail git -C original push pushee-namespaced master
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -688,11 +688,12 @@ static void receive_needs(void)
|
||||
}
|
||||
|
||||
/* return non-zero if the ref is hidden, otherwise 0 */
|
||||
static int mark_our_ref(const char *refname, const struct object_id *oid)
|
||||
static int mark_our_ref(const char *refname, const char *refname_full,
|
||||
const struct object_id *oid)
|
||||
{
|
||||
struct object *o = lookup_unknown_object(oid->hash);
|
||||
|
||||
if (ref_is_hidden(refname)) {
|
||||
if (ref_is_hidden(refname, refname_full)) {
|
||||
o->flags |= HIDDEN_REF;
|
||||
return 1;
|
||||
}
|
||||
@ -700,10 +701,12 @@ static int mark_our_ref(const char *refname, const struct object_id *oid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_ref(const char *refname, const struct object_id *oid,
|
||||
static int check_ref(const char *refname_full, const struct object_id *oid,
|
||||
int flag, void *cb_data)
|
||||
{
|
||||
mark_our_ref(refname, oid);
|
||||
const char *refname = strip_namespace(refname_full);
|
||||
|
||||
mark_our_ref(refname, refname_full, oid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -726,7 +729,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
|
||||
const char *refname_nons = strip_namespace(refname);
|
||||
struct object_id peeled;
|
||||
|
||||
if (mark_our_ref(refname, oid))
|
||||
if (mark_our_ref(refname_nons, refname, oid))
|
||||
return 0;
|
||||
|
||||
if (capabilities) {
|
||||
|
Loading…
Reference in New Issue
Block a user