Code

alloc_ref_from_str(): factor out a common pattern of alloc_ref from string
[git.git] / remote.c
index 04f7521c29d10abfa4290293e1f6319990e1c0c3..91cbb72ddeba1c60dab7aad2105e1c004ea6e198 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -232,7 +232,7 @@ static void read_branches_file(struct remote *remote)
 {
        const char *slash = strchr(remote->name, '/');
        char *frag;
-       char *branch;
+       struct strbuf branch;
        int n = slash ? slash - remote->name : 1000;
        FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
        char *s, *p;
@@ -258,17 +258,33 @@ static void read_branches_file(struct remote *remote)
        strcpy(p, s);
        if (slash)
                strcat(p, slash);
+
+       /*
+        * With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
+        * reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
+        * the partial URL obtained from the branches file plus
+        * "/netdev-2.6" and does not store it in any tracking ref.
+        * #branch specifier in the file is ignored.
+        *
+        * Otherwise, the branches file would have URL and optionally
+        * #branch specified.  The "master" (or specified) branch is
+        * fetched and stored in the local branch of the same name.
+        */
+       strbuf_init(&branch, 0);
        frag = strchr(p, '#');
        if (frag) {
                *(frag++) = '\0';
-               branch = xmalloc(strlen(frag) + 12);
-               strcpy(branch, "refs/heads/");
-               strcat(branch, frag);
+               strbuf_addf(&branch, "refs/heads/%s", frag);
+       } else
+               strbuf_addstr(&branch, "refs/heads/master");
+       if (!slash) {
+               strbuf_addf(&branch, ":refs/heads/%s", remote->name);
        } else {
-               branch = "refs/heads/master";
+               strbuf_reset(&branch);
+               strbuf_addstr(&branch, "HEAD:");
        }
        add_url_alias(remote, p);
-       add_fetch_refspec(remote, branch);
+       add_fetch_refspec(remote, strbuf_detach(&branch, 0));
        remote->fetch_tags = 1; /* always auto-follow */
 }
 
@@ -299,7 +315,7 @@ static int handle_config(const char *key, const char *value)
        }
        if (!prefixcmp(key, "url.")) {
                struct rewrite *rewrite;
-               name = key + 5;
+               name = key + 4;
                subkey = strrchr(name, '.');
                if (!subkey)
                        return 0;
@@ -321,44 +337,49 @@ static int handle_config(const char *key, const char *value)
                return 0;
        }
        remote = make_remote(name, subkey - name);
-       if (!value) {
-               /* if we ever have a boolean variable, e.g. "remote.*.disabled"
-                * [remote "frotz"]
-                *      disabled
-                * is a valid way to set it to true; we get NULL in value so
-                * we need to handle it here.
-                *
-                * if (!strcmp(subkey, ".disabled")) {
-                *      val = git_config_bool(key, value);
-                *      return 0;
-                * } else
-                *
-                */
-               return 0; /* ignore unknown booleans */
-       }
-       if (!strcmp(subkey, ".url")) {
-               add_url(remote, xstrdup(value));
+       if (!strcmp(subkey, ".mirror"))
+               remote->mirror = git_config_bool(key, value);
+       else if (!strcmp(subkey, ".skipdefaultupdate"))
+               remote->skip_default_update = git_config_bool(key, value);
+
+       else if (!strcmp(subkey, ".url")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_url(remote, v);
        } else if (!strcmp(subkey, ".push")) {
-               add_push_refspec(remote, xstrdup(value));
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_push_refspec(remote, v);
        } else if (!strcmp(subkey, ".fetch")) {
-               add_fetch_refspec(remote, xstrdup(value));
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_fetch_refspec(remote, v);
        } else if (!strcmp(subkey, ".receivepack")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
                if (!remote->receivepack)
-                       remote->receivepack = xstrdup(value);
+                       remote->receivepack = v;
                else
                        error("more than one receivepack given, using the first");
        } else if (!strcmp(subkey, ".uploadpack")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
                if (!remote->uploadpack)
-                       remote->uploadpack = xstrdup(value);
+                       remote->uploadpack = v;
                else
                        error("more than one uploadpack given, using the first");
        } else if (!strcmp(subkey, ".tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
        } else if (!strcmp(subkey, ".proxy")) {
-               remote->http_proxy = xstrdup(value);
-       } else if (!strcmp(subkey, ".skipdefaultupdate"))
-               remote->skip_default_update = 1;
+               return git_config_string((const char **)&remote->http_proxy,
+                                        key, value);
+       }
        return 0;
 }
 
@@ -393,7 +414,7 @@ static void read_config(void)
        alias_all_urls();
 }
 
-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
        int i;
        int st;
@@ -503,17 +524,32 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
        return rs;
 
  invalid:
+       if (verify) {
+               free(rs);
+               return NULL;
+       }
        die("Invalid refspec '%s'", refspec[i]);
 }
 
