From 454e2025a933593fd751475b59cc014887b4df6d Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:11 -0500 Subject: move duplicated get_local_heads() to remote.c get_local_heads() appears to have been copied from builtin-send-pack.c to http-push.c via cut and paste. This patch moves the function and its helper one_local_ref() to remote.c. The two copies of one_local_ref() were not identical. I used the more recent version from builtin-send-pack.c after confirming with Jeff King that it was an oversight that commit 30affa1e did not update both copies. This is in preparation for being able to call it from builtin-remote.c Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'remote.c') diff --git a/remote.c b/remote.c index d7079c6dd8..01aae770a9 100644 --- a/remote.c +++ b/remote.c @@ -1376,3 +1376,29 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base, num_ours, num_theirs); return 1; } + +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + struct ref ***local_tail = cb_data; + struct ref *ref; + int len; + + /* we already know it starts with refs/ to get here */ + if (check_ref_format(refname + 5)) + return 0; + + len = strlen(refname) + 1; + ref = xcalloc(1, sizeof(*ref) + len); + hashcpy(ref->new_sha1, sha1); + memcpy(ref->name, refname, len); + **local_tail = ref; + *local_tail = &ref->next; + return 0; +} + +struct ref *get_local_heads(void) +{ + struct ref *local_refs, **local_tail = &local_refs; + for_each_ref(one_local_ref, &local_tail); + return local_refs; +} -- cgit v1.2.1 From ec8452d5a797fca865666f761b785b04212426fc Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:12 -0500 Subject: move duplicated ref_newer() to remote.c ref_newer() appears to have been copied from builtin-send-pack.c to http-push.c via cut and paste. This patch moves the function and its helper unmark_and_free() to remote.c. There was a slight difference between the two implementations, one used TMP_MARK for the mark, the other used 1. Per Jeff King, I went with TMP_MARK as more correct. This is in preparation for being able to call it from builtin-remote.c Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'remote.c') diff --git a/remote.c b/remote.c index 01aae770a9..c8b7ea4ffa 100644 --- a/remote.c +++ b/remote.c @@ -5,6 +5,7 @@ #include "diff.h" #include "revision.h" #include "dir.h" +#include "tag.h" static struct refspec s_tag_refspec = { 0, @@ -1269,6 +1270,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) return 1; } +static void unmark_and_free(struct commit_list *list, unsigned int mark) +{ + while (list) { + struct commit_list *temp = list; + temp->item->object.flags &= ~mark; + list = temp->next; + free(temp); + } +} + +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1) +{ + struct object *o; + struct commit *old, *new; + struct commit_list *list, *used; + int found = 0; + + /* Both new and old must be commit-ish and new is descendant of + * old. Otherwise we require --force. + */ + o = deref_tag(parse_object(old_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + old = (struct commit *) o; + + o = deref_tag(parse_object(new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + new = (struct commit *) o; + + if (parse_commit(new) < 0) + return 0; + + used = list = NULL; + commit_list_insert(new, &list); + while (list) { + new = pop_most_recent_commit(&list, TMP_MARK); + commit_list_insert(new, &used); + if (new == old) { + found = 1; + break; + } + } + unmark_and_free(list, TMP_MARK); + unmark_and_free(used, TMP_MARK); + return found; +} + /* * Return true if there is anything to report, otherwise false. */ -- cgit v1.2.1 From 8ef517337dc684a333111b46d88c3217202f48c3 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:13 -0500 Subject: move locate_head() to remote.c Move locate_head() to remote.c and rename it to guess_remote_head() to more accurately reflect what it does. This is in preparation for being able to call it from builtin-remote.c Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'remote.c') diff --git a/remote.c b/remote.c index c8b7ea4ffa..49a183eb5a 100644 --- a/remote.c +++ b/remote.c @@ -1451,3 +1451,40 @@ struct ref *get_local_heads(void) for_each_ref(one_local_ref, &local_tail); return local_refs; } + +const struct ref *guess_remote_head(const struct ref *refs, + const struct ref *mapped_refs, + const struct ref **remote_head_p) +{ + const struct ref *remote_head = NULL; + const struct ref *remote_master = NULL; + const struct ref *r; + for (r = refs; r; r = r->next) + if (!strcmp(r->name, "HEAD")) + remote_head = r; + + for (r = mapped_refs; r; r = r->next) + if (!strcmp(r->name, "refs/heads/master")) + remote_master = r; + + if (remote_head_p) + *remote_head_p = remote_head; + + /* If there's no HEAD value at all, never mind. */ + if (!remote_head) + return NULL; + + /* If refs/heads/master could be right, it is. */ + if (remote_master && !hashcmp(remote_master->old_sha1, + remote_head->old_sha1)) + return remote_master; + + /* Look for another ref that points there */ + for (r = mapped_refs; r; r = r->next) + if (r != remote_head && + !hashcmp(r->old_sha1, remote_head->old_sha1)) + return r; + + /* Nothing is the same */ + return NULL; +} -- cgit v1.2.1 From 6cb4e6cc0f5b2de1998492b0178eeb0f99d4a800 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:14 -0500 Subject: remote: simplify guess_remote_head() This function had complications which made it hard to extend. - It used to do two things: find the HEAD ref, and then find a matching ref, optionally returning the former via assignment to a passed-in pointer. Since finding HEAD is a one-liner, just have a caller do it themselves and pass it as an argument. - It used to manually search through the ref list for refs/heads/master; this can be a one-line call to find_ref_by_name. Originally contributed by Jeff King along with the next commit as a single patch. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'remote.c') diff --git a/remote.c b/remote.c index 49a183eb5a..aed760ee3a 100644 --- a/remote.c +++ b/remote.c @@ -1452,37 +1452,22 @@ struct ref *get_local_heads(void) return local_refs; } -const struct ref *guess_remote_head(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref **remote_head_p) +const struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs) { - const struct ref *remote_head = NULL; - const struct ref *remote_master = NULL; const struct ref *r; - for (r = refs; r; r = r->next) - if (!strcmp(r->name, "HEAD")) - remote_head = r; - - for (r = mapped_refs; r; r = r->next) - if (!strcmp(r->name, "refs/heads/master")) - remote_master = r; - if (remote_head_p) - *remote_head_p = remote_head; - - /* If there's no HEAD value at all, never mind. */ - if (!remote_head) + if (!head) return NULL; /* If refs/heads/master could be right, it is. */ - if (remote_master && !hashcmp(remote_master->old_sha1, - remote_head->old_sha1)) - return remote_master; + r = find_ref_by_name(refs, "refs/heads/master"); + if (r && !hashcmp(r->old_sha1, head->old_sha1)) + return r; /* Look for another ref that points there */ - for (r = mapped_refs; r; r = r->next) - if (r != remote_head && - !hashcmp(r->old_sha1, remote_head->old_sha1)) + for (r = refs; r; r = r->next) + if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) return r; /* Nothing is the same */ -- cgit v1.2.1 From 7b3db095d53d19e08b27114d8706ff3be6693af7 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Fri, 27 Feb 2009 14:10:04 -0500 Subject: remote: make copy_ref() perform a deep copy To ensure that copied refs can always be freed w/o causing a double-free, make copy_ref() perform a deep copy. Also have copy_ref() return NULL if asked to copy NULL to simplify things for the caller. Background: currently copy_ref() performs a shallow copy. This is fine for current callers who never free the result and/or only copy refs which contain NULL pointers. But copy_ref() is about to gain a new caller (guess_remote_head()) which copies refs where peer_ref is not NULL and the caller of guess_remote_head() will want to free the result. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'remote.c') diff --git a/remote.c b/remote.c index aed760ee3a..22203ea8e2 100644 --- a/remote.c +++ b/remote.c @@ -779,10 +779,18 @@ struct ref *alloc_ref(const char *name) static struct ref *copy_ref(const struct ref *ref) { - struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); - memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); - ret->next = NULL; - return ret; + struct ref *cpy; + size_t len; + if (!ref) + return NULL; + len = strlen(ref->name); + cpy = xmalloc(sizeof(struct ref) + len + 1); + memcpy(cpy, ref, sizeof(struct ref) + len + 1); + cpy->next = NULL; + cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL; + cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL; + cpy->peer_ref = copy_ref(ref->peer_ref); + return cpy; } struct ref *copy_ref_list(const struct ref *ref) @@ -801,6 +809,7 @@ static void free_ref(struct ref *ref) { if (!ref) return; + free_ref(ref->peer_ref); free(ref->remote_status); free(ref->symref); free(ref); @@ -811,7 +820,6 @@ void free_refs(struct ref *ref) struct ref *next; while (ref) { next = ref->next; - free(ref->peer_ref); free_ref(ref); ref = next; } -- cgit v1.2.1 From 4229f1fa325870d6b24fe2a4c7d2ed5f14c6f771 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Fri, 27 Feb 2009 14:10:05 -0500 Subject: remote: let guess_remote_head() optionally return all matches Determining HEAD is ambiguous since it is done by comparing SHA1s. In the case of multiple matches we return refs/heads/master if it matches, else we return the first match we encounter. builtin-remote needs all matches returned to it, so add a flag for it to request such. To be simple and consistent, the return value is now a copy (including peer_ref) of the matching refs. Originally contributed by Jeff King along with the prior commit as a single patch. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'remote.c') diff --git a/remote.c b/remote.c index 22203ea8e2..304e967e3c 100644 --- a/remote.c +++ b/remote.c @@ -1460,24 +1460,33 @@ struct ref *get_local_heads(void) return local_refs; } -const struct ref *guess_remote_head(const struct ref *head, - const struct ref *refs) +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all) { const struct ref *r; + struct ref *list = NULL; + struct ref **tail = &list; if (!head) return NULL; /* If refs/heads/master could be right, it is. */ - r = find_ref_by_name(refs, "refs/heads/master"); - if (r && !hashcmp(r->old_sha1, head->old_sha1)) - return r; + if (!all) { + r = find_ref_by_name(refs, "refs/heads/master"); + if (r && !hashcmp(r->old_sha1, head->old_sha1)) + return copy_ref(r); + } /* Look for another ref that points there */ - for (r = refs; r; r = r->next) - if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) - return r; + for (r = refs; r; r = r->next) { + if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) { + *tail = copy_ref(r); + tail = &((*tail)->next); + if (!all) + break; + } + } - /* Nothing is the same */ - return NULL; + return list; } -- cgit v1.2.1 From cdf690e53b5f5af1ca8679b3f3e47ea198692c18 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:16 -0500 Subject: remote: make match_refs() copy src ref before assigning to peer_ref In some instances, match_refs() sets the peer_ref field of refs in the dst list such that it points to a ref in the src list. This prevents callers from freeing both the src and dst lists, as doing so would cause a double-free since free_refs() frees the peer_ref. As well, the following configuration causes two refs in the dst list to have the same peer_ref, which can also lead to a double-free: push = refs/heads/master:refs/heads/backup push = refs/heads/master:refs/heads/master Existing callers of match_heads() call it only once and then terminate, w/o ever bothering to free the src or dst lists, so this is not currently a problem. This patch modifies match_refs() to first copy any refs it plucks from the src list before assigning them as a peer_ref. This allows builtin-remote, a future caller, to free the src and dst lists. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'remote.c') diff --git a/remote.c b/remote.c index 304e967e3c..b7606acc47 100644 --- a/remote.c +++ b/remote.c @@ -936,6 +936,7 @@ static int match_explicit(struct ref *src, struct ref *dst, struct refspec *rs) { struct ref *matched_src, *matched_dst; + int copy_src; const char *dst_value = rs->dst; char *dst_guess; @@ -946,6 +947,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { case 1: + copy_src = 1; break; case 0: /* The source could be in the get_sha1() format @@ -955,6 +957,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = try_explicit_object_name(rs->src); if (!matched_src) return error("src refspec %s does not match any.", rs->src); + copy_src = 0; break; default: return error("src refspec %s matches more than one.", rs->src); @@ -1000,7 +1003,7 @@ static int match_explicit(struct ref *src, struct ref *dst, return error("dst ref %s receives from more than one src.", matched_dst->name); else { - matched_dst->peer_ref = matched_src; + matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src; matched_dst->force = rs->force; } return 0; @@ -1108,7 +1111,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } - dst_peer->peer_ref = src; + dst_peer->peer_ref = copy_ref(src); dst_peer->force = pat->force; free_name: free(dst_name); -- cgit v1.2.1 From 5f48cb95aa0d7311623df76249a1c8a1962550f5 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:17 -0500 Subject: remote: make match_refs() not short-circuit match_refs() returns non-zero if there is an error in match_explicit_refs(), without handling any remaining pattern ref specs. Its existing callers exit upon receiving non-zero, so a partial result is of no consequence to them; however a new caller, builtin-remote, is interested in the complete result even if there are errors in match_explicit_refs(). Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'remote.c') diff --git a/remote.c b/remote.c index b7606acc47..2123005d4b 100644 --- a/remote.c +++ b/remote.c @@ -1052,6 +1052,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + int errs; static const char *default_refspec[] = { ":", 0 }; if (!nr_refspec) { @@ -1059,8 +1060,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, 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; + errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec); /* pick the remainder */ for ( ; src; src = src->next) { @@ -1116,6 +1116,8 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, free_name: free(dst_name); } + if (errs) + return -1; return 0; } -- cgit v1.2.1 From fbb074c25352627f650d2ea528ed694e77bece0f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 27 Feb 2009 14:10:06 -0500 Subject: remote: make guess_remote_head() use exact HEAD lookup if it is available Our usual method for determining the ref pointed to by HEAD is to compare HEAD's sha1 to the sha1 of all refs, trying to find a unique match. However, some transports actually get to look at HEAD directly; we should make use of that information when it is available. Currently, only http remotes support this feature. Signed-off-by: Jeff King Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'remote.c') diff --git a/remote.c b/remote.c index 2123005d4b..9b8522db35 100644 --- a/remote.c +++ b/remote.c @@ -1476,6 +1476,14 @@ struct ref *guess_remote_head(const struct ref *head, if (!head) return NULL; + /* + * Some transports support directly peeking at + * where HEAD points; if that is the case, then + * we don't have to guess. + */ + if (head->symref) + return copy_ref(find_ref_by_name(refs, head->symref)); + /* If refs/heads/master could be right, it is. */ if (!all) { r = find_ref_by_name(refs, "refs/heads/master"); -- cgit v1.2.1