@matheusbsbs / matheus-bagliano / commits / a83619d692
Fork 0

add special "matching refs" refspec

This patch provides a way to specify "push matching heads" using a special refspec ":". This is useful because it allows "push = +:" as a way to specify that matching refs will be pushed but, in addition, forced updates will be allowed, which was not possible before. Signed-off-by: Paolo Bonzini <bonzini@gnu.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Paolo Bonzini committed Apr 28, 2008 at 11:32 UTC a83619d692deeb2565335144078465acb2dd1457
6 files changed +116 -35
Documentation/git-push.txt
+7 -6
@@ -46,12 +46,6 @@ specified, the same ref that <src> referred to locally). If
46 the optional leading plus `+` is used, the remote ref is updated
47 even if it does not result in a fast forward update.
48 +
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 -+
49 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
50 +
51 A parameter <ref> without a colon pushes the <ref> from the source
@@ -59,6 +53,13 @@ repository to the destination repository under the same name.
53 +
54 Pushing an empty <src> allows you to delete the <dst> ref from
55 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).
63
64 \--all::
65 Instead of naming each ref to push, specifies that all
builtin-send-pack.c
+9 -1
@@ -537,9 +537,17 @@ static void verify_remote_names(int nr_heads, const char **heads)
537 int i;
538
539 for (i = 0; i < nr_heads; i++) {
540 + const char *local = heads[i];
541 const char *remote = strrchr(heads[i], ':');
542
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;
551 switch (check_ref_format(remote)) {
552 case 0: /* ok */
553 case CHECK_REF_FORMAT_ONELEVEL:
remote.c
+54 -27
@@ -429,6 +429,16 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
429 }
430
431 rhs = strrchr(lhs, ':');
432 +
433 + /*
434 + * Before going on, special case ":" (or "+:") as a refspec
435 + * for matching refs.
436 + */
437 + if (!fetch && rhs == lhs && rhs[1] == '\0') {
438 + rs[i].matching = 1;
439 + continue;
440 + }
441 +
442 if (rhs) {
443 rhs++;
444 rlen = strlen(rhs);
@@ -842,7 +852,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
852 const char *dst_value = rs->dst;
853 char *dst_guess;
854
845 - if (rs->pattern)
855 + if (rs->pattern || rs->matching)
856 return errs;
857
858 matched_src = matched_dst = NULL;
@@ -932,13 +942,23 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
942 const struct ref *src)
943 {
944 int i;
945 + int matching_refs = -1;
946 for (i = 0; i < rs_nr; i++) {
947 + if (rs[i].matching &&
948 + (matching_refs == -1 || rs[i].force)) {
949 + matching_refs = i;
950 + continue;
951 + }
952 +
953 if (rs[i].pattern &&
954 !prefixcmp(src->name, rs[i].src) &&
955 src->name[strlen(rs[i].src)] == '/')
956 return rs + i;
957 }
941 - return NULL;
958 + if (matching_refs != -1)
959 + return rs + matching_refs;
960 + else
961 + return NULL;
962 }
963
964 /*
@@ -949,11 +969,16 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
969 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
970 int nr_refspec, const char **refspec, int flags)
971 {
952 - struct refspec *rs =
953 - parse_push_refspec(nr_refspec, (const char **) refspec);
972 + struct refspec *rs;
973 int send_all = flags & MATCH_REFS_ALL;
974 int send_mirror = flags & MATCH_REFS_MIRROR;
975 + static const char *default_refspec[] = { ":", 0 };
976
977 + if (!nr_refspec) {
978 + nr_refspec = 1;
979 + refspec = default_refspec;
980 + }
981 + rs = parse_push_refspec(nr_refspec, (const char **) refspec);
982 if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
983 return -1;
984
@@ -964,48 +989,50 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
989 char *dst_name;
990 if (src->peer_ref)
991 continue;
967 - if (nr_refspec) {
968 - pat = check_pattern_match(rs, nr_refspec, src);
969 - if (!pat)
970 - continue;
971 - }
972 - else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
992 +
993 + pat = check_pattern_match(rs, nr_refspec, src);
994 + if (!pat)
995 + continue;
996 +
997 + if (pat->matching) {
998 /*
999 * "matching refs"; traditionally we pushed everything
1000 * including refs outside refs/heads/ hierarchy, but
1001 * that does not make much sense these days.
1002 */
978 - continue;
1003 + if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
1004 + continue;
1005 + dst_name = xstrdup(src->name);
1006
980 - if (pat) {
1007 + } else {
1008 const char *dst_side = pat->dst ? pat->dst : pat->src;
1009 dst_name = xmalloc(strlen(dst_side) +
1010 strlen(src->name) -
1011 strlen(pat->src) + 2);
1012 strcpy(dst_name, dst_side);
1013 strcat(dst_name, src->name + strlen(pat->src));
987 - } else
988 - dst_name = xstrdup(src->name);
1014 + }
1015 dst_peer = find_ref_by_name(dst, dst_name);
990 - if (dst_peer && dst_peer->peer_ref)
991 - /* We're already sending something to this ref. */
992 - goto free_name;
1016 + if (dst_peer) {
1017 + if (dst_peer->peer_ref)
1018 + /* We're already sending something to this ref. */
1019 + goto free_name;
1020 +
1021 + } else {
1022 + if (pat->matching && !(send_all || send_mirror))
1023 + /*
1024 + * Remote doesn't have it, and we have no
1025 + * explicit pattern, and we don't have
1026 + * --all nor --mirror.
1027 + */
1028 + goto free_name;
1029
994 - if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
995 - /*
996 - * Remote doesn't have it, and we have no
997 - * explicit pattern, and we don't have
998 - * --all nor --mirror.
999 - */
1000 - goto free_name;
1001 - if (!dst_peer) {
1030 /* Create a new one and link it */
1031 dst_peer = make_linked_ref(dst_name, dst_tail);
1032 hashcpy(dst_peer->new_sha1, src->new_sha1);
1033 }
1034 dst_peer->peer_ref = src;
1007 - if (pat)
1008 - dst_peer->force = pat->force;
1035 + dst_peer->force = pat->force;
1036 free_name:
1037 free(dst_name);
1038 }
remote.h
+1
@@ -46,6 +46,7 @@ int remote_has_url(struct remote *remote, const char *url);
46 struct refspec {
47 unsigned force : 1;
48 unsigned pattern : 1;
49 + unsigned matching : 1;
50
51 char *src;
52 char *dst;
t/t5511-refspec.sh
+4 -1
@@ -23,10 +23,13 @@ test_refspec () {
23 }
24
25 test_refspec push '' invalid
26 -test_refspec push ':' invalid
26 +test_refspec push ':'
27 +test_refspec push '::' invalid
28 +test_refspec push '+:'
29
30 test_refspec fetch ''
31 test_refspec fetch ':'
32 +test_refspec fetch '::' invalid
33
34 test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
35 test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid
t/t5516-fetch-push.sh
+41
@@ -165,6 +165,47 @@ test_expect_success 'push with matching heads' '
165
166 '
167
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 +
209 test_expect_success 'push with no ambiguity (1)' '
210
211 mk_test heads/master &&