stash: convert branch to builtin
[gitweb.git] / builtin / remote.c
index bc896236952079fc3ae06e23101991169dea64fc..f7edf7f2cb1f5880e5674f0bf5d41672602e52e4 100644 (file)
@@ -7,7 +7,10 @@
 #include "strbuf.h"
 #include "run-command.h"
 #include "refs.h"
+#include "refspec.h"
+#include "object-store.h"
 #include "argv-array.h"
+#include "commit-reach.h"
 
 static const char * const builtin_remote_usage[] = {
        N_("git remote [-v | --verbose]"),
@@ -166,9 +169,9 @@ static int add(int argc, const char **argv)
                OPT_STRING_LIST('t', "track", &track, N_("branch"),
                                N_("branch(es) to track")),
                OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
-               { OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"),
+               { OPTION_CALLBACK, 0, "mirror", &mirror, "(push|fetch)",
                        N_("set up remote as a mirror to push to or fetch from"),
-                       PARSE_OPT_OPTARG, parse_mirror_opt },
+                       PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt },
                OPT_END()
        };
 
@@ -245,7 +248,9 @@ static int add(int argc, const char **argv)
 struct branch_info {
        char *remote_name;
        struct string_list merge;
-       enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase;
+       enum {
+               NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE, REBASE_MERGES
+       } rebase;
 };
 
 static struct string_list branch_list = STRING_LIST_INIT_NODUP;
@@ -306,6 +311,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                                info->rebase = v;
                        else if (!strcmp(value, "preserve"))
                                info->rebase = NORMAL_REBASE;
+                       else if (!strcmp(value, "merges"))
+                               info->rebase = REBASE_MERGES;
                        else if (!strcmp(value, "interactive"))
                                info->rebase = INTERACTIVE_REBASE;
                }
@@ -322,7 +329,7 @@ static void read_branches(void)
 
 struct ref_states {
        struct remote *remote;
-       struct string_list new, stale, tracked, heads, push;
+       struct string_list new_refs, stale, tracked, heads, push;
        int queried;
 };
 
@@ -332,22 +339,21 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
        struct ref *ref, *stale_refs;
        int i;
 
-       for (i = 0; i < states->remote->fetch_refspec_nr; i++)
-               if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
+       for (i = 0; i < states->remote->fetch.nr; i++)
+               if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1))
                        die(_("Could not get fetch map for refspec %s"),
-                               states->remote->fetch_refspec[i]);
+                               states->remote->fetch.raw[i]);
 
-       states->new.strdup_strings = 1;
+       states->new_refs.strdup_strings = 1;
        states->tracked.strdup_strings = 1;
        states->stale.strdup_strings = 1;
        for (ref = fetch_map; ref; ref = ref->next) {
                if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
-                       string_list_append(&states->new, abbrev_branch(ref->name));
+                       string_list_append(&states->new_refs, abbrev_branch(ref->name));
                else
                        string_list_append(&states->tracked, abbrev_branch(ref->name));
        }
-       stale_refs = get_stale_heads(states->remote->fetch,
-                                    states->remote->fetch_refspec_nr, fetch_map);
+       stale_refs = get_stale_heads(&states->remote->fetch, fetch_map);
        for (ref = stale_refs; ref; ref = ref->next) {
                struct string_list_item *item =
                        string_list_append(&states->stale, abbrev_branch(ref->name));
@@ -356,7 +362,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
        free_refs(stale_refs);
        free_refs(fetch_map);
 
-       string_list_sort(&states->new);
+       string_list_sort(&states->new_refs);
        string_list_sort(&states->tracked);
        string_list_sort(&states->stale);
 
@@ -387,8 +393,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
        local_refs = get_local_heads();
        push_map = copy_ref_list(remote_refs);
 
-       match_push_refs(local_refs, &push_map, remote->push_refspec_nr,
-                       remote->push_refspec, MATCH_REFS_NONE);
+       match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);
 
        states->push.strdup_strings = 1;
        for (ref = push_map; ref; ref = ref->next) {
@@ -408,7 +413,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
 
                if (is_null_oid(&ref->new_oid)) {
                        info->status = PUSH_STATUS_DELETE;
-               } else if (!oidcmp(&ref->old_oid, &ref->new_oid))
+               } else if (oideq(&ref->old_oid, &ref->new_oid))
                        info->status = PUSH_STATUS_UPTODATE;
                else if (is_null_oid(&ref->old_oid))
                        info->status = PUSH_STATUS_CREATE;
@@ -434,14 +439,14 @@ static int get_push_ref_states_noquery(struct ref_states *states)
                return 0;
 
        states->push.strdup_strings = 1;
