builtin-remote: new show output style
[gitweb.git] / builtin-remote.c
index 640e4dafbfea25791ec17cb1281c7095d059e2c9..379826eed580f9179fd2d4887e14720f1e639ccf 100644 (file)
@@ -149,6 +149,7 @@ static int add(int argc, const char **argv)
 struct branch_info {
        char *remote_name;
        struct string_list merge;
+       int rebase;
 };
 
 static struct string_list branch_list;
@@ -165,10 +166,11 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 static int config_read_branches(const char *key, const char *value, void *cb)
 {
        if (!prefixcmp(key, "branch.")) {
+               const char *orig_key = key;
                char *name;
                struct string_list_item *item;
                struct branch_info *info;
-               enum { REMOTE, MERGE } type;
+               enum { REMOTE, MERGE, REBASE } type;
 
                key += 7;
                if (!postfixcmp(key, ".remote")) {
@@ -177,6 +179,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                } else if (!postfixcmp(key, ".merge")) {
                        name = xstrndup(key, strlen(key) - 6);
                        type = MERGE;
+               } else if (!postfixcmp(key, ".rebase")) {
+                       name = xstrndup(key, strlen(key) - 7);
+                       type = REBASE;
                } else
                        return 0;
 
@@ -187,9 +192,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                info = item->util;
                if (type == REMOTE) {
                        if (info->remote_name)
-                               warning("more than one branch.%s", key);
+                               warning("more than one %s", orig_key);
                        info->remote_name = xstrdup(value);
-               } else {
+               } else if (type == MERGE) {
                        char *space = strchr(value, ' ');
                        value = abbrev_branch(value);
                        while (space) {
@@ -200,7 +205,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                                space = strchr(value, ' ');
                        }
                        string_list_append(xstrdup(value), &info->merge);
-               }
+               } else
+                       info->rebase = git_config_bool(orig_key, value);
        }
        return 0;
 }
@@ -215,6 +221,7 @@ static void read_branches(void)
 struct ref_states {
        struct remote *remote;
        struct string_list new, stale, tracked, heads;
+       int queried;
 };
 
 static int handle_one_branch(const char *refname,
@@ -637,20 +644,6 @@ static int rm(int argc, const char **argv)
        return result;
 }
 
