Merge branch 'jc/sha1-name-find-fix' into maint
[gitweb.git] / builtin / remote.c
index 0e99a9957dad39ded17ce98de49b164fdb70eacd..6699bc571235167f5cae60106522c09a10ae341d 100644 (file)
@@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+       "git remote set-branches <name> [--add] <branch>...",
        "git remote set-url <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
@@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
        NULL
 };
 
+static const char * const builtin_remote_setbranches_usage[] = {
+       "git remote set-branches <name> <branch>...",
+       "git remote set-branches --add <name> <branch>...",
+       NULL
+};
+
 static const char * const builtin_remote_show_usage[] = {
        "git remote show [<options>] <name>",
        NULL
@@ -87,7 +94,7 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not)
        if (not)
                string_list_clear(list, 0);
        else
-               string_list_append(arg, list);
+               string_list_append(list, arg);
        return 0;
 }
 
@@ -110,6 +117,20 @@ enum {
        TAGS_SET = 2
 };
 
+static int add_branch(const char *key, const char *branchname,
+               const char *remotename, int mirror, struct strbuf *tmp)
+{
+       strbuf_reset(tmp);
+       strbuf_addch(tmp, '+');
+       if (mirror)
+               strbuf_addf(tmp, "refs/%s:refs/%s",
+                               branchname, branchname);
+       else
+               strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+                               branchname, remotename, branchname);
+       return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
 static int add(int argc, const char **argv)
 {
        int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
@@ -160,19 +181,10 @@ static int add(int argc, const char **argv)
        strbuf_addf(&buf, "remote.%s.fetch", name);
 
        if (track.nr == 0)
-               string_list_append("*", &track);
+               string_list_append(&track, "*");
        for (i = 0; i < track.nr; i++) {
-               struct string_list_item *item = track.items + i;
-
-               strbuf_reset(&buf2);
-               strbuf_addch(&buf2, '+');
-               if (mirror)
-                       strbuf_addf(&buf2, "refs/%s:refs/%s",
-                                       item->string, item->string);
-               else
-                       strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
-                                       item->string, name, item->string);
-               if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+               if (add_branch(buf.buf, track.items[i].string,
+                               name, mirror, &buf2))
                        return 1;
        }
 
@@ -251,7 +263,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                } else
                        return 0;
 
-               item = string_list_insert(name, &branch_list);
+               item = string_list_insert(&branch_list, name);
 
                if (!item->util)
                        item->util = xcalloc(sizeof(struct branch_info), 1);
@@ -266,11 +278,11 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                        while (space) {
                                char *merge;
                                merge = xstrndup(value, space - value);
-                               string_list_append(merge, &info->merge);
+                               string_list_append(&info->merge, merge);
                                value = abbrev_branch(space + 1);
                                space = strchr(value, ' ');
                        }
-                       string_list_append(xstrdup(value), &info->merge);
+                       string_list_append(&info->merge, xstrdup(value));
                } else
                        info->rebase = git_config_bool(orig_key, value);
        }
@@ -307,14 +319,14 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
        for (ref = fetch_map; ref; ref = ref->next) {
                unsigned char sha1[20];
                if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
-                       string_list_append(abbrev_branch(ref->name), &states->new);
+                       string_list_append(&states->new, abbrev_branch(ref->name));
                else
-                       string_list_append(abbrev_branch(ref->name), &states->tracked);
+                       string_list_append(&states->tracked, abbrev_branch(ref->name));
        }
        stale_refs = get_stale_heads(states->remote, fetch_map);
        for (ref = stale_refs; ref; ref = ref->next) {
                struct string_list_item *item =
-                       string_list_append(abbrev_branch(ref->name), &states->stale);
+                       string_list_append(&states->stale, abbrev_branch(ref->name));
                item->util = xstrdup(ref->name);
        }
        free_refs(stale_refs);
@@ -336,7 +348,7 @@ struct push_info {
                PUSH_STATUS_UPTODATE,
                PUSH_STATUS_FASTFORWARD,
                PUSH_STATUS_OUTOFDATE,
-               PUSH_STATUS_NOTQUERIED,
+               PUSH_STATUS_NOTQUERIED
        } status;
 };
 
