Code

Add matching and parsing for fetch-side refspec rules
authorDaniel Barkalow <barkalow@iabervon.org>
Tue, 11 Sep 2007 03:03:08 +0000 (23:03 -0400)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Sep 2007 10:22:30 +0000 (03:22 -0700)
Also exports parse_ref_spec().

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
remote.c
remote.h

index 711aa04ce157e59cece118ff2a77a558e93e2df5..df91b2ff990f42d6b1bf115a3bdc99041c2dc8bb 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -208,6 +208,7 @@ static void read_branches_file(struct remote *remote)
        }
        add_uri(remote, p);
        add_fetch_refspec(remote, branch);
+       remote->fetch_tags = 1; /* always auto-follow */
 }
 
 static int handle_config(const char *key, const char *value)
@@ -274,6 +275,9 @@ static int handle_config(const char *key, const char *value)
                        remote->uploadpack = xstrdup(value);
                else
                        error("more than one uploadpack given, using the first");
+       } else if (!strcmp(subkey, ".tagopt")) {
+               if (!strcmp(value, "--no-tags"))
+                       remote->fetch_tags = -1;
        }
        return 0;
 }
@@ -296,7 +300,7 @@ static void read_config(void)
        git_config(handle_config);
 }
 
-static struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
+struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
 {
        int i;
        struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
@@ -352,6 +356,10 @@ struct remote *remote_get(const char *name)
                add_uri(ret, name);
        if (!ret->uri)
                return NULL;
+       if (!strcmp(name, ".")) {
+               // we always fetch "refs/*:refs/*", which is trivial
+               add_fetch_refspec(ret, "refs/*:refs/*");
+       }
        ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
        ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec);
        return ret;
@@ -454,6 +462,14 @@ struct ref *alloc_ref(unsigned namelen)
        return ret;
 }
 
+static struct ref *copy_ref(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;
+}
+
 void free_refs(struct ref *ref)
 {
        struct ref *next;
@@ -771,3 +787,110 @@ int branch_merges(struct branch *branch, const char *refname)
        }
        return 0;
 }
+
+static struct ref *get_expanded_map(struct ref *remote_refs,
+                                   const struct refspec *refspec)
+{
+       struct ref *ref;
+       struct ref *ret = NULL;
+       struct ref **tail = &ret;
+
+       int remote_prefix_len = strlen(refspec->src);
+       int local_prefix_len = strlen(refspec->dst);
+
+       for (ref = remote_refs; ref; ref = ref->next) {
+               if (strchr(ref->name, '^'))
+                       continue; /* a dereference item */
+               if (!prefixcmp(ref->name, refspec->src)) {
+                       char *match;
+                       struct ref *cpy = copy_ref(ref);
+                       match = ref->name + remote_prefix_len;
+
+                       cpy->peer_ref = alloc_ref(local_prefix_len +
+                                                 strlen(match) + 1);
+                       sprintf(cpy->peer_ref->name, "%s%s",
+                               refspec->dst, match);
+                       if (refspec->force)
+                               cpy->peer_ref->force = 1;
+                       *tail = cpy;
+                       tail = &cpy->next;
+               }
+       }
+
+       return ret;
+}
+
+static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
+{
+       struct ref *ref;
+       for (ref = refs; ref; ref = ref->next) {
+               if (ref_matches_abbrev(name, ref->name))
+                       return ref;
+       }
+       return NULL;
+}
+
+struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
+{
+       struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
+
+       if (!ref)
+               die("Couldn't find remote ref %s\n", name);
+
+       return copy_ref(ref);
+}
+
+static struct ref *get_local_ref(const char *name)
+{
+       struct ref *ret;
+       if (!name)
+               return NULL;
+
+       if (!prefixcmp(name, "refs/")) {
+               ret = alloc_ref(strlen(name) + 1);
+               strcpy(ret->name, name);
+               return ret;
+       }
+
+       if (!prefixcmp(name, "heads/") ||
+           !prefixcmp(name, "tags/") ||
+           !prefixcmp(name, "remotes/")) {
+               ret = alloc_ref(strlen(name) + 6);
+               sprintf(ret->name, "refs/%s", name);
+               return ret;
+       }
+
+       ret = alloc_ref(strlen(name) + 12);
+       sprintf(ret->name, "refs/heads/%s", name);
+       return ret;
+}
+
+int get_fetch_map(struct ref *remote_refs,
+                 const struct refspec *refspec,
+                 struct ref ***tail)
+{
+       struct ref *ref_map, *rm;
+
+       if (refspec->pattern) {
+               ref_map = get_expanded_map(remote_refs, refspec);
+       } else {
+               ref_map = get_remote_ref(remote_refs,
+                                        refspec->src[0] ?
+                                        refspec->src : "HEAD");
+
+               ref_map->peer_ref = get_local_ref(refspec->dst);
+
+               if (refspec->force)
+                       ref_map->peer_ref->force = 1;
+       }
+
+       for (rm = ref_map; rm; rm = rm->next) {
+               if (rm->peer_ref && check_ref_format(rm->peer_ref->name + 5))
+                       die("* refusing to create funny ref '%s' locally",
+                           rm->peer_ref->name);
+       }
+
+       tail_link_ref(ref_map, tail);
+
+       return 0;
+}
index f21b6a6da1f85eb0818ea55ad43368a1e7cf5e33..89940527ad989ca7ab20794cfe7dba910cb36e34 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -15,6 +15,14 @@ struct remote {
        struct refspec *fetch;
        int fetch_refspec_nr;
 
+       /*
+        * -1 to never fetch tags
+        * 0 to auto-follow tags on heuristic (default)
+        * 1 to always auto-follow tags
+        * 2 to always fetch tags
+        */
+       int fetch_tags;
+
        const char *receivepack;
        const char *uploadpack;
 };
@@ -41,9 +49,25 @@ struct ref *alloc_ref(unsigned namelen);
  */
 void free_refs(struct ref *ref);
 
+struct refspec *parse_ref_spec(int nr_refspec, const char **refspec);
+
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
               int nr_refspec, char **refspec, int all);
 
+/*
+ * Given a list of the remote refs and the specification of things to
+ * fetch, makes a (separate) list of the refs to fetch and the local
+ * refs to store into.
+ *
+ * *tail is the pointer to the tail pointer of the list of results
+ * beforehand, and will be set to the tail pointer of the list of
+ * results afterward.
+ */
+int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec,
+                 struct ref ***tail);
+
+struct ref *get_remote_ref(struct ref *remote_refs, const char *name);
+
 /*
  * For the given remote, reads the refspec's src and sets the other fields.
  */