]> git.ipfire.org Git - thirdparty/git.git/blobdiff - connect.c
Renaming push.
[thirdparty/git.git] / connect.c
index a910af93d8ccf4305dd7e3400f4602184a7c6810..d913f296d3a153c377e88da10be46caed136d35d 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -31,11 +31,9 @@ struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **ma
                name = buffer + 41;
                if (nr_match && !path_match(name, nr_match, match))
                        continue;
-               ref = xmalloc(sizeof(*ref) + len - 40);
+               ref = xcalloc(1, sizeof(*ref) + len - 40);
                memcpy(ref->old_sha1, old_sha1, 20);
-               memset(ref->new_sha1, 0, 20);
                memcpy(ref->name, buffer + 41, len - 40);
-               ref->next = NULL;
                *list = ref;
                list = &ref->next;
        }
@@ -81,6 +79,174 @@ int path_match(const char *path, int nr, char **match)
        return 0;
 }
 
+struct refspec {
+       char *src;
+       char *dst;
+};
+
+static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
+{
+       int i;
+       struct refspec *rs = xmalloc(sizeof(*rs) * (nr_refspec + 1));
+       for (i = 0; i < nr_refspec; i++) {
+               char *sp, *dp, *ep;
+               sp = refspec[i];
+               ep = strchr(sp, ':');
+               if (ep) {
+                       dp = ep + 1;
+                       *ep = 0;
+               }
+               else
+                       dp = sp;
+               rs[i].src = sp;
+               rs[i].dst = dp;
+       }
+       rs[nr_refspec].src = rs[nr_refspec].dst = NULL;
+       return rs;
+}
+
+static int count_refspec_match(const char *pattern,
+                              struct ref *refs,
+                              struct ref **matched_ref)
+{
+       int match;
+       int patlen = strlen(pattern);
+
+       for (match = 0; refs; refs = refs->next) {
+               char *name = refs->name;
+               int namelen = strlen(name);
+               if (namelen < patlen ||
+                   memcmp(name + namelen - patlen, pattern, patlen))
+                       continue;
+               if (namelen != patlen && name[namelen - patlen - 1] != '/')
+                       continue;
+               match++;
+               *matched_ref = refs;
+       }
+       return match;
+}
+
+static void link_dst_tail(struct ref *ref, struct ref ***tail)
+{
+       **tail = ref;
+       *tail = &ref->next;
+       **tail = NULL;
+}
+
+static int match_explicit_refs(struct ref *src, struct ref *dst,
+                              struct ref ***dst_tail, struct refspec *rs)
+{
+       int i, errs;
+       for (i = errs = 0; rs[i].src; i++) {
+               struct ref *matched_src, *matched_dst;
+
+               matched_src = matched_dst = NULL;
+               switch (count_refspec_match(rs[i].src, src, &matched_src)) {
+               case 1:
+                       break;
+               case 0:
+                       errs = 1;
+                       error("src refspec %s does not match any.");
+                       break;
+               default:
+                       errs = 1;
+                       error("src refspec %s matches more than one.",
+                             rs[i].src);
+                       break;
+               }
+               switch (count_refspec_match(rs[i].dst, dst, &matched_dst)) {
+               case 1:
+                       break;
+               case 0:
+                       if (!memcmp(rs[i].dst, "refs/", 5)) {
+                               int len = strlen(rs[i].dst) + 1;
+                               matched_dst = xcalloc(1, sizeof(*dst) + len);
+                               memcpy(matched_dst->name, rs[i].dst, len);
+                               link_dst_tail(matched_dst, dst_tail);
+                       }
+                       else if (!strcmp(rs[i].src, rs[i].dst) &&
+                                matched_src) {
+                               /* pushing "master:master" when
+                                * remote does not have master yet.
+                                */
+                               int len = strlen(matched_src->name);
+                               matched_dst = xcalloc(1, sizeof(*dst) + len);
+                               memcpy(matched_dst->name, matched_src->name,
+                                      len);
+                               link_dst_tail(matched_dst, dst_tail);
+                       }
+                       else {
+                               errs = 1;
+                               error("dst refspec %s does not match any "
+                                     "existing ref on the remote and does "
+                                     "not start with refs/.", rs[i].dst);
+                       }
+                       break;
+               default:
+                       errs = 1;
+                       error("dst refspec %s matches more than one.",
+                             rs[i].dst);
+                       break;
+               }
+               if (errs)
+                       continue;
+               if (matched_src->peer_ref) {
+                       errs = 1;
+                       error("src ref %s is sent to more than one dst.",
+                             matched_src->name);
+               }
+               else
+                       matched_src->peer_ref = matched_dst;
+               if (matched_dst->peer_ref) {
+                       errs = 1;
+                       error("dst ref %s receives from more than one src.",
+                             matched_dst->name);
+               }
+               else
+                       matched_dst->peer_ref = matched_src;
+       }
+       return -errs;
+}
+
+static struct ref *find_ref_by_name(struct ref *list, const char *name)
+{
+       for ( ; list; list = list->next)
+               if (!strcmp(list->name, name))
+                       return list;
+       return NULL;
+}
+
+int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
+              int nr_refspec, char **refspec, int all)
+{
+       struct refspec *rs = parse_ref_spec(nr_refspec, refspec);
+
+       if (nr_refspec)
+               return match_explicit_refs(src, dst, dst_tail, rs);
+
+       /* pick the remainder */
+       for ( ; src; src = src->next) {
+               struct ref *dst_peer;
+               if (src->peer_ref)
+                       continue;
+               dst_peer = find_ref_by_name(dst, src->name);
+               if (dst_peer && dst_peer->peer_ref)
+                       continue;
+               if (!dst_peer) {
+                       if (!all)
+                               continue;
+                       /* Create a new one and link it */
+                       int len = strlen(src->name) + 1;
+                       dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
+                       memcpy(dst_peer->name, src->name, len);
+                       memcpy(dst_peer->new_sha1, src->new_sha1, 20);
+                       link_dst_tail(dst_peer, dst_tail);
+               }
+               dst_peer->peer_ref = src;
+       }
+       return 0;
+}
+
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,