Merge branch 'bw/ref-prefix-for-configured-refspec'

"git fetch $there $refspec" that talks over protocol v2 can take
advantage of server-side ref filtering; the code has been extended
so that this mechanism triggers also when fetching with configured
refspec.

* bw/ref-prefix-for-configured-refspec: (38 commits)
  fetch: generate ref-prefixes when using a configured refspec
  refspec: consolidate ref-prefix generation logic
  submodule: convert push_unpushed_submodules to take a struct refspec
  remote: convert check_push_refs to take a struct refspec
  remote: convert match_push_refs to take a struct refspec
  http-push: store refspecs in a struct refspec
  transport: remove transport_verify_remote_names
  send-pack: store refspecs in a struct refspec
  transport: convert transport_push to take a struct refspec
  push: convert to use struct refspec
  push: check for errors earlier
  remote: convert match_explicit_refs to take a struct refspec
  remote: convert get_ref_match to take a struct refspec
  remote: convert query_refspecs to take a struct refspec
  remote: convert apply_refspecs to take a struct refspec
  remote: convert get_stale_heads to take a struct refspec
  fetch: convert prune_refs to take a struct refspec
  fetch: convert get_ref_map to take a struct refspec
  fetch: convert do_fetch to take a struct refspec
  refspec: remove the deprecated functions
  ...
This commit is contained in:
Junio C Hamano
2018-05-30 21:51:26 +09:00
23 changed files with 568 additions and 611 deletions

353
remote.c
View File

