Sync with maint to grab trivial doc fixes
[gitweb.git] / remote.c
index ca1edd901e4e64cb497b790f675537283c4ee1c0..efcba931eca963bd6a5fd13f01a4859e0ae9e14d 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -49,6 +49,7 @@ static int branches_nr;
 
 static struct branch *current_branch;
 static const char *default_remote_name;
+static const char *pushremote_name;
 static int explicit_default_remote_name;
 
 static struct rewrites rewrites;
@@ -275,10 +276,9 @@ static void read_remotes_file(struct remote *remote)
 
 static void read_branches_file(struct remote *remote)
 {
-       const char *slash = strchr(remote->name, '/');
        char *frag;
        struct strbuf branch = STRBUF_INIT;
-       int n = slash ? slash - remote->name : 1000;
+       int n = 1000;
        FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
        char *s, *p;
        int len;
@@ -298,21 +298,11 @@ static void read_branches_file(struct remote *remote)
        while (isspace(p[-1]))
                *--p = 0;
        len = p - s;
-       if (slash)
-               len += strlen(slash);
        p = xmalloc(len + 1);
        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
+        * 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.
         */
@@ -322,12 +312,8 @@ static void read_branches_file(struct remote *remote)
                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 {
-               strbuf_reset(&branch);
-               strbuf_addstr(&branch, "HEAD:");
-       }
+
+       strbuf_addf(&branch, ":refs/heads/%s", remote->name);
        add_url_alias(remote, p);
        add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
        /*
@@ -357,13 +343,16 @@ static int handle_config(const char *key, const char *value, void *cb)
                        return 0;
                branch = make_branch(name, subkey - name);
                if (!strcmp(subkey, ".remote")) {
-                       if (!value)
-                               return config_error_nonbool(key);
-                       branch->remote_name = xstrdup(value);
+                       if (git_config_string(&branch->remote_name, key, value))
+                               return -1;
                        if (branch == current_branch) {
                                default_remote_name = branch->remote_name;
                                explicit_default_remote_name = 1;
                        }
+               } else if (!strcmp(subkey, ".pushremote")) {
+                       if (branch == current_branch)
+                               if (git_config_string(&pushremote_name, key, value))
+                                       return -1;
                } else if (!strcmp(subkey, ".merge")) {
                        if (!value)
                                return config_error_nonbool(key);
@@ -389,9 +378,16 @@ static int handle_config(const char *key, const char *value, void *cb)
                        add_instead_of(rewrite, xstrdup(value));
                }
        }
+
        if (prefixcmp(key,  "remote."))
                return 0;
        name = key + 7;
+
+       /* Handle remote.* variables */
+       if (!strcmp(name, "pushdefault"))
+               return git_config_string(&pushremote_name, key, value);
+
+       /* Handle remote.<name>.* variables */
        if (*name == '/') {
                warning("Config remote shorthand cannot begin with '/': %s",
                        name);
@@ -671,17 +667,21 @@ static int valid_remote_nick(const char *name)
        return !strchr(name, '/'); /* no slash */
 }
 
-struct remote *remote_get(const char *name)
+static struct remote *remote_get_1(const char *name, const char *pushremote_name)
 {
        struct remote *ret;
        int name_given = 0;
 
-       read_config();
        if (name)
                name_given = 1;
        else {
-               name = default_remote_name;
-               name_given = explicit_default_remote_name;
+               if (pushremote_name) {
+                       name = pushremote_name;
+                       name_given = 1;
+               } else {
+                       name = default_remote_name;
+                       name_given = explicit_default_remote_name;
+               }
        }
 
        ret = make_remote(name, 0);
@@ -700,6 +700,18 @@ struct remote *remote_get(const char *name)
        return ret;
 }
 
+struct remote *remote_get(const char *name)
+{
+       read_config();
+       return remote_get_1(name, NULL);
+}
+
+struct remote *pushremote_get(const char *name)
+{
+       read_config();
+       return remote_get_1(name, pushremote_name);
+}
+
 int remote_is_configured(const char *name)
 {
        int i;
@@ -1290,6 +1302,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        free(sent_tips.tip);
 }
 
+static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
+{
+       for ( ; ref; ref = ref->next)
+               string_list_append_nodup(ref_index, ref->name)->util = ref;
+
+       sort_string_list(ref_index);
+}
+
 /*
  * Given the set of refs the local repository has, the set of refs the
  * remote repository has, and the refspec used for push, determine
@@ -1308,6 +1328,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
        int errs;
        static const char *default_refspec[] = { ":", NULL };
        struct ref *ref, **dst_tail = tail_ref(dst);
+       struct string_list dst_ref_index = STRING_LIST_INIT_NODUP;
 
        if (!nr_refspec) {
                nr_refspec = 1;
@@ -1318,6 +1339,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
        /* pick the remainder */
        for (ref = src; ref; ref = ref->next) {
+               struct string_list_item *dst_item;
                struct ref *dst_peer;
                const struct refspec *pat = NULL;
                char *dst_name;
@@ -1326,7 +1348,11 @@ int match_push_refs(struct ref *src, struct ref **dst,
                if (!dst_name)
                        continue;
 
-               dst_peer = find_ref_by_name(*dst, dst_name);
+               if (!dst_ref_index.nr)
+                       prepare_ref_index(&dst_ref_index, *dst);
+
+               dst_item = string_list_lookup(&dst_ref_index, dst_name);
+               dst_peer = dst_item ? dst_item->util : NULL;
                if (dst_peer) {
                        if (dst_peer->peer_ref)
                                /* We're already sending something to this ref. */
@@ -1343,6 +1369,8 @@ int match_push_refs(struct ref *src, struct ref **dst,
                        /* Create a new one and link it */
                        dst_peer = make_linked_ref(dst_name, &dst_tail);
                        hashcpy(dst_peer->new_sha1, ref->new_sha1);
+                       string_list_insert(&dst_ref_index,
+                               dst_peer->name)->util = dst_peer;
                }
                dst_peer->peer_ref = copy_ref(ref);
                dst_peer->force = pat->force;
@@ -1350,10 +1378,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
                free(dst_name);
        }
 
+       string_list_clear(&dst_ref_index, 0);
+
        if (flags & MATCH_REFS_FOLLOW_TAGS)
                add_missing_tags(src, dst, &dst_tail);
 
        if (send_prune) {
+               struct string_list src_ref_index = STRING_LIST_INIT_NODUP;
                /* check for missing refs on the remote */
                for (ref = *dst; ref; ref = ref->next) {
                        char *src_name;
@@ -1364,11 +1395,15 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
                        src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
                        if (src_name) {
-                               if (!find_ref_by_name(src, src_name))
+                               if (!src_ref_index.nr)
+                                       prepare_ref_index(&src_ref_index, src);
+                               if (!string_list_has_string(&src_ref_index,
+                                           src_name))
                                        ref->peer_ref = alloc_delete_ref();
                                free(src_name);
                        }
                }
+               string_list_clear(&src_ref_index, 0);
        }
        if (errs)
                return -1;
@@ -1447,8 +1482,7 @@ struct branch *branch_get(const char *name)
                ret->remote = remote_get(ret->remote_name);
                if (ret->merge_nr) {
                        int i;
-                       ret->merge = xcalloc(sizeof(*ret->merge),
-                                            ret->merge_nr);
+                       ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
                        for (i = 0; i < ret->merge_nr; i++) {
                                ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
                                ret->merge[i]->src = xstrdup(ret->merge_name[i]);