apply: tighten constness of line buffer
[gitweb.git] / builtin / remote.c
index 277765b864202b2b6f20069e0cb3f95eaef4fcaa..c810643815e38f4ada805e2af49973e4e0051801 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "builtin.h"
 #include "parse-options.h"
 #include "transport.h"
 #include "remote.h"
@@ -9,13 +9,14 @@
 
 static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose]",
-       "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
+       "git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>",
        "git remote rename <old> <new>",
        "git remote rm <name>",
        "git remote set-head <name> (-a | -d | <branch>)",
        "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 [-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
@@ -81,16 +88,6 @@ static inline int postfixcmp(const char *string, const char *postfix)
        return strcmp(string + len1 - len2, postfix);
 }
 
-static int opt_parse_track(const struct option *opt, const char *arg, int not)
-{
-       struct string_list *list = opt->value;
-       if (not)
-               string_list_clear(list, 0);
-       else
-               string_list_append(arg, list);
-       return 0;
-}
-
 static int fetch_remote(const char *name)
 {
        const char *argv[] = { "fetch", name, NULL, NULL };
@@ -104,10 +101,58 @@ static int fetch_remote(const char *name)
        return 0;
 }
 
+enum {
+       TAGS_UNSET = 0,
+       TAGS_DEFAULT = 1,
+       TAGS_SET = 2
+};
+
+#define MIRROR_NONE 0
+#define MIRROR_FETCH 1
+#define MIRROR_PUSH 2
+#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH)
+
+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 const char mirror_advice[] =
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead";
+
+static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
+{
+       unsigned *mirror = opt->value;
+       if (not)
+               *mirror = MIRROR_NONE;
+       else if (!arg) {
+               warning("%s", mirror_advice);
+               *mirror = MIRROR_BOTH;
+       }
+       else if (!strcmp(arg, "fetch"))
+               *mirror = MIRROR_FETCH;
+       else if (!strcmp(arg, "push"))
+               *mirror = MIRROR_PUSH;
+       else
+               return error("unknown mirror argument: %s", arg);
+       return 0;
+}
+
 static int add(int argc, const char **argv)
 {
-       int fetch = 0, mirror = 0;
-       struct string_list track = { NULL, 0, 0 };
+       int fetch = 0, fetch_tags = TAGS_DEFAULT;
+       unsigned mirror = MIRROR_NONE;
+       struct string_list track = STRING_LIST_INIT_NODUP;
        const char *master = NULL;
        struct remote *remote;
        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
@@ -116,10 +161,17 @@ static int add(int argc, const char **argv)
 
        struct option options[] = {
                OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
-               OPT_CALLBACK('t', "track", &track, "branch",
-                       "branch(es) to track", opt_parse_track),
+               OPT_SET_INT(0, "tags", &fetch_tags,
+                           "import all tags and associated objects when fetching",
+                           TAGS_SET),
+               OPT_SET_INT(0, NULL, &fetch_tags,
+                           "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
+               OPT_STRING_LIST('t', "track", &track, "branch",
+                               "branch(es) to track"),
                OPT_STRING('m', "master", &master, "branch", "master branch"),
-               OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
+               { OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch",
+                       "set up remote as a mirror to push to or fetch from",
+                       PARSE_OPT_OPTARG, parse_mirror_opt },
                OPT_END()
        };
 
@@ -129,6 +181,11 @@ static int add(int argc, const char **argv)
        if (argc < 2)
                usage_with_options(builtin_remote_add_usage, options);
 
+       if (mirror && master)
+               die("specifying a master branch makes no sense with --mirror");
+       if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
+               die("specifying branches to track makes sense only with fetch mirrors");
+
        name = argv[0];
        url = argv[1];
 
@@ -145,33 +202,33 @@ static int add(int argc, const char **argv)
        if (git_config_set(buf.buf, url))
                return 1;
 
-       strbuf_reset(&buf);
-       strbuf_addf(&buf, "remote.%s.fetch", name);
-
-       if (track.nr == 0)
-               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))
-                       return 1;
+       if (!mirror || mirror & MIRROR_FETCH) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "remote.%s.fetch", name);
+               if (track.nr == 0)
+                       string_list_append(&track, "*");
+               for (i = 0; i < track.nr; i++) {
+                       if (add_branch(buf.buf, track.items[i].string,
+                                      name, mirror, &buf2))
+                               return 1;
+               }
        }
 
