Skip to content

Commit

Permalink
Merge branch 'pb/push'
Browse files Browse the repository at this point in the history
* pb/push:
  add special "matching refs" refspec
  • Loading branch information
Junio C Hamano committed May 23, 2008
2 parents e5e9714 + a83619d commit 182fb4d
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,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 @@ -855,7 +865,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 @@ -945,13 +955,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 @@ -962,11 +982,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 @@ -977,48 +1002,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
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 182fb4d

Please sign in to comment.