Skip to content

Commit b88605f

Browse files
committed
Merge branch 'jc/maint-fetch-regression-1.5.4' into maint
* jc/maint-fetch-regression-1.5.4: git-fetch test: test tracking fetch results, not just FETCH_HEAD Fix branches file configuration Tighten refspec processing
2 parents 90b2290 + a466637 commit b88605f

File tree

70 files changed

+1067
-55
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1067
-55
lines changed

builtin-fetch.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,5 +630,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
630630

631631
signal(SIGINT, unlock_pack_on_signal);
632632
atexit(unlock_pack);
633-
return do_fetch(transport, parse_ref_spec(ref_nr, refs), ref_nr);
633+
return do_fetch(transport,
634+
parse_fetch_refspec(ref_nr, refs), ref_nr);
634635
}

builtin-send-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ static void verify_remote_names(int nr_heads, const char **heads)
536536
int i;
537537

538538
for (i = 0; i < nr_heads; i++) {
539-
const char *remote = strchr(heads[i], ':');
539+
const char *remote = strrchr(heads[i], ':');
540540

541541
remote = remote ? (remote + 1) : heads[i];
542542
switch (check_ref_format(remote)) {

remote.c

Lines changed: 159 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ static void read_branches_file(struct remote *remote)
171171
{
172172
const char *slash = strchr(remote->name, '/');
173173
char *frag;
174-
char *branch;
174+
struct strbuf branch;
175175
int n = slash ? slash - remote->name : 1000;
176176
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
177177
char *s, *p;
@@ -197,17 +197,33 @@ static void read_branches_file(struct remote *remote)
197197
strcpy(p, s);
198198
if (slash)
199199
strcat(p, slash);
200+
201+
/*
202+
* With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
203+
* reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
204+
* the partial URL obtained from the branches file plus
205+
* "/netdev-2.6" and does not store it in any tracking ref.
206+
* #branch specifier in the file is ignored.
207+
*
208+
* Otherwise, the branches file would have URL and optionally
209+
* #branch specified. The "master" (or specified) branch is
210+
* fetched and stored in the local branch of the same name.
211+
*/
212+
strbuf_init(&branch, 0);
200213
frag = strchr(p, '#');
201214
if (frag) {
202215
*(frag++) = '\0';
203-
branch = xmalloc(strlen(frag) + 12);
204-
strcpy(branch, "refs/heads/");
205-
strcat(branch, frag);
216+
strbuf_addf(&branch, "refs/heads/%s", frag);
217+
} else
218+
strbuf_addstr(&branch, "refs/heads/master");
219+
if (!slash) {
220+
strbuf_addf(&branch, ":refs/heads/%s", remote->name);
206221
} else {
207-
branch = "refs/heads/master";
222+
strbuf_reset(&branch);
223+
strbuf_addstr(&branch, "HEAD:");
208224
}
209225
add_url(remote, p);
210-
add_fetch_refspec(remote, branch);
226+
add_fetch_refspec(remote, strbuf_detach(&branch, 0));
211227
remote->fetch_tags = 1; /* always auto-follow */
212228
}
213229

@@ -305,42 +321,127 @@ static void read_config(void)
305321
git_config(handle_config);
306322
}
307323

308-
struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
324+
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
309325
{
310326
int i;
327+
int st;
311328
struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
329+
312330
for (i = 0; i < nr_refspec; i++) {
313-
const char *sp, *ep, *gp;
314-
sp = refspec[i];
315-
if (*sp == '+') {
331+
size_t llen, rlen;
332+
int is_glob;
333+
const char *lhs, *rhs;
334+
335+
llen = rlen = is_glob = 0;
336+
337+
lhs = refspec[i];
338+
if (*lhs == '+') {
316339
rs[i].force = 1;
317-
sp++;
340+
lhs++;
318341
}
319-
gp = strchr(sp, '*');
320-
ep = strchr(sp, ':');
321-
if (gp && ep && gp > ep)
322-
gp = NULL;
323-
if (ep) {
324-
if (ep[1]) {
325-
const char *glob = strchr(ep + 1, '*');
326-
if (!glob)
327-
gp = NULL;
328-
if (gp)
329-
rs[i].dst = xstrndup(ep + 1,
330-
glob - ep - 1);
331-
else
332-
rs[i].dst = xstrdup(ep + 1);
342+
343+
rhs = strrchr(lhs, ':');
344+
if (rhs) {
345+
rhs++;
346+
rlen = strlen(rhs);
347+
is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
348+
if (is_glob)
349+
rlen -= 2;
350+
rs[i].dst = xstrndup(rhs, rlen);
351+
}
352+
353+
llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
354+
if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
355+
if ((rhs && !is_glob) || (!rhs && fetch))
356+
goto invalid;
357+
is_glob = 1;
358+
llen -= 2;
359+
} else if (rhs && is_glob) {
360+
goto invalid;
361+
}
362+
363+
rs[i].pattern = is_glob;
364+
rs[i].src = xstrndup(lhs, llen);
365+
366+
if (fetch) {
367+
/*
368+
* LHS
369+
* - empty is allowed; it means HEAD.
370+
* - otherwise it must be a valid looking ref.
371+
*/
372+
if (!*rs[i].src)
373+
; /* empty is ok */
374+
else {
375+
st = check_ref_format(rs[i].src);
376+
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
377+
goto invalid;
378+
}
379+
/*
380+
* RHS
381+
* - missing is ok, and is same as empty.
382+
* - empty is ok; it means not to store.
383+
* - otherwise it must be a valid looking ref.
384+
*/
385+
if (!rs[i].dst) {
386+
; /* ok */
387+
} else if (!*rs[i].dst) {
388+
; /* ok */
389+
} else {
390+
st = check_ref_format(rs[i].dst);
391+
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
392+
goto invalid;
333393
}
334394
} else {
335-
ep = sp + strlen(sp);
336-
}
337-
if (gp) {
338-
rs[i].pattern = 1;
339-
ep = gp;
395+
/*
396+
* LHS
397+
* - empty is allowed; it means delete.
398+
* - when wildcarded, it must be a valid looking ref.
399+
* - otherwise, it must be an extended SHA-1, but
400+
* there is no existing way to validate this.
401+
*/
402+
if (!*rs[i].src)
403+
; /* empty is ok */
404+
else if (is_glob) {
405+
st = check_ref_format(rs[i].src);
406+
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
407+
goto invalid;
408+
}
409+
else
410+
; /* anything goes, for now */
411+
/*
412+
* RHS
413+
* - missing is allowed, but LHS then must be a
414+
* valid looking ref.
415+
* - empty is not allowed.
416+
* - otherwise it must be a valid looking ref.
417+
*/
418+
if (!rs[i].dst) {
419+
st = check_ref_format(rs[i].src);
420+
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
421+
goto invalid;
422+
} else if (!*rs[i].dst) {
423+
goto invalid;
424+
} else {
425+
st = check_ref_format(rs[i].dst);
426+
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
427+
goto invalid;
428+
}
340429
}
341-
rs[i].src = xstrndup(sp, ep - sp);
342430
}
343431
return rs;
432+
433+
invalid:
434+
die("Invalid refspec '%s'", refspec[i]);
435+
}
436+
437+
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
438+
{
439+
return parse_refspec_internal(nr_refspec, refspec, 1);
440+
}
441+
442+
struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
443+
{
444+
return parse_refspec_internal(nr_refspec, refspec, 0);
344445
}
345446

346447
static int valid_remote_nick(const char *name)
@@ -371,8 +472,8 @@ struct remote *remote_get(const char *name)
371472
add_url(ret, name);
372473
if (!ret->url)
373474
return NULL;
374-
ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
375-
ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec);
475+
ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
476+
ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
376477
return ret;
377478
}
378479

@@ -385,11 +486,11 @@ int for_each_remote(each_remote_fn fn, void *priv)
385486
if (!r)
386487
continue;
387488
if (!r->fetch)
388-
r->fetch = parse_ref_spec(r->fetch_refspec_nr,
389-
r->fetch_refspec);
489+
r->fetch = parse_fetch_refspec(r->fetch_refspec_nr,
490+
r->fetch_refspec);
390491
if (!r->push)
391-
r->push = parse_ref_spec(r->push_refspec_nr,
392-
r->push_refspec);
492+
r->push = parse_push_refspec(r->push_refspec_nr,
493+
r->push_refspec);
393494
result = fn(r, priv);
394495
}
395496
return result;
@@ -455,7 +556,8 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
455556
if (!fetch->dst)
456557
continue;
457558
if (fetch->pattern) {
458-
if (!prefixcmp(needle, key)) {
559+
if (!prefixcmp(needle, key) &&
560+
needle[strlen(key)] == '/') {
459561
*result = xmalloc(strlen(value) +
460562
strlen(needle) -
461563
strlen(key) + 1);
@@ -695,7 +797,9 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
695797
{
696798
int i;
697799
for (i = 0; i < rs_nr; i++) {
698-
if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
800+
if (rs[i].pattern &&
801+
!prefixcmp(src->name, rs[i].src) &&
802+
src->name[strlen(rs[i].src)] == '/')
699803
return rs + i;
700804
}
701805
return NULL;
@@ -710,7 +814,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
710814
int nr_refspec, const char **refspec, int flags)
711815
{
712816
struct refspec *rs =
713-
parse_ref_spec(nr_refspec, (const char **) refspec);
817+
parse_push_refspec(nr_refspec, (const char **) refspec);
714818
int send_all = flags & MATCH_REFS_ALL;
715819
int send_mirror = flags & MATCH_REFS_MIRROR;
716820

@@ -894,7 +998,7 @@ int get_fetch_map(const struct ref *remote_refs,
894998
struct ref ***tail,
895999
int missing_ok)
8961000
{
897-
struct ref *ref_map, *rm;
1001+
struct ref *ref_map, **rmp;
8981002

8991003
if (refspec->pattern) {
9001004
ref_map = get_expanded_map(remote_refs, refspec);
@@ -911,10 +1015,20 @@ int get_fetch_map(const struct ref *remote_refs,
9111015
}
9121016
}
9131017

914-
for (rm = ref_map; rm; rm = rm->next) {
915-
if (rm->peer_ref && check_ref_format(rm->peer_ref->name + 5))
916-
die("* refusing to create funny ref '%s' locally",
917-
rm->peer_ref->name);
1018+
for (rmp = &ref_map; *rmp; ) {
1019+
if ((*rmp)->peer_ref) {
1020+
int st = check_ref_format((*rmp)->peer_ref->name + 5);
1021+
if (st && st != CHECK_REF_FORMAT_ONELEVEL) {
1022+
struct ref *ignore = *rmp;
1023+
error("* Ignoring funny ref '%s' locally",
1024+
(*rmp)->peer_ref->name);
1025+
*rmp = (*rmp)->next;
1026+
free(ignore->peer_ref);
1027+
free(ignore);
1028+
continue;
1029+
}
1030+
}
1031+
rmp = &((*rmp)->next);
9181032
}
9191033

9201034
if (ref_map)

remote.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ void free_refs(struct ref *ref);
6363
*/
6464
void ref_remove_duplicates(struct ref *ref_map);
6565

66-
struct refspec *parse_ref_spec(int nr_refspec, const char **refspec);
66+
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
67+
struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
6768

6869
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
6970
int nr_refspec, const char **refspec, int all);

t/t5511-refspec.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/sh
2+
3+
test_description='refspec parsing'
4+
5+
. ./test-lib.sh
6+
7+
test_refspec () {
8+
9+
kind=$1 refspec=$2 expect=$3
10+
git config remote.frotz.url "." &&
11+
git config --remove-section remote.frotz &&
12+
git config remote.frotz.url "." &&
13+
git config "remote.frotz.$kind" "$refspec" &&
14+
if test "$expect" != invalid
15+
then
16+
title="$kind $refspec"
17+
test='git ls-remote frotz'
18+
else
19+
title="$kind $refspec (invalid)"
20+
test='test_must_fail git ls-remote frotz'
21+
fi
22+
test_expect_success "$title" "$test"
23+
}
24+
25+
test_refspec push '' invalid
26+
test_refspec push ':' invalid
27+
28+
test_refspec fetch ''
29+
test_refspec fetch ':'
30+
31+
test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
32+
test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid
33+
test_refspec push 'refs/heads:refs/remotes/frotz/*' invalid
34+
test_refspec push 'refs/heads/master:refs/remotes/frotz/xyzzy'
35+
36+
37+
# These have invalid LHS, but we do not have a formal "valid sha-1
38+
# expression syntax checker" so they are not checked with the current
39+
# code. They will be caught downstream anyway, but we may want to
40+
# have tighter check later...
41+
42+
: test_refspec push 'refs/heads/master::refs/remotes/frotz/xyzzy' invalid
43+
: test_refspec push 'refs/heads/maste :refs/remotes/frotz/xyzzy' invalid
44+
45+
test_refspec fetch 'refs/heads/*:refs/remotes/frotz/*'
46+
test_refspec fetch 'refs/heads/*:refs/remotes/frotz' invalid
47+
test_refspec fetch 'refs/heads:refs/remotes/frotz/*' invalid
48+
test_refspec fetch 'refs/heads/master:refs/remotes/frotz/xyzzy'
49+
test_refspec fetch 'refs/heads/master::refs/remotes/frotz/xyzzy' invalid
50+
test_refspec fetch 'refs/heads/maste :refs/remotes/frotz/xyzzy' invalid
51+
52+
test_refspec push 'master~1:refs/remotes/frotz/backup'
53+
test_refspec fetch 'master~1:refs/remotes/frotz/backup' invalid
54+
test_refspec push 'HEAD~4:refs/remotes/frotz/new'
55+
test_refspec fetch 'HEAD~4:refs/remotes/frotz/new' invalid
56+
57+
test_refspec push 'HEAD'
58+
test_refspec fetch 'HEAD'
59+
test_refspec push 'refs/heads/ nitfol' invalid
60+
test_refspec fetch 'refs/heads/ nitfol' invalid
61+
62+
test_refspec push 'HEAD:' invalid
63+
test_refspec fetch 'HEAD:'
64+
test_refspec push 'refs/heads/ nitfol:' invalid
65+
test_refspec fetch 'refs/heads/ nitfol:' invalid
66+
67+
test_refspec push ':refs/remotes/frotz/deleteme'
68+
test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
69+
test_refspec push ':refs/remotes/frotz/delete me' invalid
70+
test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid
71+
72+
test_done

0 commit comments

Comments
 (0)