builtin-remote.con commit builtin-fetch: add --prune option (f360d84)
   1#include "cache.h"
   2#include "parse-options.h"
   3#include "transport.h"
   4#include "remote.h"
   5#include "string-list.h"
   6#include "strbuf.h"
   7#include "run-command.h"
   8#include "refs.h"
   9
  10static const char * const builtin_remote_usage[] = {
  11        "git remote [-v | --verbose]",
  12        "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
  13        "git remote rename <old> <new>",
  14        "git remote rm <name>",
  15        "git remote set-head <name> [-a | -d | <branch>]",
  16        "git remote show [-n] <name>",
  17        "git remote prune [-n | --dry-run] <name>",
  18        "git remote [-v | --verbose] update [-p | --prune] [group]",
  19        NULL
  20};
  21
  22#define GET_REF_STATES (1<<0)
  23#define GET_HEAD_NAMES (1<<1)
  24#define GET_PUSH_REF_STATES (1<<2)
  25
  26static int verbose;
  27
  28static int show_all(void);
  29static int prune_remote(const char *remote, int dry_run);
  30
  31static inline int postfixcmp(const char *string, const char *postfix)
  32{
  33        int len1 = strlen(string), len2 = strlen(postfix);
  34        if (len1 < len2)
  35                return 1;
  36        return strcmp(string + len1 - len2, postfix);
  37}
  38
  39static int opt_parse_track(const struct option *opt, const char *arg, int not)
  40{
  41        struct string_list *list = opt->value;
  42        if (not)
  43                string_list_clear(list, 0);
  44        else
  45                string_list_append(arg, list);
  46        return 0;
  47}
  48
  49static int fetch_remote(const char *name)
  50{
  51        const char *argv[] = { "fetch", name, NULL, NULL };
  52        if (verbose) {
  53                argv[1] = "-v";
  54                argv[2] = name;
  55        }
  56        printf("Updating %s\n", name);
  57        if (run_command_v_opt(argv, RUN_GIT_CMD))
  58                return error("Could not fetch %s", name);
  59        return 0;
  60}
  61
  62static int add(int argc, const char **argv)
  63{
  64        int fetch = 0, mirror = 0;
  65        struct string_list track = { NULL, 0, 0 };
  66        const char *master = NULL;
  67        struct remote *remote;
  68        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
  69        const char *name, *url;
  70        int i;
  71
  72        struct option options[] = {
  73                OPT_GROUP("add specific options"),
  74                OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
  75                OPT_CALLBACK('t', "track", &track, "branch",
  76                        "branch(es) to track", opt_parse_track),
  77                OPT_STRING('m', "master", &master, "branch", "master branch"),
  78                OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
  79                OPT_END()
  80        };
  81
  82        argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
  83                             0);
  84
  85        if (argc < 2)
  86                usage_with_options(builtin_remote_usage, options);
  87
  88        name = argv[0];
  89        url = argv[1];
  90
  91        remote = remote_get(name);
  92        if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
  93                        remote->fetch_refspec_nr))
  94                die("remote %s already exists.", name);
  95
  96        strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
  97        if (!valid_fetch_refspec(buf2.buf))
  98                die("'%s' is not a valid remote name", name);
  99
 100        strbuf_addf(&buf, "remote.%s.url", name);
 101        if (git_config_set(buf.buf, url))
 102                return 1;
 103
 104        strbuf_reset(&buf);
 105        strbuf_addf(&buf, "remote.%s.fetch", name);
 106
 107        if (track.nr == 0)
 108                string_list_append("*", &track);
 109        for (i = 0; i < track.nr; i++) {
 110                struct string_list_item *item = track.items + i;
 111
 112                strbuf_reset(&buf2);
 113                strbuf_addch(&buf2, '+');
 114                if (mirror)
 115                        strbuf_addf(&buf2, "refs/%s:refs/%s",
 116                                        item->string, item->string);
 117                else
 118                        strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
 119                                        item->string, name, item->string);
 120                if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
 121                        return 1;
 122        }
 123
 124        if (mirror) {
 125                strbuf_reset(&buf);
 126                strbuf_addf(&buf, "remote.%s.mirror", name);
 127                if (git_config_set(buf.buf, "true"))
 128                        return 1;
 129        }
 130
 131        if (fetch && fetch_remote(name))
 132                return 1;
 133
 134        if (master) {
 135                strbuf_reset(&buf);
 136                strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
 137
 138                strbuf_reset(&buf2);
 139                strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
 140
 141                if (create_symref(buf.buf, buf2.buf, "remote add"))
 142                        return error("Could not setup master '%s'", master);
 143        }
 144
 145        strbuf_release(&buf);
 146        strbuf_release(&buf2);
 147        string_list_clear(&track, 0);
 148
 149        return 0;
 150}
 151
 152struct branch_info {
 153        char *remote_name;
 154        struct string_list merge;
 155        int rebase;
 156};
 157
 158static struct string_list branch_list;
 159
 160static const char *abbrev_ref(const char *name, const char *prefix)
 161{
 162        const char *abbrev = skip_prefix(name, prefix);
 163        if (abbrev)
 164                return abbrev;
 165        return name;
 166}
 167#define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
 168
 169static int config_read_branches(const char *key, const char *value, void *cb)
 170{
 171        if (!prefixcmp(key, "branch.")) {
 172                const char *orig_key = key;
 173                char *name;
 174                struct string_list_item *item;
 175                struct branch_info *info;
 176                enum { REMOTE, MERGE, REBASE } type;
 177
 178                key += 7;
 179                if (!postfixcmp(key, ".remote")) {
 180                        name = xstrndup(key, strlen(key) - 7);
 181                        type = REMOTE;
 182                } else if (!postfixcmp(key, ".merge")) {
 183                        name = xstrndup(key, strlen(key) - 6);
 184                        type = MERGE;
 185                } else if (!postfixcmp(key, ".rebase")) {
 186                        name = xstrndup(key, strlen(key) - 7);
 187                        type = REBASE;
 188                } else
 189                        return 0;
 190
 191                item = string_list_insert(name, &branch_list);
 192
 193                if (!item->util)
 194                        item->util = xcalloc(sizeof(struct branch_info), 1);
 195                info = item->util;
 196                if (type == REMOTE) {
 197                        if (info->remote_name)
 198                                warning("more than one %s", orig_key);
 199                        info->remote_name = xstrdup(value);
 200                } else if (type == MERGE) {
 201                        char *space = strchr(value, ' ');
 202                        value = abbrev_branch(value);
 203                        while (space) {
 204                                char *merge;
 205                                merge = xstrndup(value, space - value);
 206                                string_list_append(merge, &info->merge);
 207                                value = abbrev_branch(space + 1);
 208                                space = strchr(value, ' ');
 209                        }
 210                        string_list_append(xstrdup(value), &info->merge);
 211                } else
 212                        info->rebase = git_config_bool(orig_key, value);
 213        }
 214        return 0;
 215}
 216
 217static void read_branches(void)
 218{
 219        if (branch_list.nr)
 220                return;
 221        git_config(config_read_branches, NULL);
 222}
 223
 224struct ref_states {
 225        struct remote *remote;
 226        struct string_list new, stale, tracked, heads, push;
 227        int queried;
 228};
 229
 230static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
 231{
 232        struct ref *fetch_map = NULL, **tail = &fetch_map;
 233        struct ref *ref, *stale_refs;
 234        int i;
 235
 236        for (i = 0; i < states->remote->fetch_refspec_nr; i++)
 237                if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
 238                        die("Could not get fetch map for refspec %s",
 239                                states->remote->fetch_refspec[i]);
 240
 241        states->new.strdup_strings = states->tracked.strdup_strings = 1;
 242        for (ref = fetch_map; ref; ref = ref->next) {
 243                unsigned char sha1[20];
 244                if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
 245                        string_list_append(abbrev_branch(ref->name), &states->new);
 246                else
 247                        string_list_append(abbrev_branch(ref->name), &states->tracked);
 248        }
 249        stale_refs = get_stale_heads(states->remote, fetch_map);
 250        for (ref = stale_refs; ref; ref = ref->next) {
 251                struct string_list_item *item =
 252                        string_list_append(abbrev_branch(ref->name), &states->stale);
 253                item->util = xstrdup(ref->name);
 254        }
 255        free_refs(stale_refs);
 256        free_refs(fetch_map);
 257
 258        sort_string_list(&states->new);
 259        sort_string_list(&states->tracked);
 260        sort_string_list(&states->stale);
 261
 262        return 0;
 263}
 264
 265struct push_info {
 266        char *dest;
 267        int forced;
 268        enum {
 269                PUSH_STATUS_CREATE = 0,
 270                PUSH_STATUS_DELETE,
 271                PUSH_STATUS_UPTODATE,
 272                PUSH_STATUS_FASTFORWARD,
 273                PUSH_STATUS_OUTOFDATE,
 274                PUSH_STATUS_NOTQUERIED,
 275        } status;
 276};
 277
 278static int get_push_ref_states(const struct ref *remote_refs,
 279        struct ref_states *states)
 280{
 281        struct remote *remote = states->remote;
 282        struct ref *ref, *local_refs, *push_map;
 283        if (remote->mirror)
 284                return 0;
 285
 286        local_refs = get_local_heads();
 287        push_map = copy_ref_list(remote_refs);
 288
 289        match_refs(local_refs, &push_map, remote->push_refspec_nr,
 290                   remote->push_refspec, MATCH_REFS_NONE);
 291
 292        states->push.strdup_strings = 1;
 293        for (ref = push_map; ref; ref = ref->next) {
 294                struct string_list_item *item;
 295                struct push_info *info;
 296
 297                if (!ref->peer_ref)
 298                        continue;
 299                hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 300
 301                item = string_list_append(abbrev_branch(ref->peer_ref->name),
 302                                          &states->push);
 303                item->util = xcalloc(sizeof(struct push_info), 1);
 304                info = item->util;
 305                info->forced = ref->force;
 306                info->dest = xstrdup(abbrev_branch(ref->name));
 307
 308                if (is_null_sha1(ref->new_sha1)) {
 309                        info->status = PUSH_STATUS_DELETE;
 310                } else if (!hashcmp(ref->old_sha1, ref->new_sha1))
 311                        info->status = PUSH_STATUS_UPTODATE;
 312                else if (is_null_sha1(ref->old_sha1))
 313                        info->status = PUSH_STATUS_CREATE;
 314                else if (has_sha1_file(ref->old_sha1) &&
 315                         ref_newer(ref->new_sha1, ref->old_sha1))
 316                        info->status = PUSH_STATUS_FASTFORWARD;
 317                else
 318                        info->status = PUSH_STATUS_OUTOFDATE;
 319        }
 320        free_refs(local_refs);
 321        free_refs(push_map);
 322        return 0;
 323}
 324
 325static int get_push_ref_states_noquery(struct ref_states *states)
 326{
 327        int i;
 328        struct remote *remote = states->remote;
 329        struct string_list_item *item;
 330        struct push_info *info;
 331
 332        if (remote->mirror)
 333                return 0;
 334
 335        states->push.strdup_strings = 1;
 336        if (!remote->push_refspec_nr) {
 337                item = string_list_append("(matching)", &states->push);
 338                info = item->util = xcalloc(sizeof(struct push_info), 1);
 339                info->status = PUSH_STATUS_NOTQUERIED;
 340                info->dest = xstrdup(item->string);
 341        }
 342        for (i = 0; i < remote->push_refspec_nr; i++) {
 343                struct refspec *spec = remote->push + i;
 344                if (spec->matching)
 345                        item = string_list_append("(matching)", &states->push);
 346                else if (strlen(spec->src))
 347                        item = string_list_append(spec->src, &states->push);
 348                else
 349                        item = string_list_append("(delete)", &states->push);
 350
 351                info = item->util = xcalloc(sizeof(struct push_info), 1);
 352                info->forced = spec->force;
 353                info->status = PUSH_STATUS_NOTQUERIED;
 354                info->dest = xstrdup(spec->dst ? spec->dst : item->string);
 355        }
 356        return 0;
 357}
 358
 359static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
 360{
 361        struct ref *ref, *matches;
 362        struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
 363        struct refspec refspec;
 364
 365        refspec.force = 0;
 366        refspec.pattern = 1;
 367        refspec.src = refspec.dst = "refs/heads/*";
 368        states->heads.strdup_strings = 1;
 369        get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 370        matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 371                                    fetch_map, 1);
 372        for (ref = matches; ref; ref = ref->next)
 373                string_list_append(abbrev_branch(ref->name), &states->heads);
 374
 375        free_refs(fetch_map);
 376        free_refs(matches);
 377
 378        return 0;
 379}
 380
 381struct known_remote {
 382        struct known_remote *next;
 383        struct remote *remote;
 384};
 385
 386struct known_remotes {
 387        struct remote *to_delete;
 388        struct known_remote *list;
 389};
 390
 391static int add_known_remote(struct remote *remote, void *cb_data)
 392{
 393        struct known_remotes *all = cb_data;
 394        struct known_remote *r;
 395
 396        if (!strcmp(all->to_delete->name, remote->name))
 397                return 0;
 398
 399        r = xmalloc(sizeof(*r));
 400        r->remote = remote;
 401        r->next = all->list;
 402        all->list = r;
 403        return 0;
 404}
 405
 406struct branches_for_remote {
 407        struct remote *remote;
 408        struct string_list *branches, *skipped;
 409        struct known_remotes *keep;
 410};
 411
 412static int add_branch_for_removal(const char *refname,
 413        const unsigned char *sha1, int flags, void *cb_data)
 414{
 415        struct branches_for_remote *branches = cb_data;
 416        struct refspec refspec;
 417        struct string_list_item *item;
 418        struct known_remote *kr;
 419
 420        memset(&refspec, 0, sizeof(refspec));
 421        refspec.dst = (char *)refname;
 422        if (remote_find_tracking(branches->remote, &refspec))
 423                return 0;
 424
 425        /* don't delete a branch if another remote also uses it */
 426        for (kr = branches->keep->list; kr; kr = kr->next) {
 427                memset(&refspec, 0, sizeof(refspec));
 428                refspec.dst = (char *)refname;
 429                if (!remote_find_tracking(kr->remote, &refspec))
 430                        return 0;
 431        }
 432
 433        /* don't delete non-remote refs */
 434        if (prefixcmp(refname, "refs/remotes")) {
 435                /* advise user how to delete local branches */
 436                if (!prefixcmp(refname, "refs/heads/"))
 437                        string_list_append(abbrev_branch(refname),
 438                                           branches->skipped);
 439                /* silently skip over other non-remote refs */
 440                return 0;
 441        }
 442
 443        /* make sure that symrefs are deleted */
 444        if (flags & REF_ISSYMREF)
 445                return unlink(git_path("%s", refname));
 446
 447        item = string_list_append(refname, branches->branches);
 448        item->util = xmalloc(20);
 449        hashcpy(item->util, sha1);
 450
 451        return 0;
 452}
 453
 454struct rename_info {
 455        const char *old;
 456        const char *new;
 457        struct string_list *remote_branches;
 458};
 459
 460static int read_remote_branches(const char *refname,
 461        const unsigned char *sha1, int flags, void *cb_data)
 462{
 463        struct rename_info *rename = cb_data;
 464        struct strbuf buf = STRBUF_INIT;
 465        struct string_list_item *item;
 466        int flag;
 467        unsigned char orig_sha1[20];
 468        const char *symref;
 469
 470        strbuf_addf(&buf, "refs/remotes/%s", rename->old);
 471        if (!prefixcmp(refname, buf.buf)) {
 472                item = string_list_append(xstrdup(refname), rename->remote_branches);
 473                symref = resolve_ref(refname, orig_sha1, 1, &flag);
 474                if (flag & REF_ISSYMREF)
 475                        item->util = xstrdup(symref);
 476                else
 477                        item->util = NULL;
 478        }
 479
 480        return 0;
 481}
 482
 483static int migrate_file(struct remote *remote)
 484{
 485        struct strbuf buf = STRBUF_INIT;
 486        int i;
 487        char *path = NULL;
 488
 489        strbuf_addf(&buf, "remote.%s.url", remote->name);
 490        for (i = 0; i < remote->url_nr; i++)
 491                if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
 492                        return error("Could not append '%s' to '%s'",
 493                                        remote->url[i], buf.buf);
 494        strbuf_reset(&buf);
 495        strbuf_addf(&buf, "remote.%s.push", remote->name);
 496        for (i = 0; i < remote->push_refspec_nr; i++)
 497                if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
 498                        return error("Could not append '%s' to '%s'",
 499                                        remote->push_refspec[i], buf.buf);
 500        strbuf_reset(&buf);
 501        strbuf_addf(&buf, "remote.%s.fetch", remote->name);
 502        for (i = 0; i < remote->fetch_refspec_nr; i++)
 503                if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
 504                        return error("Could not append '%s' to '%s'",
 505                                        remote->fetch_refspec[i], buf.buf);
 506        if (remote->origin == REMOTE_REMOTES)
 507                path = git_path("remotes/%s", remote->name);
 508        else if (remote->origin == REMOTE_BRANCHES)
 509                path = git_path("branches/%s", remote->name);
 510        if (path)
 511                unlink_or_warn(path);
 512        return 0;
 513}
 514
 515static int mv(int argc, const char **argv)
 516{
 517        struct option options[] = {
 518                OPT_END()
 519        };
 520        struct remote *oldremote, *newremote;
 521        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
 522        struct string_list remote_branches = { NULL, 0, 0, 0 };
 523        struct rename_info rename;
 524        int i;
 525
 526        if (argc != 3)
 527                usage_with_options(builtin_remote_usage, options);
 528
 529        rename.old = argv[1];
 530        rename.new = argv[2];
 531        rename.remote_branches = &remote_branches;
 532
 533        oldremote = remote_get(rename.old);
 534        if (!oldremote)
 535                die("No such remote: %s", rename.old);
 536
 537        if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
 538                return migrate_file(oldremote);
 539
 540        newremote = remote_get(rename.new);
 541        if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
 542                die("remote %s already exists.", rename.new);
 543
 544        strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
 545        if (!valid_fetch_refspec(buf.buf))
 546                die("'%s' is not a valid remote name", rename.new);
 547
 548        strbuf_reset(&buf);
 549        strbuf_addf(&buf, "remote.%s", rename.old);
 550        strbuf_addf(&buf2, "remote.%s", rename.new);
 551        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
 552                return error("Could not rename config section '%s' to '%s'",
 553                                buf.buf, buf2.buf);
 554
 555        strbuf_reset(&buf);
 556        strbuf_addf(&buf, "remote.%s.fetch", rename.new);
 557        if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
 558                return error("Could not remove config section '%s'", buf.buf);
 559        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
 560                char *ptr;
 561
 562                strbuf_reset(&buf2);
 563                strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
 564                ptr = strstr(buf2.buf, rename.old);
 565                if (ptr)
 566                        strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
 567                                        rename.new, strlen(rename.new));
 568                if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
 569                        return error("Could not append '%s'", buf.buf);
 570        }
 571
 572        read_branches();
 573        for (i = 0; i < branch_list.nr; i++) {
 574                struct string_list_item *item = branch_list.items + i;
 575                struct branch_info *info = item->util;
 576                if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
 577                        strbuf_reset(&buf);
 578                        strbuf_addf(&buf, "branch.%s.remote", item->string);
 579                        if (git_config_set(buf.buf, rename.new)) {
 580                                return error("Could not set '%s'", buf.buf);
 581                        }
 582                }
 583        }
 584
 585        /*
 586         * First remove symrefs, then rename the rest, finally create
 587         * the new symrefs.
 588         */
 589        for_each_ref(read_remote_branches, &rename);
 590        for (i = 0; i < remote_branches.nr; i++) {
 591                struct string_list_item *item = remote_branches.items + i;
 592                int flag = 0;
 593                unsigned char sha1[20];
 594
 595                resolve_ref(item->string, sha1, 1, &flag);
 596                if (!(flag & REF_ISSYMREF))
 597                        continue;
 598                if (delete_ref(item->string, NULL, REF_NODEREF))
 599                        die("deleting '%s' failed", item->string);
 600        }
 601        for (i = 0; i < remote_branches.nr; i++) {
 602                struct string_list_item *item = remote_branches.items + i;
 603
 604                if (item->util)
 605                        continue;
 606                strbuf_reset(&buf);
 607                strbuf_addstr(&buf, item->string);
 608                strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
 609                                rename.new, strlen(rename.new));
 610                strbuf_reset(&buf2);
 611                strbuf_addf(&buf2, "remote: renamed %s to %s",
 612                                item->string, buf.buf);
 613                if (rename_ref(item->string, buf.buf, buf2.buf))
 614                        die("renaming '%s' failed", item->string);
 615        }
 616        for (i = 0; i < remote_branches.nr; i++) {
 617                struct string_list_item *item = remote_branches.items + i;
 618
 619                if (!item->util)
 620                        continue;
 621                strbuf_reset(&buf);
 622                strbuf_addstr(&buf, item->string);
 623                strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
 624                                rename.new, strlen(rename.new));
 625                strbuf_reset(&buf2);
 626                strbuf_addstr(&buf2, item->util);
 627                strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
 628                                rename.new, strlen(rename.new));
 629                strbuf_reset(&buf3);
 630                strbuf_addf(&buf3, "remote: renamed %s to %s",
 631                                item->string, buf.buf);
 632                if (create_symref(buf.buf, buf2.buf, buf3.buf))
 633                        die("creating '%s' failed", buf.buf);
 634        }
 635        return 0;
 636}
 637
 638static int remove_branches(struct string_list *branches)
 639{
 640        int i, result = 0;
 641        for (i = 0; i < branches->nr; i++) {
 642                struct string_list_item *item = branches->items + i;
 643                const char *refname = item->string;
 644                unsigned char *sha1 = item->util;
 645
 646                if (delete_ref(refname, sha1, 0))
 647                        result |= error("Could not remove branch %s", refname);
 648        }
 649        return result;
 650}
 651
 652static int rm(int argc, const char **argv)
 653{
 654        struct option options[] = {
 655                OPT_END()
 656        };
 657        struct remote *remote;
 658        struct strbuf buf = STRBUF_INIT;
 659        struct known_remotes known_remotes = { NULL, NULL };
 660        struct string_list branches = { NULL, 0, 0, 1 };
 661        struct string_list skipped = { NULL, 0, 0, 1 };
 662        struct branches_for_remote cb_data = {
 663                NULL, &branches, &skipped, &known_remotes
 664        };
 665        int i, result;
 666
 667        if (argc != 2)
 668                usage_with_options(builtin_remote_usage, options);
 669
 670        remote = remote_get(argv[1]);
 671        if (!remote)
 672                die("No such remote: %s", argv[1]);
 673
 674        known_remotes.to_delete = remote;
 675        for_each_remote(add_known_remote, &known_remotes);
 676
 677        strbuf_addf(&buf, "remote.%s", remote->name);
 678        if (git_config_rename_section(buf.buf, NULL) < 1)
 679                return error("Could not remove config section '%s'", buf.buf);
 680
 681        read_branches();
 682        for (i = 0; i < branch_list.nr; i++) {
 683                struct string_list_item *item = branch_list.items + i;
 684                struct branch_info *info = item->util;
 685                if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
 686                        const char *keys[] = { "remote", "merge", NULL }, **k;
 687                        for (k = keys; *k; k++) {
 688                                strbuf_reset(&buf);
 689                                strbuf_addf(&buf, "branch.%s.%s",
 690                                                item->string, *k);
 691                                if (git_config_set(buf.buf, NULL)) {
 692                                        strbuf_release(&buf);
 693                                        return -1;
 694                                }
 695                        }
 696                }
 697        }
 698
 699        /*
 700         * We cannot just pass a function to for_each_ref() which deletes
 701         * the branches one by one, since for_each_ref() relies on cached
 702         * refs, which are invalidated when deleting a branch.
 703         */
 704        cb_data.remote = remote;
 705        result = for_each_ref(add_branch_for_removal, &cb_data);
 706        strbuf_release(&buf);
 707
 708        if (!result)
 709                result = remove_branches(&branches);
 710        string_list_clear(&branches, 1);
 711
 712        if (skipped.nr) {
 713                fprintf(stderr, skipped.nr == 1 ?
 714                        "Note: A non-remote branch was not removed; "
 715                        "to delete it, use:\n" :
 716                        "Note: Non-remote branches were not removed; "
 717                        "to delete them, use:\n");
 718                for (i = 0; i < skipped.nr; i++)
 719                        fprintf(stderr, "  git branch -d %s\n",
 720                                skipped.items[i].string);
 721        }
 722        string_list_clear(&skipped, 0);
 723
 724        return result;
 725}
 726
 727static void clear_push_info(void *util, const char *string)
 728{
 729        struct push_info *info = util;
 730        free(info->dest);
 731        free(info);
 732}
 733
 734static void free_remote_ref_states(struct ref_states *states)
 735{
 736        string_list_clear(&states->new, 0);
 737        string_list_clear(&states->stale, 0);
 738        string_list_clear(&states->tracked, 0);
 739        string_list_clear(&states->heads, 0);
 740        string_list_clear_func(&states->push, clear_push_info);
 741}
 742
 743static int append_ref_to_tracked_list(const char *refname,
 744        const unsigned char *sha1, int flags, void *cb_data)
 745{
 746        struct ref_states *states = cb_data;
 747        struct refspec refspec;
 748
 749        if (flags & REF_ISSYMREF)
 750                return 0;
 751
 752        memset(&refspec, 0, sizeof(refspec));
 753        refspec.dst = (char *)refname;
 754        if (!remote_find_tracking(states->remote, &refspec))
 755                string_list_append(abbrev_branch(refspec.src), &states->tracked);
 756
 757        return 0;
 758}
 759
 760static int get_remote_ref_states(const char *name,
 761                                 struct ref_states *states,
 762                                 int query)
 763{
 764        struct transport *transport;
 765        const struct ref *remote_refs;
 766
 767        states->remote = remote_get(name);
 768        if (!states->remote)
 769                return error("No such remote: %s", name);
 770
 771        read_branches();
 772
 773        if (query) {
 774                transport = transport_get(states->remote, states->remote->url_nr > 0 ?
 775                        states->remote->url[0] : NULL);
 776                remote_refs = transport_get_remote_refs(transport);
 777                transport_disconnect(transport);
 778
 779                states->queried = 1;
 780                if (query & GET_REF_STATES)
 781                        get_ref_states(remote_refs, states);
 782                if (query & GET_HEAD_NAMES)
 783                        get_head_names(remote_refs, states);
 784                if (query & GET_PUSH_REF_STATES)
 785                        get_push_ref_states(remote_refs, states);
 786        } else {
 787                for_each_ref(append_ref_to_tracked_list, states);
 788                sort_string_list(&states->tracked);
 789                get_push_ref_states_noquery(states);
 790        }
 791
 792        return 0;
 793}
 794
 795struct show_info {
 796        struct string_list *list;
 797        struct ref_states *states;
 798        int width, width2;
 799        int any_rebase;
 800};
 801
 802static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
 803{
 804        struct show_info *info = cb_data;
 805        int n = strlen(item->string);
 806        if (n > info->width)
 807                info->width = n;
 808        string_list_insert(item->string, info->list);
 809        return 0;
 810}
 811
 812static int show_remote_info_item(struct string_list_item *item, void *cb_data)
 813{
 814        struct show_info *info = cb_data;
 815        struct ref_states *states = info->states;
 816        const char *name = item->string;
 817
 818        if (states->queried) {
 819                const char *fmt = "%s";
 820                const char *arg = "";
 821                if (string_list_has_string(&states->new, name)) {
 822                        fmt = " new (next fetch will store in remotes/%s)";
 823                        arg = states->remote->name;
 824                } else if (string_list_has_string(&states->tracked, name))
 825                        arg = " tracked";
 826                else if (string_list_has_string(&states->stale, name))
 827                        arg = " stale (use 'git remote prune' to remove)";
 828                else
 829                        arg = " ???";
 830                printf("    %-*s", info->width, name);
 831                printf(fmt, arg);
 832                printf("\n");
 833        } else
 834                printf("    %s\n", name);
 835
 836        return 0;
 837}
 838
 839static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
 840{
 841        struct show_info *show_info = cb_data;
 842        struct ref_states *states = show_info->states;
 843        struct branch_info *branch_info = branch_item->util;
 844        struct string_list_item *item;
 845        int n;
 846
 847        if (!branch_info->merge.nr || !branch_info->remote_name ||
 848            strcmp(states->remote->name, branch_info->remote_name))
 849                return 0;
 850        if ((n = strlen(branch_item->string)) > show_info->width)
 851                show_info->width = n;
 852        if (branch_info->rebase)
 853                show_info->any_rebase = 1;
 854
 855        item = string_list_insert(branch_item->string, show_info->list);
 856        item->util = branch_info;
 857
 858        return 0;
 859}
 860
 861static int show_local_info_item(struct string_list_item *item, void *cb_data)
 862{
 863        struct show_info *show_info = cb_data;
 864        struct branch_info *branch_info = item->util;
 865        struct string_list *merge = &branch_info->merge;
 866        const char *also;
 867        int i;
 868
 869        if (branch_info->rebase && branch_info->merge.nr > 1) {
 870                error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
 871                        item->string);
 872                return 0;
 873        }
 874
 875        printf("    %-*s ", show_info->width, item->string);
 876        if (branch_info->rebase) {
 877                printf("rebases onto remote %s\n", merge->items[0].string);
 878                return 0;
 879        } else if (show_info->any_rebase) {
 880                printf(" merges with remote %s\n", merge->items[0].string);
 881                also = "    and with remote";
 882        } else {
 883                printf("merges with remote %s\n", merge->items[0].string);
 884                also = "   and with remote";
 885        }
 886        for (i = 1; i < merge->nr; i++)
 887                printf("    %-*s %s %s\n", show_info->width, "", also,
 888                       merge->items[i].string);
 889
 890        return 0;
 891}
 892
 893static int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
 894{
 895        struct show_info *show_info = cb_data;
 896        struct push_info *push_info = push_item->util;
 897        struct string_list_item *item;
 898        int n;
 899        if ((n = strlen(push_item->string)) > show_info->width)
 900                show_info->width = n;
 901        if ((n = strlen(push_info->dest)) > show_info->width2)
 902                show_info->width2 = n;
 903        item = string_list_append(push_item->string, show_info->list);
 904        item->util = push_item->util;
 905        return 0;
 906}
 907
 908/*
 909 * Sorting comparison for a string list that has push_info
 910 * structs in its util field
 911 */
 912static int cmp_string_with_push(const void *va, const void *vb)
 913{
 914        const struct string_list_item *a = va;
 915        const struct string_list_item *b = vb;
 916        const struct push_info *a_push = a->util;
 917        const struct push_info *b_push = b->util;
 918        int cmp = strcmp(a->string, b->string);
 919        return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
 920}
 921
 922static int show_push_info_item(struct string_list_item *item, void *cb_data)
 923{
 924        struct show_info *show_info = cb_data;
 925        struct push_info *push_info = item->util;
 926        char *src = item->string, *status = NULL;
 927
 928        switch (push_info->status) {
 929        case PUSH_STATUS_CREATE:
 930                status = "create";
 931                break;
 932        case PUSH_STATUS_DELETE:
 933                status = "delete";
 934                src = "(none)";
 935                break;
 936        case PUSH_STATUS_UPTODATE:
 937                status = "up to date";
 938                break;
 939        case PUSH_STATUS_FASTFORWARD:
 940                status = "fast forwardable";
 941                break;
 942        case PUSH_STATUS_OUTOFDATE:
 943                status = "local out of date";
 944                break;
 945        case PUSH_STATUS_NOTQUERIED:
 946                break;
 947        }
 948        if (status)
 949                printf("    %-*s %s to %-*s (%s)\n", show_info->width, src,
 950                        push_info->forced ? "forces" : "pushes",
 951                        show_info->width2, push_info->dest, status);
 952        else
 953                printf("    %-*s %s to %s\n", show_info->width, src,
 954                        push_info->forced ? "forces" : "pushes",
 955                        push_info->dest);
 956        return 0;
 957}
 958
 959static int show(int argc, const char **argv)
 960{
 961        int no_query = 0, result = 0, query_flag = 0;
 962        struct option options[] = {
 963                OPT_GROUP("show specific options"),
 964                OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
 965                OPT_END()
 966        };
 967        struct ref_states states;
 968        struct string_list info_list = { NULL, 0, 0, 0 };
 969        struct show_info info;
 970
 971        argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
 972                             0);
 973
 974        if (argc < 1)
 975                return show_all();
 976
 977        if (!no_query)
 978                query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
 979
 980        memset(&states, 0, sizeof(states));
 981        memset(&info, 0, sizeof(info));
 982        info.states = &states;
 983        info.list = &info_list;
 984        for (; argc; argc--, argv++) {
 985                int i;
 986                const char **url;
 987                int url_nr;
 988
 989                get_remote_ref_states(*argv, &states, query_flag);
 990
 991                printf("* remote %s\n", *argv);
 992                printf("  Fetch URL: %s\n", states.remote->url_nr > 0 ?
 993                        states.remote->url[0] : "(no URL)");
 994                if (states.remote->pushurl_nr) {
 995                        url = states.remote->pushurl;
 996                        url_nr = states.remote->pushurl_nr;
 997                } else {
 998                        url = states.remote->url;
 999                        url_nr = states.remote->url_nr;
1000                }
1001                for (i=0; i < url_nr; i++)
1002                        printf("  Push  URL: %s\n", url[i]);
1003                if (!i)
1004                        printf("  Push  URL: %s\n", "(no URL)");
1005                if (no_query)
1006                        printf("  HEAD branch: (not queried)\n");
1007                else if (!states.heads.nr)
1008                        printf("  HEAD branch: (unknown)\n");
1009                else if (states.heads.nr == 1)
1010                        printf("  HEAD branch: %s\n", states.heads.items[0].string);
1011                else {
1012                        printf("  HEAD branch (remote HEAD is ambiguous,"
1013                               " may be one of the following):\n");
1014                        for (i = 0; i < states.heads.nr; i++)
1015                                printf("    %s\n", states.heads.items[i].string);
1016                }
1017
1018                /* remote branch info */
1019                info.width = 0;
1020                for_each_string_list(add_remote_to_show_info, &states.new, &info);
1021                for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
1022                for_each_string_list(add_remote_to_show_info, &states.stale, &info);
1023                if (info.list->nr)
1024                        printf("  Remote branch%s:%s\n",
1025                               info.list->nr > 1 ? "es" : "",
1026                                no_query ? " (status not queried)" : "");
1027                for_each_string_list(show_remote_info_item, info.list, &info);
1028                string_list_clear(info.list, 0);
1029
1030                /* git pull info */
1031                info.width = 0;
1032                info.any_rebase = 0;
1033                for_each_string_list(add_local_to_show_info, &branch_list, &info);
1034                if (info.list->nr)
1035                        printf("  Local branch%s configured for 'git pull':\n",
1036                               info.list->nr > 1 ? "es" : "");
1037                for_each_string_list(show_local_info_item, info.list, &info);
1038                string_list_clear(info.list, 0);
1039
1040                /* git push info */
1041                if (states.remote->mirror)
1042                        printf("  Local refs will be mirrored by 'git push'\n");
1043
1044                info.width = info.width2 = 0;
1045                for_each_string_list(add_push_to_show_info, &states.push, &info);
1046                qsort(info.list->items, info.list->nr,
1047                        sizeof(*info.list->items), cmp_string_with_push);
1048                if (info.list->nr)
1049                        printf("  Local ref%s configured for 'git push'%s:\n",
1050                                info.list->nr > 1 ? "s" : "",
1051                                no_query ? " (status not queried)" : "");
1052                for_each_string_list(show_push_info_item, info.list, &info);
1053                string_list_clear(info.list, 0);
1054
1055                free_remote_ref_states(&states);
1056        }
1057
1058        return result;
1059}
1060
1061static int set_head(int argc, const char **argv)
1062{
1063        int i, opt_a = 0, opt_d = 0, result = 0;
1064        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
1065        char *head_name = NULL;
1066
1067        struct option options[] = {
1068                OPT_GROUP("set-head specific options"),
1069                OPT_BOOLEAN('a', "auto", &opt_a,
1070                            "set refs/remotes/<name>/HEAD according to remote"),
1071                OPT_BOOLEAN('d', "delete", &opt_d,
1072                            "delete refs/remotes/<name>/HEAD"),
1073                OPT_END()
1074        };
1075        argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
1076                             0);
1077        if (argc)
1078                strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
1079
1080        if (!opt_a && !opt_d && argc == 2) {
1081                head_name = xstrdup(argv[1]);
1082        } else if (opt_a && !opt_d && argc == 1) {
1083                struct ref_states states;
1084                memset(&states, 0, sizeof(states));
1085                get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
1086                if (!states.heads.nr)
1087                        result |= error("Cannot determine remote HEAD");
1088                else if (states.heads.nr > 1) {
1089                        result |= error("Multiple remote HEAD branches. "
1090                                        "Please choose one explicitly with:");
1091                        for (i = 0; i < states.heads.nr; i++)
1092                                fprintf(stderr, "  git remote set-head %s %s\n",
1093                                        argv[0], states.heads.items[i].string);
1094                } else
1095                        head_name = xstrdup(states.heads.items[0].string);
1096                free_remote_ref_states(&states);
1097        } else if (opt_d && !opt_a && argc == 1) {
1098                if (delete_ref(buf.buf, NULL, REF_NODEREF))
1099                        result |= error("Could not delete %s", buf.buf);
1100        } else
1101                usage_with_options(builtin_remote_usage, options);
1102
1103        if (head_name) {
1104                unsigned char sha1[20];
1105                strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
1106                /* make sure it's valid */
1107                if (!resolve_ref(buf2.buf, sha1, 1, NULL))
1108                        result |= error("Not a valid ref: %s", buf2.buf);
1109                else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
1110                        result |= error("Could not setup %s", buf.buf);
1111                if (opt_a)
1112                        printf("%s/HEAD set to %s\n", argv[0], head_name);
1113                free(head_name);
1114        }
1115
1116        strbuf_release(&buf);
1117        strbuf_release(&buf2);
1118        return result;
1119}
1120
1121static int prune(int argc, const char **argv)
1122{
1123        int dry_run = 0, result = 0;
1124        struct option options[] = {
1125                OPT_GROUP("prune specific options"),
1126                OPT__DRY_RUN(&dry_run),
1127                OPT_END()
1128        };
1129
1130        argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
1131                             0);
1132
1133        if (argc < 1)
1134                usage_with_options(builtin_remote_usage, options);
1135
1136        for (; argc; argc--, argv++)
1137                result |= prune_remote(*argv, dry_run);
1138
1139        return result;
1140}
1141
1142static int prune_remote(const char *remote, int dry_run)
1143{
1144        int result = 0, i;
1145        struct ref_states states;
1146        const char *dangling_msg = dry_run
1147                ? " %s will become dangling!\n"
1148                : " %s has become dangling!\n";
1149
1150        memset(&states, 0, sizeof(states));
1151        get_remote_ref_states(remote, &states, GET_REF_STATES);
1152
1153        if (states.stale.nr) {
1154                printf("Pruning %s\n", remote);
1155                printf("URL: %s\n",
1156                       states.remote->url_nr
1157                       ? states.remote->url[0]
1158                       : "(no URL)");
1159        }
1160
1161        for (i = 0; i < states.stale.nr; i++) {
1162                const char *refname = states.stale.items[i].util;
1163
1164                if (!dry_run)
1165                        result |= delete_ref(refname, NULL, 0);
1166
1167                printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
1168                       abbrev_ref(refname, "refs/remotes/"));
1169                warn_dangling_symref(stdout, dangling_msg, refname);
1170        }
1171
1172        free_remote_ref_states(&states);
1173        return result;
1174}
1175
1176static int get_one_remote_for_update(struct remote *remote, void *priv)
1177{
1178        struct string_list *list = priv;
1179        if (!remote->skip_default_update)
1180                string_list_append(remote->name, list);
1181        return 0;
1182}
1183
1184static struct remote_group {
1185        const char *name;
1186        struct string_list *list;
1187} remote_group;
1188
1189static int get_remote_group(const char *key, const char *value, void *num_hits)
1190{
1191        if (!prefixcmp(key, "remotes.") &&
1192                        !strcmp(key + 8, remote_group.name)) {
1193                /* split list by white space */
1194                int space = strcspn(value, " \t\n");
1195                while (*value) {
1196                        if (space > 1) {
1197                                string_list_append(xstrndup(value, space),
1198                                                remote_group.list);
1199                                ++*((int *)num_hits);
1200                        }
1201                        value += space + (value[space] != '\0');
1202                        space = strcspn(value, " \t\n");
1203                }
1204        }
1205
1206        return 0;
1207}
1208
1209static int update(int argc, const char **argv)
1210{
1211        int i, result = 0, prune = 0;
1212        struct string_list list = { NULL, 0, 0, 0 };
1213        static const char *default_argv[] = { NULL, "default", NULL };
1214        struct option options[] = {
1215                OPT_GROUP("update specific options"),
1216                OPT_BOOLEAN('p', "prune", &prune,
1217                            "prune remotes after fetching"),
1218                OPT_END()
1219        };
1220
1221        argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
1222                             PARSE_OPT_KEEP_ARGV0);
1223        if (argc < 2) {
1224                argc = 2;
1225                argv = default_argv;
1226        }
1227
1228        remote_group.list = &list;
1229        for (i = 1; i < argc; i++) {
1230                int groups_found = 0;
1231                remote_group.name = argv[i];
1232                result = git_config(get_remote_group, &groups_found);
1233                if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
1234                        struct remote *remote;
1235                        if (!remote_is_configured(argv[i]))
1236                                die("No such remote or remote group: %s",
1237                                    argv[i]);
1238                        remote = remote_get(argv[i]);
1239                        string_list_append(remote->name, remote_group.list);
1240                }
1241        }
1242
1243        if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
1244                result = for_each_remote(get_one_remote_for_update, &list);
1245
1246        for (i = 0; i < list.nr; i++) {
1247                int err = fetch_remote(list.items[i].string);
1248                result |= err;
1249                if (!err && prune)
1250                        result |= prune_remote(list.items[i].string, 0);
1251        }
1252
1253        /* all names were strdup()ed or strndup()ed */
1254        list.strdup_strings = 1;
1255        string_list_clear(&list, 0);
1256
1257        return result;
1258}
1259
1260static int get_one_entry(struct remote *remote, void *priv)
1261{
1262        struct string_list *list = priv;
1263        struct strbuf url_buf = STRBUF_INIT;
1264        const char **url;
1265        int i, url_nr;
1266
1267        if (remote->url_nr > 0) {
1268                strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
1269                string_list_append(remote->name, list)->util =
1270                                strbuf_detach(&url_buf, NULL);
1271        } else
1272                string_list_append(remote->name, list)->util = NULL;
1273        if (remote->pushurl_nr) {
1274                url = remote->pushurl;
1275                url_nr = remote->pushurl_nr;
1276        } else {
1277                url = remote->url;
1278                url_nr = remote->url_nr;
1279        }
1280        for (i = 0; i < url_nr; i++)
1281        {
1282                strbuf_addf(&url_buf, "%s (push)", url[i]);
1283                string_list_append(remote->name, list)->util =
1284                                strbuf_detach(&url_buf, NULL);
1285        }
1286
1287        return 0;
1288}
1289
1290static int show_all(void)
1291{
1292        struct string_list list = { NULL, 0, 0 };
1293        int result;
1294
1295        list.strdup_strings = 1;
1296        result = for_each_remote(get_one_entry, &list);
1297
1298        if (!result) {
1299                int i;
1300
1301                sort_string_list(&list);
1302                for (i = 0; i < list.nr; i++) {
1303                        struct string_list_item *item = list.items + i;
1304                        if (verbose)
1305                                printf("%s\t%s\n", item->string,
1306                                        item->util ? (const char *)item->util : "");
1307                        else {
1308                                if (i && !strcmp((item - 1)->string, item->string))
1309                                        continue;
1310                                printf("%s\n", item->string);
1311                        }
1312                }
1313        }
1314        string_list_clear(&list, 1);
1315        return result;
1316}
1317
1318int cmd_remote(int argc, const char **argv, const char *prefix)
1319{
1320        struct option options[] = {
1321                OPT__VERBOSE(&verbose),
1322                OPT_END()
1323        };
1324        int result;
1325
1326        argc = parse_options(argc, argv, prefix, options, builtin_remote_usage,
1327                PARSE_OPT_STOP_AT_NON_OPTION);
1328
1329        if (argc < 1)
1330                result = show_all();
1331        else if (!strcmp(argv[0], "add"))
1332                result = add(argc, argv);
1333        else if (!strcmp(argv[0], "rename"))
1334                result = mv(argc, argv);
1335        else if (!strcmp(argv[0], "rm"))
1336                result = rm(argc, argv);
1337        else if (!strcmp(argv[0], "set-head"))
1338                result = set_head(argc, argv);
1339        else if (!strcmp(argv[0], "show"))
1340                result = show(argc, argv);
1341        else if (!strcmp(argv[0], "prune"))
1342                result = prune(argc, argv);
1343        else if (!strcmp(argv[0], "update"))
1344                result = update(argc, argv);
1345        else {
1346                error("Unknown subcommand: %s", argv[0]);
1347                usage_with_options(builtin_remote_usage, options);
1348        }
1349
1350        return result ? 1 : 0;
1351}