Navigation Menu

Skip to content

Commit

Permalink
add special "matching refs" refspec
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bonzini authored and gitster committed May 5, 2008
1 parent c697ad1 commit a83619d
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 35 deletions.
13 changes: 7 additions & 6 deletions Documentation/git-push.txt
Expand Up @@ -46,19 +46,20 @@ specified, the same ref that <src> referred to locally). If
the optional leading plus `+` is used, the remote ref is updated
even if it does not result in a fast forward update.
+
Note: If no explicit refspec is found, (that is neither
on the command line nor in any Push line of the
corresponding remotes file---see below), then "matching" heads are
pushed: for every head that exists on the local side, the remote side is
updated if a head of the same name already exists on the remote side.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
A parameter <ref> without a colon pushes the <ref> from the source
repository to the destination repository under the same name.
+
Pushing an empty <src> allows you to delete the <dst> ref from
the remote repository.
+
The special refspec `:` (or `+:` to allow non-fast forward updates)
directs git to push "matching" heads: for every head that exists on
the local side, the remote side is updated if a head of the same name
already exists on the remote side. This is the default operation mode
if no explicit refspec is found (that is neither on the command line
nor in any Push line of the corresponding remotes file---see below).

\--all::
Instead of naming each ref to push, specifies that all
Expand Down
10 changes: 9 additions & 1 deletion builtin-send-pack.c
Expand Up @@ -537,9 +537,17 @@ static void verify_remote_names(int nr_heads, const char **heads)
int i;

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

remote = remote ? (remote + 1) : heads[i];
if (*local == '+')
local++;

/* A matching refspec is okay. */
if (remote == local && remote[1] == '\0')
continue;

remote = remote ? (remote + 1) : local;
switch (check_ref_format(remote)) {
case 0: /* ok */
case CHECK_REF_FORMAT_ONELEVEL:
Expand Down
81 changes: 54 additions & 27 deletions remote.c
Expand Up @@ -429,6 +429,16 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
}

rhs = strrchr(lhs, ':');

/*
* Before going on, special case ":" (or "+:") as a refspec
* for matching refs.
*/
if (!fetch && rhs == lhs && rhs[1] == '\0') {
rs[i].matching = 1;
continue;
}

if (rhs) {
rhs++;
rlen = strlen(rhs);
Expand Down Expand Up @@ -842,7 +852,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
const char *dst_value = rs->dst;
char *dst_guess;

if (rs->pattern)
if (rs->pattern || rs->matching)
return errs;

matched_src = matched_dst = NULL;
Expand Down Expand Up @@ -932,13 +942,23 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
const struct ref *src)
{
int i;
int matching_refs = -1;
for (i = 0; i < rs_nr; i++) {
if (rs[i].matching &&
(matching_refs == -1 || rs[i].force)) {
matching_refs = i;
continue;
}

if (rs[i].pattern &&
!prefixcmp(src->name, rs[i].src) &&
src->name[strlen(rs[i].src)] == '/')
return rs + i;
}
return NULL;
if (matching_refs != -1)
return rs + matching_refs;
else
return NULL;
}

/*
Expand All @@ -949,11 +969,16 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
int nr_refspec, const char **refspec, int flags)
{
struct refspec *rs =
parse_push_refspec(nr_refspec, (const char **) refspec);
struct refspec *rs;
int send_all = flags & MATCH_REFS_ALL;
int send_mirror = flags & MATCH_REFS_MIRROR;
static const char *default_refspec[] = { ":", 0 };

if (!nr_refspec) {
nr_refspec = 1;
refspec = default_refspec;
}
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
return -1;

Expand All @@ -964,48 +989,50 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
char *dst_name;
if (src->peer_ref)
continue;
if (nr_refspec) {
pat = check_pattern_match(rs, nr_refspec, src);
if (!pat)
continue;
}
else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))

pat = check_pattern_match(rs, nr_refspec, src);
if (!pat)
continue;

if (pat->matching) {
/*
* "matching refs"; traditionally we pushed everything
* including refs outside refs/heads/ hierarchy, but
* that does not make much sense these days.
*/
continue;
if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
continue;
dst_name = xstrdup(src->name);

if (pat) {
} else {
const char *dst_side = pat->dst ? pat->dst : pat->src;
dst_name = xmalloc(strlen(dst_side) +
strlen(src->name) -
strlen(pat->src) + 2);
strcpy(dst_name, dst_side);
strcat(dst_name, src->name + strlen(pat->src));
} else
dst_name = xstrdup(src->name);
}
dst_peer = find_ref_by_name(dst, dst_name);
if (dst_peer && dst_peer->peer_ref)
/* We're already sending something to this ref. */
goto free_name;
if (dst_peer) {
if (dst_peer->peer_ref)
/* We're already sending something to this ref. */
goto free_name;

} else {
if (pat->matching && !(send_all || send_mirror))
/*
* Remote doesn't have it, and we have no
* explicit pattern, and we don't have
* --all nor --mirror.
*/
goto free_name;

if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
/*
* Remote doesn't have it, and we have no
* explicit pattern, and we don't have
* --all nor --mirror.
*/
goto free_name;
if (!dst_peer) {
/* Create a new one and link it */
dst_peer = make_linked_ref(dst_name, dst_tail);
hashcpy(dst_peer->new_sha1, src->new_sha1);
}
dst_peer->peer_ref = src;
if (pat)
dst_peer->force = pat->force;
dst_peer->force = pat->force;
free_name:
free(dst_name);
}
Expand Down
1 change: 1 addition & 0 deletions remote.h
Expand Up @@ -46,6 +46,7 @@ int remote_has_url(struct remote *remote, const char *url);
struct refspec {
unsigned force : 1;
unsigned pattern : 1;
unsigned matching : 1;

char *src;
char *dst;
Expand Down
5 changes: 4 additions & 1 deletion t/t5511-refspec.sh
Expand Up @@ -23,10 +23,13 @@ test_refspec () {
}

test_refspec push '' invalid
test_refspec push ':' invalid
test_refspec push ':'
test_refspec push '::' invalid
test_refspec push '+:'

test_refspec fetch ''
test_refspec fetch ':'
test_refspec fetch '::' invalid

test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid
Expand Down
41 changes: 41 additions & 0 deletions t/t5516-fetch-push.sh
Expand Up @@ -165,6 +165,47 @@ test_expect_success 'push with matching heads' '
'

test_expect_success 'push with matching heads on the command line' '
mk_test heads/master &&
git push testrepo : &&
check_push_result $the_commit heads/master
'

test_expect_success 'failed (non-fast-forward) push with matching heads' '
mk_test heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
! git push testrepo &&
check_push_result $the_commit heads/master &&
git reset --hard $the_commit
'

test_expect_success 'push --force with matching heads' '
mk_test heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
git push --force testrepo &&
! check_push_result $the_commit heads/master &&
git reset --hard $the_commit
'

test_expect_success 'push with matching heads and forced update' '
mk_test heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
git push testrepo +: &&
! check_push_result $the_commit heads/master &&
git reset --hard $the_commit
'

test_expect_success 'push with no ambiguity (1)' '
mk_test heads/master &&
Expand Down

0 comments on commit a83619d

Please sign in to comment.