subtree: improve decision on merges kept in split
[gitweb.git] / builtin / remote.c
index 805ffc05cdb80e4a69de4134e757f9c71e8033dc..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,7 +169,7 @@ 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_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;
                }
@@ -332,10 +339,10 @@ 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_refs.strdup_strings = 1;
        states->tracked.strdup_strings = 1;
@@ -346,8 +353,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
                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));
@@ -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));
@@ -562,7 +567,7 @@ static int read_remote_branches(const char *refname,
 
        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,7 +613,7 @@ 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;
 
@@ -621,7 +626,7 @@ static int mv(int argc, const char **argv)
 
        oldremote = remote_get(rename.old_name);
        if (!remote_is_configured(oldremote, 1))
-               die(_("No such remote: %s"), rename.old_name);
+               die(_("No such remote: '%s'"), rename.old_name);
 
        if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
@@ -645,11 +650,11 @@ static int mv(int argc, const char **argv)
        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_name);
-       for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
+       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;
@@ -730,6 +735,7 @@ static int mv(int argc, const char **argv)
                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);
@@ -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;
@@ -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);