-       if (!remote->push_refspec_nr) {
+       if (!remote->push.nr) {
                item = string_list_append(&states->push, _("(matching)"));
                info = item->util = xcalloc(1, sizeof(struct push_info));
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
        }
-       for (i = 0; i < remote->push_refspec_nr; i++) {
-               struct refspec *spec = remote->push + i;
+       for (i = 0; i < remote->push.nr; i++) {
+               const struct refspec_item *spec = &remote->push.items[i];
                if (spec->matching)
                        item = string_list_append(&states->push, _("(matching)"));
                else if (strlen(spec->src))
@@ -461,7 +466,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 {
        struct ref *ref, *matches;
        struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-       struct refspec refspec;
+       struct refspec_item refspec;
 
        refspec.force = 0;
        refspec.pattern = 1;
@@ -514,7 +519,7 @@ static int add_branch_for_removal(const char *refname,
        const struct object_id *oid, int flags, void *cb_data)
 {
        struct branches_for_remote *branches = cb_data;
-       struct refspec refspec;
+       struct refspec_item refspec;
        struct known_remote *kr;
 
        memset(&refspec, 0, sizeof(refspec));
@@ -546,8 +551,8 @@ static int add_branch_for_removal(const char *refname,
 }
 
 struct rename_info {
-       const char *old;
-       const char *new;
+       const char *old_name;
+       const char *new_name;
        struct string_list *remote_branches;
 };
 
@@ -560,9 +565,9 @@ static int read_remote_branches(const char *refname,
        int flag;
        const char *symref;
 
-       strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
+       strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
        if (starts_with(refname, buf.buf)) {
-               item = string_list_append(rename->remote_branches, xstrdup(refname));
+               item = string_list_append(rename->remote_branches, refname);
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
                                            NULL, &flag);
                if (symref && (flag & REF_ISSYMREF))
@@ -585,12 +590,12 @@ static int migrate_file(struct remote *remote)
                git_config_set_multivar(buf.buf, remote->url[i], "^$", 0);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.push", remote->name);
-       for (i = 0; i < remote->push_refspec_nr; i++)
-               git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0);
+       for (i = 0; i < remote->push.raw_nr; i++)
+               git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", remote->name);
-       for (i = 0; i < remote->fetch_refspec_nr; i++)
-               git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0);
+       for (i = 0; i < remote->fetch.raw_nr; i++)
+               git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0);
        if (remote->origin == REMOTE_REMOTES)
                unlink_or_warn(git_path("remotes/%s", remote->name));
        else if (remote->origin == REMOTE_BRANCHES)
@@ -608,55 +613,55 @@ static int mv(int argc, const char **argv)
        struct remote *oldremote, *newremote;
        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 string_list remote_branches = STRING_LIST_INIT_DUP;
        struct rename_info rename;
        int i, refspec_updated = 0;
 
        if (argc != 3)
                usage_with_options(builtin_remote_rename_usage, options);
 
-       rename.old = argv[1];
-       rename.new = argv[2];
+       rename.old_name = argv[1];
+       rename.new_name = argv[2];
        rename.remote_branches = &remote_branches;
 
-       oldremote = remote_get(rename.old);
+       oldremote = remote_get(rename.old_name);
        if (!remote_is_configured(oldremote, 1))
-               die(_("No such remote: %s"), rename.old);
+               die(_("No such remote: '%s'"), rename.old_name);
 
-       if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
+       if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
 
-       newremote = remote_get(rename.new);
+       newremote = remote_get(rename.new_name);
        if (remote_is_configured(newremote, 1))
-               die(_("remote %s already exists."), rename.new);
+               die(_("remote %s already exists."), rename.new_name);
 
-       strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
+       strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
        if (!valid_fetch_refspec(buf.buf))
-               die(_("'%s' is not a valid remote name"), rename.new);
+               die(_("'%s' is not a valid remote name"), rename.new_name);
 
        strbuf_reset(&buf);
-       strbuf_addf(&buf, "remote.%s", rename.old);
-       strbuf_addf(&buf2, "remote.%s", rename.new);
+       strbuf_addf(&buf, "remote.%s", rename.old_name);
+       strbuf_addf(&buf2, "remote.%s", rename.new_name);
        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
                return error(_("Could not rename config section '%s' to '%s'"),
                                buf.buf, buf2.buf);
 
        strbuf_reset(&buf);
-       strbuf_addf(&buf, "remote.%s.fetch", rename.new);
+       strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
        git_config_set_multivar(buf.buf, NULL, NULL, 1);
-       strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
-       for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
+       strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
+       for (i = 0; i < oldremote->fetch.raw_nr; i++) {
                char *ptr;
 
                strbuf_reset(&buf2);
-               strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
+               strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
                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));
+                                     strlen(rename.old_name), rename.new_name,
+                                     strlen(rename.new_name));
                } else
                        warning(_("Not updating non-default fetch refspec\n"
                                  "\t%s\n"
@@ -670,10 +675,10 @@ static int mv(int argc, const char **argv)
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
                struct branch_info *info = item->util;
-               if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
+               if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) {
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
-                       git_config_set(buf.buf, rename.new);
+                       git_config_set(buf.buf, rename.new_name);
                }
        }
 
@@ -690,10 +695,10 @@ static int mv(int argc, const char **argv)
                int flag = 0;
                struct object_id oid;
 
-               read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag);
+               read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
                if (!(flag & REF_ISSYMREF))
                        continue;
