Skip to content

Commit 182fb4d

Browse files
committed
Merge branch 'pb/push'
* pb/push: add special "matching refs" refspec
2 parents e5e9714 + a83619d commit 182fb4d

File tree

6 files changed

+116
-35
lines changed

6 files changed

+116
-35
lines changed

Documentation/git-push.txt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,20 @@ specified, the same ref that <src> referred to locally). If
4646
the optional leading plus `+` is used, the remote ref is updated
4747
even if it does not result in a fast forward update.
4848
+
49-
Note: If no explicit refspec is found, (that is neither
50-
on the command line nor in any Push line of the
51-
corresponding remotes file---see below), then "matching" heads are
52-
pushed: for every head that exists on the local side, the remote side is
53-
updated if a head of the same name already exists on the remote side.
54-
+
5549
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
5650
+
5751
A parameter <ref> without a colon pushes the <ref> from the source
5852
repository to the destination repository under the same name.
5953
+
6054
Pushing an empty <src> allows you to delete the <dst> ref from
6155
the remote repository.
56+
+
57+
The special refspec `:` (or `+:` to allow non-fast forward updates)
58+
directs git to push "matching" heads: for every head that exists on
59+
the local side, the remote side is updated if a head of the same name
60+
already exists on the remote side. This is the default operation mode
61+
if no explicit refspec is found (that is neither on the command line
62+
nor in any Push line of the corresponding remotes file---see below).
6263

6364
\--all::
6465
Instead of naming each ref to push, specifies that all

builtin-send-pack.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,9 +537,17 @@ static void verify_remote_names(int nr_heads, const char **heads)
537537
int i;
538538

539539
for (i = 0; i < nr_heads; i++) {
540+
const char *local = heads[i];
540541
const char *remote = strrchr(heads[i], ':');
541542

542-
remote = remote ? (remote + 1) : heads[i];
543+
if (*local == '+')
544+
local++;
545+
546+
/* A matching refspec is okay. */
547+
if (remote == local && remote[1] == '\0')
548+
continue;
549+
550+
remote = remote ? (remote + 1) : local;
543551
switch (check_ref_format(remote)) {
544552
case 0: /* ok */
545553
case CHECK_REF_FORMAT_ONELEVEL:

remote.c

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,16 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
434434
}
435435

436436
rhs = strrchr(lhs, ':');
437+
438+
/*
439+
* Before going on, special case ":" (or "+:") as a refspec
440+
* for matching refs.
441+
*/
442+
if (!fetch && rhs == lhs && rhs[1] == '\0') {
443+
rs[i].matching = 1;
444+
continue;
445+
}
446+
437447
if (rhs) {
438448
rhs++;
439449
rlen = strlen(rhs);
@@ -855,7 +865,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
855865
const char *dst_value = rs->dst;
856866
char *dst_guess;
857867

858-
if (rs->pattern)
868+
if (rs->pattern || rs->matching)
859869
return errs;
860870

861871
matched_src = matched_dst = NULL;
@@ -945,13 +955,23 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
945955
const struct ref *src)
946956
{
947957
int i;
958+
int matching_refs = -1;
948959
for (i = 0; i < rs_nr; i++) {
960+
if (rs[i].matching &&
961+
(matching_refs == -1 || rs[i].force)) {
962+
matching_refs = i;
963+
continue;
964+
}
965+
949966
if (rs[i].pattern &&
950967
!prefixcmp(src->name, rs[i].src) &&
951968
src->name[strlen(rs[i].src)] == '/')
952969
return rs + i;
953970
}
954-
return NULL;
971+
if (matching_refs != -1)
972+
return rs + matching_refs;
973+
else
974+
return NULL;
955975
}
956976

957977
/*
@@ -962,11 +982,16 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
962982
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
963983
int nr_refspec, const char **refspec, int flags)
964984
{
965-
struct refspec *rs =
966-
parse_push_refspec(nr_refspec, (const char **) refspec);
985+
struct refspec *rs;
967986
int send_all = flags & MATCH_REFS_ALL;
968987
int send_mirror = flags & MATCH_REFS_MIRROR;
988+
static const char *default_refspec[] = { ":", 0 };
969989

990+
if (!nr_refspec) {
991+
nr_refspec = 1;
992+
refspec = default_refspec;
993+
}
994+
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
970995
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
971996
return -1;
972997

@@ -977,48 +1002,50 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
9771002
char *dst_name;
9781003
if (src->peer_ref)
9791004
continue;
980-
if (nr_refspec) {
981-
pat = check_pattern_match(rs, nr_refspec, src);
982-
if (!pat)
983-
continue;
984-
}
985-
else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
1005+
1006+
pat = check_pattern_match(rs, nr_refspec, src);
1007+
if (!pat)
1008+
continue;
1009+
1010+
if (pat->matching) {
9861011
/*
9871012
* "matching refs"; traditionally we pushed everything
9881013
* including refs outside refs/heads/ hierarchy, but
9891014
* that does not make much sense these days.
9901015
*/
991-
continue;
1016+
if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
1017+
continue;
1018+
dst_name = xstrdup(src->name);
9921019

993-
if (pat) {
1020+
} else {
9941021
const char *dst_side = pat->dst ? pat->dst : pat->src;
9951022
dst_name = xmalloc(strlen(dst_side) +
9961023
strlen(src->name) -
9971024
strlen(pat->src) + 2);
9981025
strcpy(dst_name, dst_side);
9991026
strcat(dst_name, src->name + strlen(pat->src));
1000-
} else
1001-
dst_name = xstrdup(src->name);
1027+
}
10021028
dst_peer = find_ref_by_name(dst, dst_name);
1003-
if (dst_peer && dst_peer->peer_ref)
1004-
/* We're already sending something to this ref. */
1005-
goto free_name;
1029+
if (dst_peer) {
1030+
if (dst_peer->peer_ref)
1031+
/* We're already sending something to this ref. */
1032+
goto free_name;
1033+
1034+
} else {
1035+
if (pat->matching && !(send_all || send_mirror))
1036+
/*
1037+
* Remote doesn't have it, and we have no
1038+
* explicit pattern, and we don't have
1039+
* --all nor --mirror.
1040+
*/
1041+
goto free_name;
10061042

1007-
if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
1008-
/*
1009-
* Remote doesn't have it, and we have no
1010-
* explicit pattern, and we don't have
1011-
* --all nor --mirror.
1012-
*/
1013-
goto free_name;
1014-
if (!dst_peer) {
10151043
/* Create a new one and link it */
10161044
dst_peer = make_linked_ref(dst_name, dst_tail);
10171045
hashcpy(dst_peer->new_sha1, src->new_sha1);
10181046
}
10191047
dst_peer->peer_ref = src;
1020-
if (pat)
1021-
dst_peer->force = pat->force;
1048+
dst_peer->force = pat->force;
10221049
free_name:
10231050
free(dst_name);
10241051
}

