Skip to content

Commit ba928c1

Browse files
peffgitster
authored andcommitted
push: detect local refspec errors early
When pushing, we do not even look at our push refspecs until after we have made contact with the remote receive-pack and gotten its list of refs. This means that we may go to some work, including asking the user to log in, before realizing we have simple errors like "git push origin matser". We cannot catch all refspec problems, since fully evaluating the refspecs requires knowing what the remote side has. But we can do a quick sanity check of the local side and catch a few simple error cases. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 471fd3f commit ba928c1

File tree

4 files changed

+80
-2
lines changed

4 files changed

+80
-2
lines changed

remote.c

+25
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,31 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
13731373
sort_string_list(ref_index);
13741374
}
13751375

1376+
/*
1377+
* Given only the set of local refs, sanity-check the set of push
1378+
* refspecs. We can't catch all errors that match_push_refs would,
1379+
* but we can catch some errors early before even talking to the
1380+
* remote side.
1381+
*/
1382+
int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
1383+
{
1384+
struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);
1385+
int ret = 0;
1386+
int i;
1387+
1388+
for (i = 0; i < nr_refspec; i++) {
1389+
struct refspec *rs = refspec + i;
1390+
1391+
if (rs->pattern || rs->matching)
1392+
continue;
1393+
1394+
ret |= match_explicit_lhs(src, rs, NULL, NULL);
1395+
}
1396+
1397+
free_refspec(nr_refspec, refspec);
1398+
return ret;
1399+
}
1400+
13761401
/*
13771402
* Given the set of refs the local repository has, the set of refs the
13781403
* remote repository has, and the refspec used for push, determine

remote.h

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);
166166
char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
167167
const char *name);
168168

169+
int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);
169170
int match_push_refs(struct ref *src, struct ref **dst,
170171
int nr_refspec, const char **refspec, int all);
171172
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,

t/t5529-push-errors.sh

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/sh
2+
3+
test_description='detect some push errors early (before contacting remote)'
4+
. ./test-lib.sh
5+
6+
test_expect_success 'setup commits' '
7+
test_commit one
8+
'
9+
10+
test_expect_success 'setup remote' '
11+
git init --bare remote.git &&
12+
git remote add origin remote.git
13+
'
14+
15+
test_expect_success 'setup fake receive-pack' '
16+
FAKE_RP_ROOT=$(pwd) &&
17+
export FAKE_RP_ROOT &&
18+
write_script fake-rp <<-\EOF &&
19+
echo yes >"$FAKE_RP_ROOT"/rp-ran
20+
exit 1
21+
EOF
22+
git config remote.origin.receivepack "\"\$FAKE_RP_ROOT/fake-rp\""
23+
'
24+
25+
test_expect_success 'detect missing branches early' '
26+
echo no >rp-ran &&
27+
echo no >expect &&
28+
test_must_fail git push origin missing &&
29+
test_cmp expect rp-ran
30+
'
31+
32+
test_expect_success 'detect missing sha1 expressions early' '
33+
echo no >rp-ran &&
34+
echo no >expect &&
35+
test_must_fail git push origin master~2:master &&
36+
test_cmp expect rp-ran
37+
'
38+
39+
test_expect_success 'detect ambiguous refs early' '
40+
git branch foo &&
41+
git tag foo &&
42+
echo no >rp-ran &&
43+
echo no >expect &&
44+
test_must_fail git push origin foo &&
45+
test_cmp expect rp-ran
46+
'
47+
48+
test_done

transport.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -1132,8 +1132,7 @@ int transport_push(struct transport *transport,
11321132

11331133
return transport->push(transport, refspec_nr, refspec, flags);
11341134
} else if (transport->push_refs) {
1135-
struct ref *remote_refs =
1136-
transport->get_refs_list(transport, 1);
1135+
struct ref *remote_refs;
11371136
struct ref *local_refs = get_local_heads();
11381137
int match_flags = MATCH_REFS_NONE;
11391138
int verbose = (transport->verbose > 0);
@@ -1142,6 +1141,11 @@ int transport_push(struct transport *transport,
11421141
int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
11431142
int push_ret, ret, err;
11441143

1144+
if (check_push_refs(local_refs, refspec_nr, refspec) < 0)
1145+
return -1;
1146+
1147+
remote_refs = transport->get_refs_list(transport, 1);
1148+
11451149
if (flags & TRANSPORT_PUSH_ALL)
11461150
match_flags |= MATCH_REFS_ALL;
11471151
if (flags & TRANSPORT_PUSH_MIRROR)

0 commit comments

Comments
 (0)