struct branch_info {
char *remote_name;
struct string_list merge;
+ int rebase;
};
static struct string_list branch_list;
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")) {
} 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;
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) {
space = strchr(value, ' ');
}
string_list_append(xstrdup(value), &info->merge);
- }
+ } else
+ info->rebase = git_config_bool(orig_key, value);
}
return 0;
}
struct ref_states {
struct remote *remote;
struct string_list new, stale, tracked, heads;
+ int queried;
};
static int handle_one_branch(const char *refname,
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);
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)
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;
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);
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;
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 ?
}
check_remote_track () {
- actual=$(git remote show "$1" | sed -e '1,/Tracked/d') &&
+ actual=$(git remote show "$1" | sed -ne 's|^ \(.*\) tracked$|\1|p')
shift &&
tokens_match "$*" "$actual"
}
* remote origin
URL: $(pwd)/one
HEAD branch: master
- Remote branch merged with 'git pull' while on branch master
- master
- New remote branch (next fetch will store in remotes/origin)
- master
- Tracked remote branches
- master
- side
+ Remote branches:
+ master new (next fetch will store in remotes/origin)
+ side tracked
+ Local branches configured for 'git pull':
+ master merges with remote master
+ octopus merges with remote topic-a
+ and with remote topic-b
+ and with remote topic-c
+ rebase rebases onto remote master
Local branches pushed with 'git push'
master:upstream
+refs/tags/lastbackup
test_expect_success 'show' '
(cd test &&
- git config --add remote.origin.fetch \
- refs/heads/master:refs/heads/upstream &&
+ git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
git fetch &&
+ git branch --track octopus origin/master &&
+ git branch --track rebase origin/master &&
git branch -d -r origin/master &&
git config --add remote.two.url ../two &&
+ git config branch.rebase.rebase true &&
+ git config branch.octopus.merge "topic-a topic-b topic-c" &&
(cd ../one &&
echo 1 > file &&
test_tick &&
git commit -m update file) &&
- git config remote.origin.push \
- refs/heads/master:refs/heads/upstream &&
- git config --add remote.origin.push \
- +refs/tags/lastbackup &&
+ git config remote.origin.push refs/heads/master:refs/heads/upstream &&
+ git config --add remote.origin.push +refs/tags/lastbackup &&
git remote show origin two > output &&
+ git branch -d rebase octopus &&
test_cmp expect output)
'
* remote origin
URL: $(pwd)/one
HEAD branch: (not queried)
- Remote branch merged with 'git pull' while on branch master
- master
- Tracked remote branches
+ Remote branches: (status not queried)
master
side
+ Local branch configured for 'git pull':
+ master merges with remote master
Local branches pushed with 'git push'
master:upstream
+refs/tags/lastbackup