-       if (mirror) {
+       if (mirror & MIRROR_PUSH) {
                strbuf_reset(&buf);
                strbuf_addf(&buf, "remote.%s.mirror", name);
                if (git_config_set(buf.buf, "true"))
                        return 1;
        }
 
+       if (fetch_tags != TAGS_DEFAULT) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "remote.%s.tagopt", name);
+               if (git_config_set(buf.buf,
+                       fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
+                       return 1;
+       }
+
        if (fetch && fetch_remote(name))
                return 1;
 
@@ -232,7 +289,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);
@@ -247,11 +304,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);
        }
@@ -288,14 +345,15 @@ 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);
+       stale_refs = get_stale_heads(states->remote->fetch,
+                                    states->remote->fetch_refspec_nr, 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);
@@ -317,7 +375,7 @@ struct push_info {
                PUSH_STATUS_UPTODATE,
                PUSH_STATUS_FASTFORWARD,
                PUSH_STATUS_OUTOFDATE,
-               PUSH_STATUS_NOTQUERIED,
+               PUSH_STATUS_NOTQUERIED
        } status;
 };
 
@@ -332,8 +390,8 @@ static int get_push_ref_states(const struct ref *remote_refs,
        local_refs = get_local_heads();
        push_map = copy_ref_list(remote_refs);
 
-       match_refs(local_refs, &push_map, remote->push_refspec_nr,
-                  remote->push_refspec, MATCH_REFS_NONE);
+       match_push_refs(local_refs, &push_map, remote->push_refspec_nr,
+                       remote->push_refspec, MATCH_REFS_NONE);
 
        states->push.strdup_strings = 1;
        for (ref = push_map; ref; ref = ref->next) {
@@ -344,8 +402,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;
@@ -380,7 +438,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);
@@ -388,11 +446,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;
@@ -416,7 +474,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);
@@ -476,12 +534,12 @@ static int add_branch_for_removal(const char *refname,
                        return 0;
        }
 
-       /* don't delete non-remote refs */
+       /* don't delete non-remote-tracking refs */
        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;
        }
@@ -490,7 +548,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);
 
@@ -513,9 +571,9 @@ static int read_remote_branches(const char *refname,
        unsigned char orig_sha1[20];
        const char *symref;
 
-       strbuf_addf(&buf, "refs/remotes/%s", rename->old);
+       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);
@@ -564,10 +622,11 @@ static int mv(int argc, const char **argv)
                OPT_END()
        };
        struct remote *oldremote, *newremote;
-       struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
-       struct string_list remote_branches = { NULL, 0, 0, 0 };
+       struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT,
+               old_remote_context = STRBUF_INIT;
+       struct string_list remote_branches = STRING_LIST_INIT_NODUP;
        struct rename_info rename;
-       int i;
+       int i, refspec_updated = 0;
 
        if (argc != 3)
                usage_with_options(builtin_remote_rename_usage, options);
@@ -602,15 +661,25 @@ static int mv(int argc, const char **argv)
        strbuf_addf(&buf, "remote.%s.fetch", rename.new);
        if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
                return error("Could not remove config section '%s'", buf.buf);
+       strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
                char *ptr;
 
                strbuf_reset(&buf2);
                strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