-               if (delete_ref(NULL, item->string, NULL, REF_NODEREF))
+               if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
                        die(_("deleting '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
@@ -703,8 +708,8 @@ static int mv(int argc, const char **argv)
                        continue;
                strbuf_reset(&buf);
                strbuf_addstr(&buf, item->string);
-               strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
-                               rename.new, strlen(rename.new));
+               strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
+                               rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf2);
                strbuf_addf(&buf2, "remote: renamed %s to %s",
                                item->string, buf.buf);
@@ -718,18 +723,19 @@ static int mv(int argc, const char **argv)
                        continue;
                strbuf_reset(&buf);
                strbuf_addstr(&buf, item->string);
-               strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
-                               rename.new, strlen(rename.new));
+               strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
+                               rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf2);
                strbuf_addstr(&buf2, item->util);
-               strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
-                               rename.new, strlen(rename.new));
+               strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name),
+                               rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf3);
                strbuf_addf(&buf3, "remote: renamed %s to %s",
                                item->string, buf.buf);
                if (create_symref(buf.buf, buf2.buf, buf3.buf))
                        die(_("creating '%s' failed"), buf.buf);
        }
+       string_list_clear(&remote_branches, 1);
        return 0;
 }
 
@@ -756,7 +762,7 @@ static int rm(int argc, const char **argv)
 
        remote = remote_get(argv[1]);
        if (!remote_is_configured(remote, 1))
-               die(_("No such remote: %s"), argv[1]);
+               die(_("No such remote: '%s'"), argv[1]);
 
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
@@ -788,7 +794,7 @@ static int rm(int argc, const char **argv)
        strbuf_release(&buf);
 
        if (!result)
-               result = delete_refs("remote: remove", &branches, REF_NODEREF);
+               result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
        string_list_clear(&branches, 0);
 
        if (skipped.nr) {
@@ -822,7 +828,7 @@ static void clear_push_info(void *util, const char *string)
 
 static void free_remote_ref_states(struct ref_states *states)
 {
-       string_list_clear(&states->new, 0);
+       string_list_clear(&states->new_refs, 0);
        string_list_clear(&states->stale, 1);
        string_list_clear(&states->tracked, 0);
        string_list_clear(&states->heads, 0);
@@ -833,7 +839,7 @@ static int append_ref_to_tracked_list(const char *refname,
        const struct object_id *oid, int flags, void *cb_data)
 {
        struct ref_states *states = cb_data;
-       struct refspec refspec;
+       struct refspec_item refspec;
 
        if (flags & REF_ISSYMREF)
                return 0;
@@ -855,14 +861,14 @@ static int get_remote_ref_states(const char *name,
 
        states->remote = remote_get(name);
        if (!states->remote)
-               return error(_("No such remote: %s"), name);
+               return error(_("No such remote: '%s'"), name);
 
        read_branches();
 
        if (query) {
                transport = transport_get(states->remote, states->remote->url_nr > 0 ?
                        states->remote->url[0] : NULL);
-               remote_refs = transport_get_remote_refs(transport);
+               remote_refs = transport_get_remote_refs(transport, NULL);
                transport_disconnect(transport);
 
                states->queried = 1;
@@ -907,7 +913,7 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
        if (states->queried) {
                const char *fmt = "%s";
                const char *arg = "";
-               if (string_list_has_string(&states->new, name)) {
+               if (string_list_has_string(&states->new_refs, name)) {
                        fmt = _(" new (next fetch will store in remotes/%s)");
                        arg = states->remote->name;
                } else if (string_list_has_string(&states->tracked, name))
@@ -963,9 +969,15 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
 
        printf("    %-*s ", show_info->width, item->string);
        if (branch_info->rebase) {
-               printf_ln(branch_info->rebase == INTERACTIVE_REBASE
-                         ? _("rebases interactively onto remote %s")
-                         : _("rebases onto remote %s"), merge->items[0].string);
+               const char *msg;
+               if (branch_info->rebase == INTERACTIVE_REBASE)
+                       msg = _("rebases interactively onto remote %s");
+               else if (branch_info->rebase == REBASE_MERGES)
+                       msg = _("rebases interactively (with merges) onto "
+                               "remote %s");
+               else
+                       msg = _("rebases onto remote %s");
+               printf_ln(msg, merge->items[0].string);
                return 0;
        } else if (show_info->any_rebase) {
                printf_ln(_(" merges with remote %s"), merge->items[0].string);
@@ -1176,7 +1188,7 @@ static int show(int argc, const char **argv)
 
                /* remote branch info */
                info.width = 0;
-               for_each_string_list(&states.new, add_remote_to_show_info, &info);
+               for_each_string_list(&states.new_refs, 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)
@@ -1255,7 +1267,7 @@ static int set_head(int argc, const char **argv)
                        head_name = xstrdup(states.heads.items[0].string);
                free_remote_ref_states(&states);
        } else if (opt_d && !opt_a && argc == 1) {
-               if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF))
+               if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
                        result |= error(_("Could not delete %s"), buf.buf);
        } else
                usage_with_options(builtin_remote_sethead_usage, options);