@@ -363,8 +375,8 @@ static int get_push_ref_states(const struct ref *remote_refs,
                        continue;
                hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 
-               item = string_list_append(abbrev_branch(ref->peer_ref->name),
-                                         &states->push);
+               item = string_list_append(&states->push,
+                                         abbrev_branch(ref->peer_ref->name));
                item->util = xcalloc(sizeof(struct push_info), 1);
                info = item->util;
                info->forced = ref->force;
@@ -399,7 +411,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)
 
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
-               item = string_list_append("(matching)", &states->push);
+               item = string_list_append(&states->push, "(matching)");
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
@@ -407,11 +419,11 @@ static int get_push_ref_states_noquery(struct ref_states *states)
        for (i = 0; i < remote->push_refspec_nr; i++) {
                struct refspec *spec = remote->push + i;
                if (spec->matching)
-                       item = string_list_append("(matching)", &states->push);
+                       item = string_list_append(&states->push, "(matching)");
                else if (strlen(spec->src))
-                       item = string_list_append(spec->src, &states->push);
+                       item = string_list_append(&states->push, spec->src);
                else
-                       item = string_list_append("(delete)", &states->push);
+                       item = string_list_append(&states->push, "(delete)");
 
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->forced = spec->force;
@@ -435,7 +447,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
        matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
                                    fetch_map, 1);
        for (ref = matches; ref; ref = ref->next)
-               string_list_append(abbrev_branch(ref->name), &states->heads);
+               string_list_append(&states->heads, abbrev_branch(ref->name));
 
        free_refs(fetch_map);
        free_refs(matches);