+int valid_fetch_refspec(const char *fetch_refspec_str)
+{
+       const char *fetch_refspec[] = { fetch_refspec_str };
+       struct refspec *refspec;
+
+       refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+       if (refspec)
+               free(refspec);
+       return !!refspec;
+}
+
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
 {
-       return parse_refspec_internal(nr_refspec, refspec, 1);
+       return parse_refspec_internal(nr_refspec, refspec, 1, 0);
 }
 
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 {
-       return parse_refspec_internal(nr_refspec, refspec, 0);
+       return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
 
 static int valid_remote_nick(const char *name)
@@ -655,6 +691,13 @@ struct ref *alloc_ref(unsigned namelen)
        return ret;
 }
 
+struct ref *alloc_ref_from_str(const char* str)
+{
+       struct ref *ret = alloc_ref(strlen(str) + 1);
+       strcpy(ret->name, str);
+       return ret;
+}
+
 static struct ref *copy_ref(const struct ref *ref)
 {
        struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
@@ -675,13 +718,22 @@ struct ref *copy_ref_list(const struct ref *ref)
        return ret;
 }
 
+void free_ref(struct ref *ref)
+{
+       if (!ref)
+               return;
+       free(ref->remote_status);
+       free(ref->symref);
+       free(ref);
+}
+
 void free_refs(struct ref *ref)
 {
        struct ref *next;
        while (ref) {
                next = ref->next;
                free(ref->peer_ref);
-               free(ref);
+               free_ref(ref);
                ref = next;
        }
 }
@@ -752,7 +804,6 @@ static struct ref *try_explicit_object_name(const char *name)
 {
        unsigned char sha1[20];
        struct ref *ref;
-       int len;
 
        if (!*name) {
                ref = alloc_ref(20);
@@ -762,25 +813,38 @@ static struct ref *try_explicit_object_name(const char *name)
        }
        if (get_sha1(name, sha1))
                return NULL;
-       len = strlen(name) + 1;
-       ref = alloc_ref(len);
-       memcpy(ref->name, name, len);
+       ref = alloc_ref_from_str(name);
        hashcpy(ref->new_sha1, sha1);
        return ref;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       struct ref *ret;
-       size_t len;
-
-       len = strlen(name) + 1;
-       ret = alloc_ref(len);
-       memcpy(ret->name, name, len);
+       struct ref *ret = alloc_ref_from_str(name);
        tail_link_ref(ret, tail);
        return ret;
 }
 
+static char *guess_ref(const char *name, struct ref *peer)
+{
+       struct strbuf buf = STRBUF_INIT;
+       unsigned char sha1[20];
+
+       const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+       if (!r)
+               return NULL;
+
+       if (!prefixcmp(r, "refs/heads/"))
+               strbuf_addstr(&buf, "refs/heads/");
+       else if (!prefixcmp(r, "refs/tags/"))
+               strbuf_addstr(&buf, "refs/tags/");
+       else
+               return NULL;
+
+       strbuf_addstr(&buf, name);
+       return strbuf_detach(&buf, NULL);
+}
+
 static int match_explicit(struct ref *src, struct ref *dst,
                          struct ref ***dst_tail,
                          struct refspec *rs,
@@ -789,6 +853,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        struct ref *matched_src, *matched_dst;
 
        const char *dst_value = rs->dst;
+       char *dst_guess;
 
        if (rs->pattern)
                return errs;
@@ -835,10 +900,15 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 0:
                if (!memcmp(dst_value, "refs/", 5))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
+               else if((dst_guess = guess_ref(dst_value, matched_src)))
+                       matched_dst = make_linked_ref(dst_guess, dst_tail);
                else
-                       error("dst refspec %s does not match any "
-                             "existing ref on the remote and does "
-                             "not start with refs/.", dst_value);
+                       error("unable to push to unqualified destination: %s\n"
+                             "The destination refspec neither matches an "
+                             "existing ref on the remote nor\n"
+                             "begins with refs/, and we are unable to "
+                             "guess a prefix based on the source ref.",
+                             dst_value);
                break;
        default:
                matched_dst = NULL;
@@ -1054,9 +1124,7 @@ static struct ref *get_local_ref(const char *name)
                return NULL;
 
        if (!prefixcmp(name, "refs/")) {
-               ret = alloc_ref(strlen(name) + 1);
-               strcpy(ret->name, name);
-               return ret;
+               return alloc_ref_from_str(name);
        }
 
        if (!prefixcmp(name, "heads/") ||
@@ -1115,3 +1183,15 @@ int get_fetch_map(const struct ref *remote_refs,
 
        return 0;
 }
+
+int resolve_remote_symref(struct ref *ref, struct ref *list)
+{
+       if (!ref->symref)
+               return 0;
+       for (; list; list = list->next)
+               if (!strcmp(ref->symref, list->name)) {
+                       hashcpy(ref->old_sha1, list->old_sha1);
+                       return 0;
+               }
+       return 1;
+}