-static void show_list(const char *title, struct string_list *list,
-                     const char *extra_arg)
-{
-       int i;
-
-       if (!list->nr)
-               return;
-
-       printf(title, list->nr > 1 ? "es" : "", extra_arg);
-       printf("\n");
-       for (i = 0; i < list->nr; i++)
-               printf("    %s\n", list->items[i].string);
-}
-
 static void free_remote_ref_states(struct ref_states *states)
 {
        string_list_clear(&states->new, 0);
@@ -695,6 +688,7 @@ static int get_remote_ref_states(const char *name,
                remote_refs = transport_get_remote_refs(transport);
                transport_disconnect(transport);
 
+               states->queried = 1;
                if (query & GET_REF_STATES)
                        get_ref_states(remote_refs, states);
                if (query & GET_HEAD_NAMES)
@@ -707,6 +701,104 @@ static int get_remote_ref_states(const char *name,
        return 0;
 }
 
+struct show_info {
+       struct string_list *list;
+       struct ref_states *states;
+       int width;
+       int any_rebase;
+};
+
+int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
+{
+       struct show_info *info = cb_data;
+       int n = strlen(item->string);
+       if (n > info->width)
+               info->width = n;
+       string_list_insert(item->string, info->list);
+       return 0;
+}
+
+int show_remote_info_item(struct string_list_item *item, void *cb_data)
+{
+       struct show_info *info = cb_data;
+       struct ref_states *states = info->states;
+       const char *name = item->string;
+
+       if (states->queried) {
+               const char *fmt = "%s";
+               const char *arg = "";
+               if (string_list_has_string(&states->new, name)) {
+                       fmt = " new (next fetch will store in remotes/%s)";
+                       arg = states->remote->name;
+               } else if (string_list_has_string(&states->tracked, name))
+                       arg = " tracked";
+               else if (string_list_has_string(&states->stale, name))
+                       arg = " stale (use 'git remote prune' to remove)";
+               else
+                       arg = " ???";
+               printf("    %-*s", info->width, name);
+               printf(fmt, arg);
+               printf("\n");
+       } else
+               printf("    %s\n", name);
+
+       return 0;
+}
+
+int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
+{
+       struct show_info *show_info = cb_data;
+       struct ref_states *states = show_info->states;
+       struct branch_info *branch_info = branch_item->util;
+       struct string_list_item *item;
+       int n;
+
+       if (!branch_info->merge.nr || !branch_info->remote_name ||
+           strcmp(states->remote->name, branch_info->remote_name))
+               return 0;
+       if ((n = strlen(branch_item->string)) > show_info->width)
+               show_info->width = n;
+       if (branch_info->rebase)
+               show_info->any_rebase = 1;
+
+       item = string_list_insert(branch_item->string, show_info->list);
+       item->util = branch_info;
+
+       return 0;
+}
+
+int show_local_info_item(struct string_list_item *item, void *cb_data)
+{
+       struct show_info *show_info = cb_data;
+       struct branch_info *branch_info = item->util;
+       struct string_list *merge = &branch_info->merge;
+       const char *also;
+       int i;
+
+       if (branch_info->rebase && branch_info->merge.nr > 1) {
+               error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
+                       item->string);
+               return 0;
+       }
+
+       printf("    %-*s ", show_info->width, item->string);
+       if (branch_info->rebase) {
+               printf("rebases onto remote %s\n", merge->items[0].string);
+               return 0;
+       } else if (show_info->any_rebase) {
+               printf(" merges with remote %s\n", merge->items[0].string);
+               also = "    and with remote";
+       } else {
+               printf("merges with remote %s\n", merge->items[0].string);
+               also = "   and with remote";
+       }
+       for (i = 1; i < merge->nr; i++)
+               printf("    %-*s %s %s\n", show_info->width, "", also,
+                      merge->items[i].string);
+
+       return 0;
+}
+
 static int show(int argc, const char **argv)
 {
        int no_query = 0, result = 0, query_flag = 0;
@@ -716,6 +808,8 @@ static int show(int argc, const char **argv)
                OPT_END()
        };
        struct ref_states states;
+       struct string_list info_list = { NULL, 0, 0, 0 };
+       struct show_info info;
 
        argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
@@ -726,6 +820,9 @@ static int show(int argc, const char **argv)
                query_flag = (GET_REF_STATES | GET_HEAD_NAMES);
 
        memset(&states, 0, sizeof(states));
+       memset(&info, 0, sizeof(info));
+       info.states = &states;
+       info.list = &info_list;
        for (; argc; argc--, argv++) {
                int i;
 
@@ -747,32 +844,29 @@ static int show(int argc, const char **argv)
                                printf("    %s\n", states.heads.items[i].string);
                }
 
-               for (i = 0; i < branch_list.nr; i++) {
-                       struct string_list_item *branch = branch_list.items + i;
-                       struct branch_info *info = branch->util;
-                       int j;
-
-                       if (!info->merge.nr || strcmp(*argv, info->remote_name))
-                               continue;
-                       printf("  Remote branch%s merged with 'git pull' "
-                               "while on branch %s\n   ",
-                               info->merge.nr > 1 ? "es" : "",
-                               branch->string);
-                       for (j = 0; j < info->merge.nr; j++)
-                               printf(" %s", info->merge.items[j].string);
-                       printf("\n");
-               }
-
-               if (!no_query) {
-                       show_list("  New remote branch%s (next fetch "
-                               "will store in remotes/%s)",
-                               &states.new, states.remote->name);
-                       show_list("  Stale tracking branch%s (use 'git remote "
-                               "prune')", &states.stale, "");
-               }
-
-               show_list("  Tracked remote branch%s", &states.tracked, "");
-
+               /* 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);
+               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);
+               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);
+               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);
+               string_list_clear(info.list, 0);
+
+               /* git push info */
                if (states.remote->push_refspec_nr) {
                        printf("  Local branch%s pushed with 'git push'\n",
                                states.remote->push_refspec_nr > 1 ?