@@ -499,8 +511,8 @@ static int add_branch_for_removal(const char *refname,
        if (prefixcmp(refname, "refs/remotes")) {
                /* advise user how to delete local branches */
                if (!prefixcmp(refname, "refs/heads/"))
-                       string_list_append(abbrev_branch(refname),
-                                          branches->skipped);
+                       string_list_append(branches->skipped,
+                                          abbrev_branch(refname));
                /* silently skip over other non-remote refs */
                return 0;
        }
@@ -509,7 +521,7 @@ static int add_branch_for_removal(const char *refname,
        if (flags & REF_ISSYMREF)
                return unlink(git_path("%s", refname));
 
-       item = string_list_append(refname, branches->branches);
+       item = string_list_append(branches->branches, refname);
        item->util = xmalloc(20);
        hashcpy(item->util, sha1);
 
@@ -534,7 +546,7 @@ static int read_remote_branches(const char *refname,
 
        strbuf_addf(&buf, "refs/remotes/%s", rename->old);
        if (!prefixcmp(refname, buf.buf)) {
-               item = string_list_append(xstrdup(refname), rename->remote_branches);
+               item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref(refname, orig_sha1, 1, &flag);
                if (flag & REF_ISSYMREF)
                        item->util = xstrdup(symref);
@@ -724,11 +736,14 @@ static int rm(int argc, const char **argv)
        struct known_remotes known_remotes = { NULL, NULL };
        struct string_list branches = { NULL, 0, 0, 1 };
        struct string_list skipped = { NULL, 0, 0, 1 };
-       struct branches_for_remote cb_data = {
-               NULL, &branches, &skipped, &known_remotes
-       };
+       struct branches_for_remote cb_data;
        int i, result;
 
+       memset(&cb_data, 0, sizeof(cb_data));
+       cb_data.branches = &branches;
+       cb_data.skipped = &skipped;
+       cb_data.keep = &known_remotes;
+
        if (argc != 2)
                usage_with_options(builtin_remote_rm_usage, options);
 
@@ -817,7 +832,7 @@ static int append_ref_to_tracked_list(const char *refname,
        memset(&refspec, 0, sizeof(refspec));
        refspec.dst = (char *)refname;
        if (!remote_find_tracking(states->remote, &refspec))
-               string_list_append(abbrev_branch(refspec.src), &states->tracked);
+               string_list_append(&states->tracked, abbrev_branch(refspec.src));
 
        return 0;
 }
@@ -870,7 +885,7 @@ static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
        int n = strlen(item->string);
        if (n > info->width)
                info->width = n;
-       string_list_insert(item->string, info->list);
+       string_list_insert(info->list, item->string);
        return 0;
 }
 
@@ -917,7 +932,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
        if (branch_info->rebase)
                show_info->any_rebase = 1;
 
-       item = string_list_insert(branch_item->string, show_info->list);
+       item = string_list_insert(show_info->list, branch_item->string);
        item->util = branch_info;
 
        return 0;
@@ -965,7 +980,7 @@ static int add_push_to_show_info(struct string_list_item *push_item, void *cb_da
                show_info->width = n;
        if ((n = strlen(push_info->dest)) > show_info->width2)
                show_info->width2 = n;
-       item = string_list_append(push_item->string, show_info->list);
+       item = string_list_append(show_info->list, push_item->string);
        item->util = push_item->util;
        return 0;
 }
@@ -1081,24 +1096,24 @@ static int show(int argc, const char **argv)
 
                /* remote branch info */
                info.width = 0;
-               for_each_string_list(add_remote_to_show_info, &states.new, &info);
-               for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
-               for_each_string_list(add_remote_to_show_info, &states.stale, &info);
+               for_each_string_list(&states.new, add_remote_to_show_info, &info);
+               for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
+               for_each_string_list(&states.stale, add_remote_to_show_info, &info);
                if (info.list->nr)
                        printf("  Remote branch%s:%s\n",
                               info.list->nr > 1 ? "es" : "",
                                no_query ? " (status not queried)" : "");
-               for_each_string_list(show_remote_info_item, info.list, &info);
+               for_each_string_list(info.list, show_remote_info_item, &info);
                string_list_clear(info.list, 0);
 
                /* git pull info */
                info.width = 0;
                info.any_rebase = 0;
-               for_each_string_list(add_local_to_show_info, &branch_list, &info);
+               for_each_string_list(&branch_list, add_local_to_show_info, &info);
                if (info.list->nr)
                        printf("  Local branch%s configured for 'git pull':\n",
                               info.list->nr > 1 ? "es" : "");
-               for_each_string_list(show_local_info_item, info.list, &info);
+               for_each_string_list(info.list, show_local_info_item, &info);
                string_list_clear(info.list, 0);
 
                /* git push info */
@@ -1106,14 +1121,14 @@ static int show(int argc, const char **argv)
                        printf("  Local refs will be mirrored by 'git push'\n");
 
                info.width = info.width2 = 0;
-               for_each_string_list(add_push_to_show_info, &states.push, &info);
+               for_each_string_list(&states.push, add_push_to_show_info, &info);
                qsort(info.list->items, info.list->nr,
                        sizeof(*info.list->items), cmp_string_with_push);
                if (info.list->nr)
                        printf("  Local ref%s configured for 'git push'%s:\n",
                                info.list->nr > 1 ? "s" : "",
                                no_query ? " (status not queried)" : "");
-               for_each_string_list(show_push_info_item, info.list, &info);
+               for_each_string_list(info.list, show_push_info_item, &info);
                string_list_clear(info.list, 0);
 
                free_remote_ref_states(&states);
@@ -1284,6 +1299,72 @@ static int update(int argc, const char **argv)
        return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
 }
 
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+       return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+                       const char *key)
+{
+       const char *remotename = remote->name;
+       int mirror = remote->mirror;
+       struct strbuf refspec = STRBUF_INIT;
+
+       for (; *branches; branches++)
+               if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+                       strbuf_release(&refspec);
+                       return 1;
+               }
+
+       strbuf_release(&refspec);
+       return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+                               int add_mode)
+{
+       struct strbuf key = STRBUF_INIT;
+       struct remote *remote;
+
+       strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+       if (!remote_is_configured(remotename))
+               die("No such remote '%s'", remotename);
+       remote = remote_get(remotename);
+
+       if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+               strbuf_release(&key);
+               return 1;
+       }
+       if (add_branches(remote, branches, key.buf)) {
+               strbuf_release(&key);
+               return 1;
+       }
+
+       strbuf_release(&key);
+       return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+       int add_mode = 0;
+       struct option options[] = {
+               OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, NULL, options,
+                            builtin_remote_setbranches_usage, 0);
+       if (argc == 0) {
+               error("no remote specified");
+               usage_with_options(builtin_remote_seturl_usage, options);
+       }
+       argv[argc] = NULL;
+
+       return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
 static int set_url(int argc, const char **argv)
 {
        int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1379,10 +1460,10 @@ static int get_one_entry(struct remote *remote, void *priv)
 
        if (remote->url_nr > 0) {
                strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
-               string_list_append(remote->name, list)->util =
+               string_list_append(list, remote->name)->util =
                                strbuf_detach(&url_buf, NULL);
        } else
-               string_list_append(remote->name, list)->util = NULL;
+               string_list_append(list, remote->name)->util = NULL;
        if (remote->pushurl_nr) {
                url = remote->pushurl;
                url_nr = remote->pushurl_nr;
@@ -1393,7 +1474,7 @@ static int get_one_entry(struct remote *remote, void *priv)
        for (i = 0; i < url_nr; i++)
        {
                strbuf_addf(&url_buf, "%s (push)", url[i]);
-               string_list_append(remote->name, list)->util =
+               string_list_append(list, remote->name)->util =
                                strbuf_detach(&url_buf, NULL);
        }
 
@@ -1449,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "set-head"))
                result = set_head(argc, argv);
+       else if (!strcmp(argv[0], "set-branches"))
+               result = set_branches(argc, argv);
        else if (!strcmp(argv[0], "set-url"))
                result = set_url(argc, argv);
        else if (!strcmp(argv[0], "show"))