-               ptr = strstr(buf2.buf, rename.old);
-               if (ptr)
-                       strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
-                                       rename.new, strlen(rename.new));
+               ptr = strstr(buf2.buf, old_remote_context.buf);
+               if (ptr) {
+                       refspec_updated = 1;
+                       strbuf_splice(&buf2,
+                                     ptr-buf2.buf + strlen(":refs/remotes/"),
+                                     strlen(rename.old), rename.new,
+                                     strlen(rename.new));
+               } else
+                       warning("Not updating non-default fetch respec\n"
+                               "\t%s\n"
+                               "\tPlease update the configuration manually if necessary.",
+                               buf2.buf);
+
                if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
                        return error("Could not append '%s'", buf.buf);
        }
@@ -628,6 +697,9 @@ static int mv(int argc, const char **argv)
                }
        }
 
+       if (!refspec_updated)
+               return 0;
+
        /*
         * First remove symrefs, then rename the rest, finally create
         * the new symrefs.
@@ -703,13 +775,16 @@ static int rm(int argc, const char **argv)
        struct remote *remote;
        struct strbuf buf = STRBUF_INIT;
        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 string_list branches = STRING_LIST_INIT_DUP;
+       struct string_list skipped = STRING_LIST_INIT_DUP;
+       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);
 
@@ -757,9 +832,9 @@ static int rm(int argc, const char **argv)
 
        if (skipped.nr) {
                fprintf(stderr, skipped.nr == 1 ?
-                       "Note: A non-remote branch was not removed; "
+                       "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
                        "to delete it, use:\n" :
-                       "Note: Non-remote branches were not removed; "
+                       "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
                        "to delete them, use:\n");
                for (i = 0; i < skipped.nr; i++)
                        fprintf(stderr, "  git branch -d %s\n",
@@ -798,7 +873,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;
 }
@@ -851,7 +926,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;
 }
 
@@ -898,7 +973,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;
@@ -946,7 +1021,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;
 }
@@ -1010,7 +1085,7 @@ static int show(int argc, const char **argv)
                OPT_END()
        };
        struct ref_states states;
-       struct string_list info_list = { NULL, 0, 0, 0 };
+       struct string_list info_list = STRING_LIST_INIT_NODUP;
        struct show_info info;
 
        argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
@@ -1043,7 +1118,7 @@ static int show(int argc, const char **argv)
                        url = states.remote->url;
                        url_nr = states.remote->url_nr;
                }
-               for (i=0; i < url_nr; i++)
+               for (i = 0; i < url_nr; i++)
                        printf("  Push  URL: %s\n", url[i]);
                if (!i)
                        printf("  Push  URL: %s\n", "(no URL)");
@@ -1062,24 +1137,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 */
@@ -1087,14 +1162,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);
@@ -1166,7 +1241,7 @@ static int prune(int argc, const char **argv)
 {
        int dry_run = 0, result = 0;
        struct option options[] = {
-               OPT__DRY_RUN(&dry_run),
+               OPT__DRY_RUN(&dry_run, "dry run"),
                OPT_END()
        };
 
@@ -1265,6 +1340,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_setbranches_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;
@@ -1286,7 +1427,7 @@ static int set_url(int argc, const char **argv)
                            "delete URLs"),
                OPT_END()
        };
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
                             PARSE_OPT_KEEP_ARGV0);
 
        if (add_mode && delete_mode)
@@ -1360,10 +1501,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;
@@ -1374,7 +1515,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);
        }
 
@@ -1383,7 +1524,7 @@ static int get_one_entry(struct remote *remote, void *priv)
 
 static int show_all(void)
 {
-       struct string_list list = { NULL, 0, 0 };
+       struct string_list list = STRING_LIST_INIT_NODUP;
        int result;
 
        list.strdup_strings = 1;
@@ -1412,7 +1553,7 @@ static int show_all(void)
 int cmd_remote(int argc, const char **argv, const char *prefix)
 {
        struct option options[] = {
-               OPT_BOOLEAN('v', "verbose", &verbose, "be verbose; must be placed before a subcommand"),
+               OPT__VERBOSE(&verbose, "be verbose; must be placed before a subcommand"),
                OPT_END()
        };
        int result;
@@ -1430,6 +1571,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"))