Merge branch 'kn/ref-filter-branch-list'
The code to list branches in "git branch" has been consolidated with the more generic ref-filter API. * kn/ref-filter-branch-list: (21 commits) ref-filter: resurrect "strip" as a synonym to "lstrip" branch: implement '--format' option branch: use ref-filter printing APIs branch, tag: use porcelain output ref-filter: allow porcelain to translate messages in the output ref-filter: add an 'rstrip=<N>' option to atoms which deal with refnames ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>' ref-filter: Do not abruptly die when using the 'lstrip=<N>' option ref-filter: rename the 'strip' option to 'lstrip' ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal() ref-filter: introduce refname_atom_parser() ref-filter: introduce refname_atom_parser_internal() ref-filter: make "%(symref)" atom work with the ':short' modifier ref-filter: add support for %(upstream:track,nobracket) ref-filter: make %(upstream:track) prints "[gone]" for invalid upstreams ref-filter: introduce format_ref_array_item() ref-filter: move get_head_description() from branch.c ref-filter: modify "%(objectname:short)" to take length ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>) ref-filter: include reference to 'used_atom' within 'atom_value' ...
This commit is contained in:
296
builtin/branch.c
296
builtin/branch.c
@ -28,6 +28,7 @@ static const char * const builtin_branch_usage[] = {
|
||||
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
|
||||
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
||||
N_("git branch [<options>] [-r | -a] [--points-at]"),
|
||||
N_("git branch [<options>] [-r | -a] [--format]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -37,11 +38,11 @@ static unsigned char head_sha1[20];
|
||||
static int branch_use_color = -1;
|
||||
static char branch_colors[][COLOR_MAXLEN] = {
|
||||
GIT_COLOR_RESET,
|
||||
GIT_COLOR_NORMAL, /* PLAIN */
|
||||
GIT_COLOR_RED, /* REMOTE */
|
||||
GIT_COLOR_NORMAL, /* LOCAL */
|
||||
GIT_COLOR_GREEN, /* CURRENT */
|
||||
GIT_COLOR_BLUE, /* UPSTREAM */
|
||||
GIT_COLOR_NORMAL, /* PLAIN */
|
||||
GIT_COLOR_RED, /* REMOTE */
|
||||
GIT_COLOR_NORMAL, /* LOCAL */
|
||||
GIT_COLOR_GREEN, /* CURRENT */
|
||||
GIT_COLOR_BLUE, /* UPSTREAM */
|
||||
};
|
||||
enum color_branch {
|
||||
BRANCH_COLOR_RESET = 0,
|
||||
@ -280,195 +281,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
|
||||
int show_upstream_ref)
|
||||
{
|
||||
int ours, theirs;
|
||||
char *ref = NULL;
|
||||
struct branch *branch = branch_get(branch_name);
|
||||
const char *upstream;
|
||||
struct strbuf fancy = STRBUF_INIT;
|
||||
int upstream_is_gone = 0;
|
||||
int added_decoration = 1;
|
||||
|
||||
if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
|
||||
if (!upstream)
|
||||
return;
|
||||
upstream_is_gone = 1;
|
||||
}
|
||||
|
||||
if (show_upstream_ref) {
|
||||
ref = shorten_unambiguous_ref(upstream, 0);
|
||||
if (want_color(branch_use_color))
|
||||
strbuf_addf(&fancy, "%s%s%s",
|
||||
branch_get_color(BRANCH_COLOR_UPSTREAM),
|
||||
ref, branch_get_color(BRANCH_COLOR_RESET));
|
||||
else
|
||||
strbuf_addstr(&fancy, ref);
|
||||
}
|
||||
|
||||
if (upstream_is_gone) {
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
|
||||
else
|
||||
added_decoration = 0;
|
||||
} else if (!ours && !theirs) {
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, _("[%s]"), fancy.buf);
|
||||
else
|
||||
added_decoration = 0;
|
||||
} else if (!ours) {
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
|
||||
else
|
||||
strbuf_addf(stat, _("[behind %d]"), theirs);
|
||||
|
||||
} else if (!theirs) {
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
|
||||
else
|
||||
strbuf_addf(stat, _("[ahead %d]"), ours);
|
||||
} else {
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
|
||||
fancy.buf, ours, theirs);
|
||||
else
|
||||
strbuf_addf(stat, _("[ahead %d, behind %d]"),
|
||||
ours, theirs);
|
||||
}
|
||||
strbuf_release(&fancy);
|
||||
if (added_decoration)
|
||||
strbuf_addch(stat, ' ');
|
||||
free(ref);
|
||||
}
|
||||
|
||||
static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
|
||||
struct ref_filter *filter, const char *refname)
|
||||
{
|
||||
struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
|
||||
const char *sub = _(" **** invalid ref ****");
|
||||
struct commit *commit = item->commit;
|
||||
|
||||
if (!parse_commit(commit)) {
|
||||
pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
|
||||
sub = subject.buf;
|
||||
}
|
||||
|
||||
if (item->kind == FILTER_REFS_BRANCHES)
|
||||
fill_tracking_info(&stat, refname, filter->verbose > 1);
|
||||
|
||||
strbuf_addf(out, " %s %s%s",
|
||||
find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
|
||||
stat.buf, sub);
|
||||
strbuf_release(&stat);
|
||||
strbuf_release(&subject);
|
||||
}
|
||||
|
||||
static char *get_head_description(void)
|
||||
{
|
||||
struct strbuf desc = STRBUF_INIT;
|
||||
struct wt_status_state state;
|
||||
memset(&state, 0, sizeof(state));
|
||||
wt_status_get_state(&state, 1);
|
||||
if (state.rebase_in_progress ||
|
||||
state.rebase_interactive_in_progress)
|
||||
strbuf_addf(&desc, _("(no branch, rebasing %s)"),
|
||||
state.branch);
|
||||
else if (state.bisect_in_progress)
|
||||
strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
|
||||
state.branch);
|
||||
else if (state.detached_from) {
|
||||
if (state.detached_at)
|
||||
/* TRANSLATORS: make sure this matches
|
||||
"HEAD detached at " in wt-status.c */
|
||||
strbuf_addf(&desc, _("(HEAD detached at %s)"),
|
||||
state.detached_from);
|
||||
else
|
||||
/* TRANSLATORS: make sure this matches
|
||||
"HEAD detached from " in wt-status.c */
|
||||
strbuf_addf(&desc, _("(HEAD detached from %s)"),
|
||||
state.detached_from);
|
||||
}
|
||||
else
|
||||
strbuf_addstr(&desc, _("(no branch)"));
|
||||
free(state.branch);
|
||||
free(state.onto);
|
||||
free(state.detached_from);
|
||||
return strbuf_detach(&desc, NULL);
|
||||
}
|
||||
|
||||
static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
|
||||
struct ref_filter *filter, const char *remote_prefix)
|
||||
{
|
||||
char c;
|
||||
int current = 0;
|
||||
int color;
|
||||
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
|
||||
const char *prefix_to_show = "";
|
||||
const char *prefix_to_skip = NULL;
|
||||
const char *desc = item->refname;
|
||||
char *to_free = NULL;
|
||||
|
||||
switch (item->kind) {
|
||||
case FILTER_REFS_BRANCHES:
|
||||
prefix_to_skip = "refs/heads/";
|
||||
skip_prefix(desc, prefix_to_skip, &desc);
|
||||
if (!filter->detached && !strcmp(desc, head))
|
||||
current = 1;
|
||||
else
|
||||
color = BRANCH_COLOR_LOCAL;
|
||||
break;
|
||||
case FILTER_REFS_REMOTES:
|
||||
prefix_to_skip = "refs/remotes/";
|
||||
skip_prefix(desc, prefix_to_skip, &desc);
|
||||
color = BRANCH_COLOR_REMOTE;
|
||||
prefix_to_show = remote_prefix;
|
||||
break;
|
||||
case FILTER_REFS_DETACHED_HEAD:
|
||||
desc = to_free = get_head_description();
|
||||
current = 1;
|
||||
break;
|
||||
default:
|
||||
color = BRANCH_COLOR_PLAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
c = ' ';
|
||||
if (current) {
|
||||
c = '*';
|
||||
color = BRANCH_COLOR_CURRENT;
|
||||
}
|
||||
|
||||
strbuf_addf(&name, "%s%s", prefix_to_show, desc);
|
||||
if (filter->verbose) {
|
||||
int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
|
||||
strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
|
||||
maxwidth + utf8_compensation, name.buf,
|
||||
branch_get_color(BRANCH_COLOR_RESET));
|
||||
} else
|
||||
strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
|
||||
name.buf, branch_get_color(BRANCH_COLOR_RESET));
|
||||
|
||||
if (item->symref) {
|
||||
const char *symref = item->symref;
|
||||
if (prefix_to_skip)
|
||||
skip_prefix(symref, prefix_to_skip, &symref);
|
||||
strbuf_addf(&out, " -> %s", symref);
|
||||
}
|
||||
else if (filter->verbose)
|
||||
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
|
||||
add_verbose_info(&out, item, filter, desc);
|
||||
if (column_active(colopts)) {
|
||||
assert(!filter->verbose && "--column and --verbose are incompatible");
|
||||
string_list_append(&output, out.buf);
|
||||
} else {
|
||||
printf("%s\n", out.buf);
|
||||
}
|
||||
strbuf_release(&name);
|
||||
strbuf_release(&out);
|
||||
free(to_free);
|
||||
}
|
||||
|
||||
static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
||||
{
|
||||
int i, max = 0;
|
||||
@ -479,7 +291,12 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
||||
|
||||
skip_prefix(it->refname, "refs/heads/", &desc);
|
||||
skip_prefix(it->refname, "refs/remotes/", &desc);
|
||||
w = utf8_strwidth(desc);
|
||||
if (it->kind == FILTER_REFS_DETACHED_HEAD) {
|
||||
char *head_desc = get_head_description();
|
||||
w = utf8_strwidth(head_desc);
|
||||
free(head_desc);
|
||||
} else
|
||||
w = utf8_strwidth(desc);
|
||||
|
||||
if (it->kind == FILTER_REFS_REMOTES)
|
||||
w += remote_bonus;
|
||||
@ -489,12 +306,73 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
||||
return max;
|
||||
}
|
||||
|
||||
static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
|
||||
static const char *quote_literal_for_format(const char *s)
|
||||
{
|
||||
static struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
strbuf_reset(&buf);
|
||||
while (*s) {
|
||||
const char *ep = strchrnul(s, '%');
|
||||
if (s < ep)
|
||||
strbuf_add(&buf, s, ep - s);
|
||||
if (*ep == '%') {
|
||||
strbuf_addstr(&buf, "%%");
|
||||
s = ep + 1;
|
||||
} else {
|
||||
s = ep;
|
||||
}
|
||||
}
|
||||
return buf.buf;
|
||||
}
|
||||
|
||||
static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
|
||||
{
|
||||
struct strbuf fmt = STRBUF_INIT;
|
||||
struct strbuf local = STRBUF_INIT;
|
||||
struct strbuf remote = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else) %%(end)",
|
||||
branch_get_color(BRANCH_COLOR_CURRENT));
|
||||
|
||||
if (filter->verbose) {
|
||||
strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
|
||||
strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
|
||||
strbuf_addf(&local, " %%(objectname:short=7) ");
|
||||
|
||||
if (filter->verbose > 1)
|
||||
strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
|
||||
"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
|
||||
branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
|
||||
else
|
||||
strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
|
||||
|
||||
strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)"
|
||||
"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)",
|
||||
branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
|
||||
branch_get_color(BRANCH_COLOR_RESET));
|
||||
} else {
|
||||
strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
|
||||
branch_get_color(BRANCH_COLOR_RESET));
|
||||
strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
|
||||
branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),
|
||||
branch_get_color(BRANCH_COLOR_RESET));
|
||||
}
|
||||
|
||||
strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
|
||||
|
||||
strbuf_release(&local);
|
||||
strbuf_release(&remote);
|
||||
return strbuf_detach(&fmt, NULL);
|
||||
}
|
||||
|
||||
static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
|
||||
{
|
||||
int i;
|
||||
struct ref_array array;
|
||||
int maxwidth = 0;
|
||||
const char *remote_prefix = "";
|
||||
struct strbuf out = STRBUF_INIT;
|
||||
char *to_free = NULL;
|
||||
|
||||
/*
|
||||
* If we are listing more than just remote branches,
|
||||
@ -506,18 +384,32 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
|
||||
|
||||
memset(&array, 0, sizeof(array));
|
||||
|
||||
verify_ref_format("%(refname)%(symref)");
|
||||
filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
|
||||
|
||||
if (filter->verbose)
|
||||
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
|
||||
|
||||
if (!format)
|
||||
format = to_free = build_format(filter, maxwidth, remote_prefix);
|
||||
verify_ref_format(format);
|
||||
|
||||
ref_array_sort(sorting, &array);
|
||||
|
||||
for (i = 0; i < array.nr; i++)
|
||||
format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
|
||||
for (i = 0; i < array.nr; i++) {
|
||||
format_ref_array_item(array.items[i], format, 0, &out);
|
||||
if (column_active(colopts)) {
|
||||
assert(!filter->verbose && "--column and --verbose are incompatible");
|
||||
/* format to a string_list to let print_columns() do its job */
|
||||
string_list_append(&output, out.buf);
|
||||
} else {
|
||||
fwrite(out.buf, 1, out.len, stdout);
|
||||
putchar('\n');
|
||||
}
|
||||
strbuf_release(&out);
|
||||
}
|
||||
|
||||
ref_array_clear(&array);
|
||||
free(to_free);
|
||||
}
|
||||
|
||||
static void reject_rebase_or_bisect_branch(const char *target)
|
||||
@ -638,6 +530,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
struct ref_filter filter;
|
||||
int icase = 0;
|
||||
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
|
||||
const char *format = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP(N_("Generic options")),
|
||||
@ -679,9 +572,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
N_("print only branches of the object"), 0, parse_opt_object_name
|
||||
},
|
||||
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
||||
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
setup_ref_filter_porcelain_msg();
|
||||
|
||||
memset(&filter, 0, sizeof(filter));
|
||||
filter.kind = FILTER_REFS_BRANCHES;
|
||||
filter.abbrev = -1;
|
||||
@ -749,7 +645,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
if (!sorting)
|
||||
sorting = ref_default_sorting();
|
||||
sorting->ignore_case = icase;
|
||||
print_ref_list(&filter, sorting);
|
||||
print_ref_list(&filter, sorting, format);
|
||||
print_columns(&output, colopts, NULL);
|
||||
string_list_clear(&output, 0);
|
||||
return 0;
|
||||
|
@ -45,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
|
||||
if (!format) {
|
||||
if (filter->lines) {
|
||||
to_free = xstrfmt("%s %%(contents:lines=%d)",
|
||||
"%(align:15)%(refname:strip=2)%(end)",
|
||||
"%(align:15)%(refname:lstrip=2)%(end)",
|
||||
filter->lines);
|
||||
format = to_free;
|
||||
} else
|
||||
format = "%(refname:strip=2)";
|
||||
format = "%(refname:lstrip=2)";
|
||||
}
|
||||
|
||||
verify_ref_format(format);
|
||||
@ -389,6 +389,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
setup_ref_filter_porcelain_msg();
|
||||
|
||||
git_config(git_tag_config, sorting_tail);
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
|
Reference in New Issue
Block a user