@ -2,6 +2,7 @@
#include "config.h"
#include "remote.h"
#include "refs.h"
#include "refspec.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
@ -13,18 +14,6 @@
enum map_direction { FROM_SRC, FROM_DST };
static struct refspec s_tag_refspec = {
0,
1,
0,
0,
"refs/tags/*",
"refs/tags/*"
};
/* See TAG_REFSPEC for the string version */
const struct refspec *tag_refspec = &s_tag_refspec;
struct counted_string {
size_t len;
const char *s;
@ -88,33 +77,6 @@ static const char *alias_url(const char *url, struct rewrites *r)
return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
}
static void add_push_refspec(struct remote *remote, const char *ref)
{
ALLOC_GROW(remote->push_refspec,
remote->push_refspec_nr + 1,
remote->push_refspec_alloc);
remote->push_refspec[remote->push_refspec_nr++] = ref;
}
static void add_fetch_refspec(struct remote *remote, const char *ref)
{
ALLOC_GROW(remote->fetch_refspec,
remote->fetch_refspec_nr + 1,
remote->fetch_refspec_alloc);
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);
@ -186,9 +148,12 @@ 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 */
ret->name = xstrndup(name, len);
refspec_init(&ret->push, REFSPEC_PUSH);
refspec_init(&ret->fetch, REFSPEC_FETCH);
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
remotes[remotes_nr++] = ret;
ret->name = xstrndup(name, len);
hashmap_entry_init(ret, lookup_entry.hash);
replaced = hashmap_put(&remotes_hash, ret);
@ -286,9 +251,9 @@ static void read_remotes_file(struct remote *remote)
if (skip_prefix(buf.buf, "URL:", &v))
add_url_alias(remote, xstrdup(skip_spaces(v)));
else if (skip_prefix(buf.buf, "Push:", &v))
add_push_refspec(remote, xstrdup(skip_spaces(v)));
refspec_append(&remote->push, skip_spaces(v));
else if (skip_prefix(buf.buf, "Pull:", &v))
add_fetch_refspec(remote, xstrdup(skip_spaces(v)));
refspec_append(&remote->fetch, skip_spaces(v));
}
strbuf_release(&buf);
fclose(f);
@ -327,15 +292,19 @@ static void read_branches_file(struct remote *remote)
frag = "master";
add_url_alias(remote, strbuf_detach(&buf, NULL));
add_fetch_refspec(remote, xstrfmt("refs/heads/%s:refs/heads/%s",
frag, remote->name));
strbuf_addf(&buf, "refs/heads/%s:refs/heads/%s",
frag, remote->name);
refspec_append(&remote->fetch, buf.buf);
/*
* Cogito compatible push: push current HEAD to remote #branch
* (master if missing)
*/
add_push_refspec(remote, xstrfmt("HEAD:refs/heads/%s", frag));
strbuf_reset(&buf);
strbuf_addf(&buf, "HEAD:refs/heads/%s", frag);
refspec_append(&remote->push, buf.buf);
remote->fetch_tags = 1; /* always auto-follow */
strbuf_release(&buf);
}
static int handle_config(const char *key, const char *value, void *cb)
@ -420,12 +389,14 @@ static int handle_config(const char *key, const char *value, void *cb)
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_push_refspec(remote, v);
refspec_append(&remote->push, v);
free((char *)v);
} else if (!strcmp(subkey, "fetch")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_fetch_refspec(remote, v);
refspec_append(&remote->fetch, v);
free((char *)v);
} else if (!strcmp(subkey, "receivepack")) {
const char *v;
if (git_config_string(&v, key, value))
@ -499,158 +470,6 @@ static void read_config(void)
alias_all_urls();
}
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));
for (i = 0; i < nr_refspec; i++) {
size_t llen;
int is_glob;
const char *lhs, *rhs;
int flags;
is_glob = 0;
lhs = refspec[i];
if (*lhs == '+') {
rs[i].force = 1;
lhs++;
}
rhs = strrchr(lhs, ':');
/*
* Before going on, special case ":" (or "+:") as a refspec
* for pushing matching refs.
*/
if (!fetch && rhs == lhs && rhs[1] == '\0') {
rs[i].matching = 1;
continue;
}
if (rhs) {
size_t rlen = strlen(++rhs);
is_glob = (1 <= rlen && strchr(rhs, '*'));
rs[i].dst = xstrndup(rhs, rlen);
}
llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
if (1 <= llen && memchr(lhs, '*', llen)) {
if ((rhs && !is_glob) || (!rhs && fetch))
goto invalid;
is_glob = 1;
} else if (rhs && is_glob) {
goto invalid;
}
rs[i].pattern = is_glob;
rs[i].src = xstrndup(lhs, llen);
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
if (fetch) {
struct object_id unused;
/* LHS */
if (!*rs[i].src)
; /* empty is ok; it means "HEAD" */
else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))
rs[i].exact_sha1 = 1; /* ok */
else if (!check_refname_format(rs[i].src, flags))
; /* valid looking ref is ok */
else
goto invalid;
/* RHS */
if (!rs[i].dst)
; /* missing is ok; it is the same as empty */
else if (!*rs[i].dst)
; /* empty is ok; it means "do not store" */
else if (!check_refname_format(rs[i].dst, flags))
; /* valid looking ref is ok */
else
goto invalid;
} else {
/*
* LHS
* - empty is allowed; it means delete.
* - when wildcarded, it must be a valid looking ref.
* - otherwise, it must be an extended SHA-1, but
* there is no existing way to validate this.
*/
if (!*rs[i].src)
; /* empty is ok */
else if (is_glob) {
if (check_refname_format(rs[i].src, flags))
goto invalid;
}
else
; /* anything goes, for now */
/*
* RHS
* - missing is allowed, but LHS then must be a
* valid looking ref.
* - empty is not allowed.
* - otherwise it must be a valid looking ref.
*/
if (!rs[i].dst) {
if (check_refname_format(rs[i].src, flags))
goto invalid;
} else if (!*rs[i].dst) {
goto invalid;
} else {
if (check_refname_format(rs[i].dst, flags))
goto invalid;
}
}
}
return rs;
invalid:
if (verify) {
/*
* nr_refspec must be greater than zero and i must be valid
* since it is only possible to reach this point from within
* the for loop above.
*/
free_refspec(i+1, rs);
return NULL;
}
die("Invalid refspec '%s'", refspec[i]);
}
int valid_fetch_refspec(const char *fetch_refspec_str)
{
struct refspec *refspec;
refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
free_refspec(1, refspec);
return !!refspec;
}
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
{
return parse_refspec_internal(nr_refspec, refspec, 1, 0);
}
struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
{
return parse_refspec_internal(nr_refspec, refspec, 0, 0);
}
void free_refspec(int nr_refspec, struct refspec *refspec)
{
int i;
if (!refspec)
return;
for (i = 0; i < nr_refspec; i++) {
free(refspec[i].src);
free(refspec[i].dst);
}
free(refspec);
}
static int valid_remote_nick(const char *name)
{
if (!name[0] || is_dot_or_dotdot(name))
@ -705,9 +524,8 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push,
pushremote_for_branch(branch, NULL);
struct remote *remote = remote_get(remote_name);
if (remote && remote->push_refspec_nr &&
(dst = apply_refspecs(remote->push,
remote->push_refspec_nr,
if (remote && remote->push.nr &&
(dst = apply_refspecs(&remote->push,
branch->refname))) {
if (explicit)
*explicit = 1;
@ -744,8 +562,6 @@ static struct remote *remote_get_1(const char *name,
add_url_alias(ret, name);
if (!valid_remote(ret))
return NULL;
ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
return ret;
}
@ -776,12 +592,6 @@ int for_each_remote(each_remote_fn fn, void *priv)
struct remote *r = remotes[i];
if (!r)
continue;
if (!r->fetch)
r->fetch = parse_fetch_refspec(r->fetch_refspec_nr,
r->fetch_refspec);
if (!r->push)
r->push = parse_push_refspec(r->push_refspec_nr,
r->push_refspec);
result = fn(r, priv);
}
return result;
@ -887,7 +697,9 @@ static int match_name_with_pattern(const char *key, const char *name,
return ret;
}
static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results)
static void query_refspecs_multiple(struct refspec *rs,
struct refspec_item *query,
struct string_list *results)
{
int i;
int find_src = !query->src;
@ -895,8 +707,8 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct
if (find_src && !query->dst)
error("query_refspecs_multiple: need either src or dst");
for (i = 0; i < ref_count; i++) {
struct refspec *refspec = &refs[i];
for (i = 0; i < rs->nr; i++) {
struct refspec_item *refspec = &rs->items[i];
const char *key = find_src ? refspec->dst : refspec->src;
const char *value = find_src ? refspec->src : refspec->dst;
const char *needle = find_src ? query->dst : query->src;
@ -913,7 +725,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct
}
}
int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
int query_refspecs(struct refspec *rs, struct refspec_item *query)
{
int i;
int find_src = !query->src;
@ -923,8 +735,8 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
if (find_src && !query->dst)
return error("query_refspecs: need either src or dst");
for (i = 0; i < ref_count; i++) {
struct refspec *refspec = &refs[i];
for (i = 0; i < rs->nr; i++) {
struct refspec_item *refspec = &rs->items[i];
const char *key = find_src ? refspec->dst : refspec->src;
const char *value = find_src ? refspec->src : refspec->dst;
@ -944,23 +756,22 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
return -1;
}
char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
const char *name)
char *apply_refspecs(struct refspec *rs, const char *name)
{
struct refspec query;
struct refspec_item query;
memset(&query, 0, sizeof(struct refspec));
memset(&query, 0, sizeof(struct refspec_item));
query.src = (char *)name;
if (query_refspecs(refspecs, nr_refspec, &query))
if (query_refspecs(rs, &query))
return NULL;
return query.dst;
}
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)
{
return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);
return query_refspecs(&remote->fetch, refspec);
}
static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
@ -1167,7 +978,7 @@ static char *guess_ref(const char *name, struct ref *peer)
}
static int match_explicit_lhs(struct ref *src,
struct refspec *rs,
struct refspec_item *rs,
struct ref **match,
int *allocated_match)
{
@ -1193,7 +1004,7 @@ static int match_explicit_lhs(struct ref *src,
static int match_explicit(struct ref *src, struct ref *dst,
struct ref ***dst_tail,
struct refspec *rs)
struct refspec_item *rs)
{
struct ref *matched_src, *matched_dst;
int allocated_src;
@ -1262,36 +1073,37 @@ static int match_explicit(struct ref *src, struct ref *dst,
}
static int match_explicit_refs(struct ref *src, struct ref *dst,
struct ref ***dst_tail, struct refspec *rs,
int rs_nr)
struct ref ***dst_tail, struct refspec *rs)
{
int i, errs;
for (i = errs = 0; i < rs_nr; i++)
errs += match_explicit(src, dst, dst_tail, &rs[i]);
for (i = errs = 0; i < rs->nr; i++)
errs += match_explicit(src, dst, dst_tail, &rs->items[i]);
return errs;
}
static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
int send_mirror, int direction, const struct refspec **ret_pat)
static char *get_ref_match(const struct refspec *rs, const struct ref *ref,
int send_mirror, int direction,
const struct refspec_item **ret_pat)
{
const struct refspec *pat;
const struct refspec_item *pat;
char *name;
int i;
int matching_refs = -1;
for (i = 0; i < rs_nr; i++) {
if (rs[i].matching &&
(matching_refs == -1 || rs[i].force)) {
for (i = 0; i < rs->nr; i++) {
const struct refspec_item *item = &rs->items[i];
if (item->matching &&
(matching_refs == -1 || item->force)) {
matching_refs = i;
continue;
}
if (rs[i].pattern) {
const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
if (item->pattern) {
const char *dst_side = item->dst ? item->dst : item->src;
int match;
if (direction == FROM_SRC)
match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
match = match_name_with_pattern(item->src, ref->name, dst_side, &name);
else
match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
match = match_name_with_pattern(dst_side, ref->name, item->src, &name);
if (match) {
matching_refs = i;
break;
@ -1301,7 +1113,7 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref
if (matching_refs == -1)
return NULL;
pat = rs + matching_refs;
pat = &rs->items[matching_refs];
if (pat->matching) {
/*
* "matching refs"; traditionally we pushed everything
@ -1443,22 +1255,20 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
* but we can catch some errors early before even talking to the
* remote side.
*/
int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
int check_push_refs(struct ref *src, struct refspec *rs)
{
struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);
int ret = 0;
int i;
for (i = 0; i < nr_refspec; i++) {
struct refspec *rs = refspec + i;
for (i = 0; i < rs->nr; i++) {
struct refspec_item *item = &rs->items[i];
if (rs->pattern || rs->matching)
if (item->pattern || item->matching)
continue;
ret |= match_explicit_lhs(src, rs, NULL, NULL);
ret |= match_explicit_lhs(src, item, NULL, NULL);
}
free_refspec(nr_refspec, refspec);
return ret;
}
@ -1471,32 +1281,29 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
* dst (e.g. pushing to a new branch, done in match_explicit_refs).
*/
int match_push_refs(struct ref *src, struct ref **dst,
int nr_refspec, const char **refspec, int flags)
struct refspec *rs, int flags)
{
struct refspec *rs;
int send_all = flags & MATCH_REFS_ALL;
int send_mirror = flags & MATCH_REFS_MIRROR;
int send_prune = flags & MATCH_REFS_PRUNE;
int errs;
static const char *default_refspec[] = { ":", NULL };
struct ref *ref, **dst_tail = tail_ref(dst);
struct string_list dst_ref_index = STRING_LIST_INIT_NODUP;
if (!nr_refspec) {
nr_refspec = 1;
refspec = default_refspec;
}
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
/* If no refspec is provided, use the default ":" */
if (!rs->nr)
refspec_append(rs, ":");
errs = match_explicit_refs(src, *dst, &dst_tail, rs);
/* pick the remainder */
for (ref = src; ref; ref = ref->next) {
struct string_list_item *dst_item;
struct ref *dst_peer;
const struct refspec *pat = NULL;
const struct refspec_item *pat = NULL;
char *dst_name;
dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
dst_name = get_ref_match(rs, ref, send_mirror, FROM_SRC, &pat);
if (!dst_name)
continue;
@ -1545,7 +1352,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
/* We're already sending something to this ref. */
continue;
src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
src_name = get_ref_match(rs, ref, send_mirror, FROM_DST, NULL);
if (src_name) {
if (!src_ref_index.nr)
prepare_ref_index(&src_ref_index, src);
@ -1557,6 +1364,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
}
string_list_clear(&src_ref_index, 0);
}
if (errs)
return -1;
return 0;
@ -1753,7 +1561,7 @@ static const char *tracking_for_push_dest(struct remote *remote,
{
char *ret;
ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
ret = apply_refspecs(&remote->fetch, refname);
if (!ret)
return error_buf(err,
_("push destination '%s' on remote '%s' has no local tracking branch"),
@ -1771,12 +1579,11 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
_("branch '%s' has no remote for pushing"),
branch->name);
if (remote->push_refspec_nr) {
if (remote->push.nr) {
char *dst;
const char *ret;
dst = apply_refspecs(remote->push, remote->push_refspec_nr,
branch->refname);
dst = apply_refspecs(&remote->push, branch->refname);
if (!dst)
return error_buf(err,
_("push refspecs for '%s' do not include '%s'"),
@ -1849,7 +1656,7 @@ static int ignore_symref_update(const char *refname)
* local symbolic ref.
*/
static struct ref *get_expanded_map(const struct ref *remote_refs,
const struct refspec *refspec)
const struct refspec_item *refspec)
{
const struct ref *ref;
struct ref *ret = NULL;
@ -1914,7 +1721,7 @@ static struct ref *get_local_ref(const char *name)
}
int get_fetch_map(const struct ref *remote_refs,
const struct refspec *refspec,
const struct refspec_item *refspec,
struct ref ***tail,
int missing_ok)
{
@ -2252,8 +2059,7 @@ struct ref *guess_remote_head(const struct ref *head,
struct stale_heads_info {
struct string_list *ref_names;
struct ref **stale_refs_tail;
struct refspec *refs;
int ref_count;
struct refspec *rs;
};
static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
@ -2261,12 +2067,12 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec query;
struct refspec_item query;
int i, stale = 1;
memset(&query, 0, sizeof(struct refspec));
memset(&query, 0, sizeof(struct refspec_item));
query.dst = (char *)refname;
query_refspecs_multiple(info->refs, info->ref_count, &query, &matches);
query_refspecs_multiple(info->rs, &query, &matches);
if (matches.nr == 0)
goto clean_exit; /* No matches */
@ -2294,7 +2100,7 @@ clean_exit:
return 0;
}
struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map)
{
struct ref *ref, *stale_refs = NULL;
struct string_list ref_names = STRING_LIST_INIT_NODUP;
@ -2302,8 +2108,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
info.ref_names = &ref_names;
info.stale_refs_tail = &stale_refs;
info.refs = refs;
info.ref_count = ref_count;
info.rs = rs;
for (ref = fetch_map; ref; ref = ref->next)
string_list_append(&ref_names, ref->name);
string_list_sort(&ref_names);
@ -2387,7 +2192,7 @@ static int remote_tracking(struct remote *remote, const char *refname,
{
char *dst;
dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
dst = apply_refspecs(&remote->fetch, refname);
if (!dst)
return -1; /* no tracking ref for refname at remote */
if (read_ref(dst, oid))