remote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ int remote_has_url(struct remote *remote, const char *url);
4747
struct refspec {
4848
unsigned force : 1;
4949
unsigned pattern : 1;
50+
unsigned matching : 1;
5051

5152
char *src;
5253
char *dst;

t/t5511-refspec.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ test_refspec () {
2323
}
2424

2525
test_refspec push '' invalid
26-
test_refspec push ':' invalid
26+
test_refspec push ':'
27+
test_refspec push '::' invalid
28+
test_refspec push '+:'
2729

2830
test_refspec fetch ''
2931
test_refspec fetch ':'
32+
test_refspec fetch '::' invalid
3033

3134
test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
3235
test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid

t/t5516-fetch-push.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,47 @@ test_expect_success 'push with matching heads' '
165165
166166
'
167167

168+
test_expect_success 'push with matching heads on the command line' '
169+
170+
mk_test heads/master &&
171+
git push testrepo : &&
172+
check_push_result $the_commit heads/master
173+
174+
'
175+
176+
test_expect_success 'failed (non-fast-forward) push with matching heads' '
177+
178+
mk_test heads/master &&
179+
git push testrepo : &&
180+
git commit --amend -massaged &&
181+
! git push testrepo &&
182+
check_push_result $the_commit heads/master &&
183+
git reset --hard $the_commit
184+
185+
'
186+
187+
test_expect_success 'push --force with matching heads' '
188+
189+
mk_test heads/master &&
190+
git push testrepo : &&
191+
git commit --amend -massaged &&
192+
git push --force testrepo &&
193+
! check_push_result $the_commit heads/master &&
194+
git reset --hard $the_commit
195+
196+
'
197+
198+
test_expect_success 'push with matching heads and forced update' '
199+
200+
mk_test heads/master &&
201+
git push testrepo : &&
202+
git commit --amend -massaged &&
203+
git push testrepo +: &&
204+
! check_push_result $the_commit heads/master &&
205+
git reset --hard $the_commit
206+
207+
'
208+
168209
test_expect_success 'push with no ambiguity (1)' '
169210
170211
mk_test heads/master &&

0 commit comments

Comments
 (0)