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:
@ -12,7 +12,7 @@ SYNOPSIS
|
|||||||
[--list] [-v [--abbrev=<length> | --no-abbrev]]
|
[--list] [-v [--abbrev=<length> | --no-abbrev]]
|
||||||
[--column[=<options>] | --no-column]
|
[--column[=<options>] | --no-column]
|
||||||
[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
|
[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
|
||||||
[--points-at <object>] [<pattern>...]
|
[--points-at <object>] [--format=<format>] [<pattern>...]
|
||||||
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
||||||
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
||||||
'git branch' --unset-upstream [<branchname>]
|
'git branch' --unset-upstream [<branchname>]
|
||||||
@ -253,6 +253,11 @@ start-point is either a local or remote-tracking branch.
|
|||||||
--points-at <object>::
|
--points-at <object>::
|
||||||
Only list branches of the given object.
|
Only list branches of the given object.
|
||||||
|
|
||||||
|
--format <format>::
|
||||||
|
A string that interpolates `%(fieldname)` from the object
|
||||||
|
pointed at by a ref being shown. The format is the same as
|
||||||
|
that of linkgit:git-for-each-ref[1].
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -95,11 +95,20 @@ refname::
|
|||||||
The name of the ref (the part after $GIT_DIR/).
|
The name of the ref (the part after $GIT_DIR/).
|
||||||
For a non-ambiguous short name of the ref append `:short`.
|
For a non-ambiguous short name of the ref append `:short`.
|
||||||
The option core.warnAmbiguousRefs is used to select the strict
|
The option core.warnAmbiguousRefs is used to select the strict
|
||||||
abbreviation mode. If `strip=<N>` is appended, strips `<N>`
|
abbreviation mode. If `lstrip=<N>` (`rstrip=<N>`) is appended, strips `<N>`
|
||||||
slash-separated path components from the front of the refname
|
slash-separated path components from the front (back) of the refname
|
||||||
(e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.
|
(e.g. `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and
|
||||||
`<N>` must be a positive integer. If a displayed ref has fewer
|
`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).
|
||||||
components than `<N>`, the command aborts with an error.
|
If `<N>` is a negative number, strip as many path components as
|
||||||
|
necessary from the specified end to leave `-<N>` path components
|
||||||
|
(e.g. `%(refname:lstrip=-2)` turns
|
||||||
|
`refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)`
|
||||||
|
turns `refs/tags/foo` into `refs`). When the ref does not have
|
||||||
|
enough components, the result becomes an empty string if
|
||||||
|
stripping with positive <N>, or it becomes the full refname if
|
||||||
|
stripping with negative <N>. Neither is an error.
|
||||||
|
+
|
||||||
|
`strip` can be used as a synomym to `lstrip`.
|
||||||
|
|
||||||
objecttype::
|
objecttype::
|
||||||
The type of the object (`blob`, `tree`, `commit`, `tag`).
|
The type of the object (`blob`, `tree`, `commit`, `tag`).
|
||||||
@ -110,21 +119,31 @@ objectsize::
|
|||||||
objectname::
|
objectname::
|
||||||
The object name (aka SHA-1).
|
The object name (aka SHA-1).
|
||||||
For a non-ambiguous abbreviation of the object name append `:short`.
|
For a non-ambiguous abbreviation of the object name append `:short`.
|
||||||
|
For an abbreviation of the object name with desired length append
|
||||||
|
`:short=<length>`, where the minimum length is MINIMUM_ABBREV. The
|
||||||
|
length may be exceeded to ensure unique object names.
|
||||||
|
|
||||||
upstream::
|
upstream::
|
||||||
The name of a local ref which can be considered ``upstream''
|
The name of a local ref which can be considered ``upstream''
|
||||||
from the displayed ref. Respects `:short` in the same way as
|
from the displayed ref. Respects `:short`, `:lstrip` and
|
||||||
`refname` above. Additionally respects `:track` to show
|
`:rstrip` in the same way as `refname` above. Additionally
|
||||||
"[ahead N, behind M]" and `:trackshort` to show the terse
|
respects `:track` to show "[ahead N, behind M]" and
|
||||||
version: ">" (ahead), "<" (behind), "<>" (ahead and behind),
|
`:trackshort` to show the terse version: ">" (ahead), "<"
|
||||||
or "=" (in sync). Has no effect if the ref does not have
|
(behind), "<>" (ahead and behind), or "=" (in sync). `:track`
|
||||||
tracking information associated with it.
|
also prints "[gone]" whenever unknown upstream ref is
|
||||||
|
encountered. Append `:track,nobracket` to show tracking
|
||||||
|
information without brackets (i.e "ahead N, behind M"). Has
|
||||||
|
no effect if the ref does not have tracking information
|
||||||
|
associated with it. All the options apart from `nobracket`
|
||||||
|
are mutually exclusive, but if used together the last option
|
||||||
|
is selected.
|
||||||
|
|
||||||
push::
|
push::
|
||||||
The name of a local ref which represents the `@{push}` location
|
The name of a local ref which represents the `@{push}`
|
||||||
for the displayed ref. Respects `:short`, `:track`, and
|
location for the displayed ref. Respects `:short`, `:lstrip`,
|
||||||
`:trackshort` options as `upstream` does. Produces an empty
|
`:rstrip`, `:track`, and `:trackshort` options as `upstream`
|
||||||
string if no `@{push}` ref is configured.
|
does. Produces an empty string if no `@{push}` ref is
|
||||||
|
configured.
|
||||||
|
|
||||||
HEAD::
|
HEAD::
|
||||||
'*' if HEAD matches current ref (the checked out branch), ' '
|
'*' if HEAD matches current ref (the checked out branch), ' '
|
||||||
@ -149,6 +168,25 @@ align::
|
|||||||
quoted, but if nested then only the topmost level performs
|
quoted, but if nested then only the topmost level performs
|
||||||
quoting.
|
quoting.
|
||||||
|
|
||||||
|
if::
|
||||||
|
Used as %(if)...%(then)...%(end) or
|
||||||
|
%(if)...%(then)...%(else)...%(end). If there is an atom with
|
||||||
|
value or string literal after the %(if) then everything after
|
||||||
|
the %(then) is printed, else if the %(else) atom is used, then
|
||||||
|
everything after %(else) is printed. We ignore space when
|
||||||
|
evaluating the string before %(then), this is useful when we
|
||||||
|
use the %(HEAD) atom which prints either "*" or " " and we
|
||||||
|
want to apply the 'if' condition only on the 'HEAD' ref.
|
||||||
|
Append ":equals=<string>" or ":notequals=<string>" to compare
|
||||||
|
the value between the %(if:...) and %(then) atoms with the
|
||||||
|
given string.
|
||||||
|
|
||||||
|
symref::
|
||||||
|
The ref which the given symbolic ref refers to. If not a
|
||||||
|
symbolic ref, nothing is printed. Respects the `:short`,
|
||||||
|
`:lstrip` and `:rstrip` options in the same way as `refname`
|
||||||
|
above.
|
||||||
|
|
||||||
In addition to the above, for commit and tag objects, the header
|
In addition to the above, for commit and tag objects, the header
|
||||||
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
|
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
|
||||||
be used to specify the value in the header field.
|
be used to specify the value in the header field.
|
||||||
@ -186,6 +224,14 @@ As a special case for the date-type fields, you may specify a format for
|
|||||||
the date by adding `:` followed by date format name (see the
|
the date by adding `:` followed by date format name (see the
|
||||||
values the `--date` option to linkgit:git-rev-list[1] takes).
|
values the `--date` option to linkgit:git-rev-list[1] takes).
|
||||||
|
|
||||||
|
Some atoms like %(align) and %(if) always require a matching %(end).
|
||||||
|
We call them "opening atoms" and sometimes denote them as %($open).
|
||||||
|
|
||||||
|
When a scripting language specific quoting is in effect, everything
|
||||||
|
between a top-level opening atom and its matching %(end) is evaluated
|
||||||
|
according to the semantics of the opening atom and only its result
|
||||||
|
from the top-level is quoted.
|
||||||
|
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
--------
|
--------
|
||||||
@ -273,6 +319,22 @@ eval=`git for-each-ref --shell --format="$fmt" \
|
|||||||
eval "$eval"
|
eval "$eval"
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
An example to show the usage of %(if)...%(then)...%(else)...%(end).
|
||||||
|
This prefixes the current branch with a star.
|
||||||
|
|
||||||
|
------------
|
||||||
|
git for-each-ref --format="%(if)%(HEAD)%(then)* %(else) %(end)%(refname:short)" refs/heads/
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
An example to show the usage of %(if)...%(then)...%(end).
|
||||||
|
This prints the authorname, if present.
|
||||||
|
|
||||||
|
------------
|
||||||
|
git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Authored by: %(authorname)%(end)"
|
||||||
|
------------
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
linkgit:git-show-ref[1]
|
linkgit:git-show-ref[1]
|
||||||
|
284
builtin/branch.c
284
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>] [-r] (-d | -D) <branch-name>..."),
|
||||||
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
||||||
N_("git branch [<options>] [-r | -a] [--points-at]"),
|
N_("git branch [<options>] [-r | -a] [--points-at]"),
|
||||||
|
N_("git branch [<options>] [-r | -a] [--format]"),
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -280,195 +281,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|||||||
return(ret);
|
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)
|
static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
||||||
{
|
{
|
||||||
int i, max = 0;
|
int i, max = 0;
|
||||||
@ -479,6 +291,11 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
|||||||
|
|
||||||
skip_prefix(it->refname, "refs/heads/", &desc);
|
skip_prefix(it->refname, "refs/heads/", &desc);
|
||||||
skip_prefix(it->refname, "refs/remotes/", &desc);
|
skip_prefix(it->refname, "refs/remotes/", &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);
|
w = utf8_strwidth(desc);
|
||||||
|
|
||||||
if (it->kind == FILTER_REFS_REMOTES)
|
if (it->kind == FILTER_REFS_REMOTES)
|
||||||
@ -489,12 +306,73 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
|||||||
return max;
|
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;
|
int i;
|
||||||
struct ref_array array;
|
struct ref_array array;
|
||||||
int maxwidth = 0;
|
int maxwidth = 0;
|
||||||
const char *remote_prefix = "";
|
const char *remote_prefix = "";
|
||||||
|
struct strbuf out = STRBUF_INIT;
|
||||||
|
char *to_free = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are listing more than just remote branches,
|
* 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));
|
memset(&array, 0, sizeof(array));
|
||||||
|
|
||||||
verify_ref_format("%(refname)%(symref)");
|
|
||||||
filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
|
filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
|
||||||
|
|
||||||
if (filter->verbose)
|
if (filter->verbose)
|
||||||
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
|
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);
|
ref_array_sort(sorting, &array);
|
||||||
|
|
||||||
for (i = 0; i < array.nr; i++)
|
for (i = 0; i < array.nr; i++) {
|
||||||
format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
|
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);
|
ref_array_clear(&array);
|
||||||
|
free(to_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reject_rebase_or_bisect_branch(const char *target)
|
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;
|
struct ref_filter filter;
|
||||||
int icase = 0;
|
int icase = 0;
|
||||||
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
|
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
|
||||||
|
const char *format = NULL;
|
||||||
|
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_GROUP(N_("Generic 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
|
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_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(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setup_ref_filter_porcelain_msg();
|
||||||
|
|
||||||
memset(&filter, 0, sizeof(filter));
|
memset(&filter, 0, sizeof(filter));
|
||||||
filter.kind = FILTER_REFS_BRANCHES;
|
filter.kind = FILTER_REFS_BRANCHES;
|
||||||
filter.abbrev = -1;
|
filter.abbrev = -1;
|
||||||
@ -749,7 +645,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
if (!sorting)
|
if (!sorting)
|
||||||
sorting = ref_default_sorting();
|
sorting = ref_default_sorting();
|
||||||
sorting->ignore_case = icase;
|
sorting->ignore_case = icase;
|
||||||
print_ref_list(&filter, sorting);
|
print_ref_list(&filter, sorting, format);
|
||||||
print_columns(&output, colopts, NULL);
|
print_columns(&output, colopts, NULL);
|
||||||
string_list_clear(&output, 0);
|
string_list_clear(&output, 0);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -45,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
|
|||||||
if (!format) {
|
if (!format) {
|
||||||
if (filter->lines) {
|
if (filter->lines) {
|
||||||
to_free = xstrfmt("%s %%(contents:lines=%d)",
|
to_free = xstrfmt("%s %%(contents:lines=%d)",
|
||||||
"%(align:15)%(refname:strip=2)%(end)",
|
"%(align:15)%(refname:lstrip=2)%(end)",
|
||||||
filter->lines);
|
filter->lines);
|
||||||
format = to_free;
|
format = to_free;
|
||||||
} else
|
} else
|
||||||
format = "%(refname:strip=2)";
|
format = "%(refname:lstrip=2)";
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_ref_format(format);
|
verify_ref_format(format);
|
||||||
@ -389,6 +389,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setup_ref_filter_porcelain_msg();
|
||||||
|
|
||||||
git_config(git_tag_config, sorting_tail);
|
git_config(git_tag_config, sorting_tail);
|
||||||
|
|
||||||
memset(&opt, 0, sizeof(opt));
|
memset(&opt, 0, sizeof(opt));
|
||||||
|
493
ref-filter.c
493
ref-filter.c
@ -14,14 +14,50 @@
|
|||||||
#include "git-compat-util.h"
|
#include "git-compat-util.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "trailer.h"
|
#include "trailer.h"
|
||||||
|
#include "wt-status.h"
|
||||||
|
|
||||||
|
static struct ref_msg {
|
||||||
|
const char *gone;
|
||||||
|
const char *ahead;
|
||||||
|
const char *behind;
|
||||||
|
const char *ahead_behind;
|
||||||
|
} msgs = {
|
||||||
|
/* Untranslated plumbing messages: */
|
||||||
|
"gone",
|
||||||
|
"ahead %d",
|
||||||
|
"behind %d",
|
||||||
|
"ahead %d, behind %d"
|
||||||
|
};
|
||||||
|
|
||||||
|
void setup_ref_filter_porcelain_msg(void)
|
||||||
|
{
|
||||||
|
msgs.gone = _("gone");
|
||||||
|
msgs.ahead = _("ahead %d");
|
||||||
|
msgs.behind = _("behind %d");
|
||||||
|
msgs.ahead_behind = _("ahead %d, behind %d");
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
|
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
|
||||||
|
typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;
|
||||||
|
|
||||||
struct align {
|
struct align {
|
||||||
align_type position;
|
align_type position;
|
||||||
unsigned int width;
|
unsigned int width;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct if_then_else {
|
||||||
|
cmp_status cmp_status;
|
||||||
|
const char *str;
|
||||||
|
unsigned int then_atom_seen : 1,
|
||||||
|
else_atom_seen : 1,
|
||||||
|
condition_satisfied : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct refname_atom {
|
||||||
|
enum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;
|
||||||
|
int lstrip, rstrip;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An atom is a valid field atom listed below, possibly prefixed with
|
* An atom is a valid field atom listed below, possibly prefixed with
|
||||||
* a "*" to denote deref_tag().
|
* a "*" to denote deref_tag().
|
||||||
@ -38,13 +74,24 @@ static struct used_atom {
|
|||||||
union {
|
union {
|
||||||
char color[COLOR_MAXLEN];
|
char color[COLOR_MAXLEN];
|
||||||
struct align align;
|
struct align align;
|
||||||
enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
|
struct {
|
||||||
remote_ref;
|
enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
|
||||||
|
struct refname_atom refname;
|
||||||
|
unsigned int nobracket : 1;
|
||||||
|
} remote_ref;
|
||||||
struct {
|
struct {
|
||||||
enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
|
enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
|
||||||
unsigned int nlines;
|
unsigned int nlines;
|
||||||
} contents;
|
} contents;
|
||||||
enum { O_FULL, O_SHORT } objectname;
|
struct {
|
||||||
|
cmp_status cmp_status;
|
||||||
|
const char *str;
|
||||||
|
} if_then_else;
|
||||||
|
struct {
|
||||||
|
enum { O_FULL, O_LENGTH, O_SHORT } option;
|
||||||
|
unsigned int length;
|
||||||
|
} objectname;
|
||||||
|
struct refname_atom refname;
|
||||||
} u;
|
} u;
|
||||||
} *used_atom;
|
} *used_atom;
|
||||||
static int used_atom_cnt, need_tagged, need_symref;
|
static int used_atom_cnt, need_tagged, need_symref;
|
||||||
@ -58,18 +105,58 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)
|
|||||||
die(_("unrecognized color: %%(color:%s)"), color_value);
|
die(_("unrecognized color: %%(color:%s)"), color_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
|
static void refname_atom_parser_internal(struct refname_atom *atom,
|
||||||
|
const char *arg, const char *name)
|
||||||
{
|
{
|
||||||
if (!arg)
|
if (!arg)
|
||||||
atom->u.remote_ref = RR_NORMAL;
|
atom->option = R_NORMAL;
|
||||||
else if (!strcmp(arg, "short"))
|
else if (!strcmp(arg, "short"))
|
||||||
atom->u.remote_ref = RR_SHORTEN;
|
atom->option = R_SHORT;
|
||||||
else if (!strcmp(arg, "track"))
|
else if (skip_prefix(arg, "lstrip=", &arg) ||
|
||||||
atom->u.remote_ref = RR_TRACK;
|
skip_prefix(arg, "strip=", &arg)) {
|
||||||
else if (!strcmp(arg, "trackshort"))
|
atom->option = R_LSTRIP;
|
||||||
atom->u.remote_ref = RR_TRACKSHORT;
|
if (strtol_i(arg, 10, &atom->lstrip))
|
||||||
else
|
die(_("Integer value expected refname:lstrip=%s"), arg);
|
||||||
die(_("unrecognized format: %%(%s)"), atom->name);
|
} else if (skip_prefix(arg, "rstrip=", &arg)) {
|
||||||
|
atom->option = R_RSTRIP;
|
||||||
|
if (strtol_i(arg, 10, &atom->rstrip))
|
||||||
|
die(_("Integer value expected refname:rstrip=%s"), arg);
|
||||||
|
} else
|
||||||
|
die(_("unrecognized %%(%s) argument: %s"), name, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
|
||||||
|
{
|
||||||
|
struct string_list params = STRING_LIST_INIT_DUP;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!arg) {
|
||||||
|
atom->u.remote_ref.option = RR_REF;
|
||||||
|
refname_atom_parser_internal(&atom->u.remote_ref.refname,
|
||||||
|
arg, atom->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
atom->u.remote_ref.nobracket = 0;
|
||||||
|
string_list_split(¶ms, arg, ',', -1);
|
||||||
|
|
||||||
|
for (i = 0; i < params.nr; i++) {
|
||||||
|
const char *s = params.items[i].string;
|
||||||
|
|
||||||
|
if (!strcmp(s, "track"))
|
||||||
|
atom->u.remote_ref.option = RR_TRACK;
|
||||||
|
else if (!strcmp(s, "trackshort"))
|
||||||
|
atom->u.remote_ref.option = RR_TRACKSHORT;
|
||||||
|
else if (!strcmp(s, "nobracket"))
|
||||||
|
atom->u.remote_ref.nobracket = 1;
|
||||||
|
else {
|
||||||
|
atom->u.remote_ref.option = RR_REF;
|
||||||
|
refname_atom_parser_internal(&atom->u.remote_ref.refname,
|
||||||
|
arg, atom->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list_clear(¶ms, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void body_atom_parser(struct used_atom *atom, const char *arg)
|
static void body_atom_parser(struct used_atom *atom, const char *arg)
|
||||||
@ -116,13 +203,25 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)
|
|||||||
static void objectname_atom_parser(struct used_atom *atom, const char *arg)
|
static void objectname_atom_parser(struct used_atom *atom, const char *arg)
|
||||||
{
|
{
|
||||||
if (!arg)
|
if (!arg)
|
||||||
atom->u.objectname = O_FULL;
|
atom->u.objectname.option = O_FULL;
|
||||||
else if (!strcmp(arg, "short"))
|
else if (!strcmp(arg, "short"))
|
||||||
atom->u.objectname = O_SHORT;
|
atom->u.objectname.option = O_SHORT;
|
||||||
else
|
else if (skip_prefix(arg, "short=", &arg)) {
|
||||||
|
atom->u.objectname.option = O_LENGTH;
|
||||||
|
if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||
|
||||||
|
atom->u.objectname.length == 0)
|
||||||
|
die(_("positive value expected objectname:short=%s"), arg);
|
||||||
|
if (atom->u.objectname.length < MINIMUM_ABBREV)
|
||||||
|
atom->u.objectname.length = MINIMUM_ABBREV;
|
||||||
|
} else
|
||||||
die(_("unrecognized %%(objectname) argument: %s"), arg);
|
die(_("unrecognized %%(objectname) argument: %s"), arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void refname_atom_parser(struct used_atom *atom, const char *arg)
|
||||||
|
{
|
||||||
|
return refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
|
||||||
|
}
|
||||||
|
|
||||||
static align_type parse_align_position(const char *s)
|
static align_type parse_align_position(const char *s)
|
||||||
{
|
{
|
||||||
if (!strcmp(s, "right"))
|
if (!strcmp(s, "right"))
|
||||||
@ -173,12 +272,27 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)
|
|||||||
string_list_clear(¶ms, 0);
|
string_list_clear(¶ms, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void if_atom_parser(struct used_atom *atom, const char *arg)
|
||||||
|
{
|
||||||
|
if (!arg) {
|
||||||
|
atom->u.if_then_else.cmp_status = COMPARE_NONE;
|
||||||
|
return;
|
||||||
|
} else if (skip_prefix(arg, "equals=", &atom->u.if_then_else.str)) {
|
||||||
|
atom->u.if_then_else.cmp_status = COMPARE_EQUAL;
|
||||||
|
} else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
|
||||||
|
atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
|
||||||
|
} else {
|
||||||
|
die(_("unrecognized %%(if) argument: %s"), arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
cmp_type cmp_type;
|
cmp_type cmp_type;
|
||||||
void (*parser)(struct used_atom *atom, const char *arg);
|
void (*parser)(struct used_atom *atom, const char *arg);
|
||||||
} valid_atom[] = {
|
} valid_atom[] = {
|
||||||
{ "refname" },
|
{ "refname" , FIELD_STR, refname_atom_parser },
|
||||||
{ "objecttype" },
|
{ "objecttype" },
|
||||||
{ "objectsize", FIELD_ULONG },
|
{ "objectsize", FIELD_ULONG },
|
||||||
{ "objectname", FIELD_STR, objectname_atom_parser },
|
{ "objectname", FIELD_STR, objectname_atom_parser },
|
||||||
@ -208,12 +322,15 @@ static struct {
|
|||||||
{ "contents", FIELD_STR, contents_atom_parser },
|
{ "contents", FIELD_STR, contents_atom_parser },
|
||||||
{ "upstream", FIELD_STR, remote_ref_atom_parser },
|
{ "upstream", FIELD_STR, remote_ref_atom_parser },
|
||||||
{ "push", FIELD_STR, remote_ref_atom_parser },
|
{ "push", FIELD_STR, remote_ref_atom_parser },
|
||||||
{ "symref" },
|
{ "symref", FIELD_STR, refname_atom_parser },
|
||||||
{ "flag" },
|
{ "flag" },
|
||||||
{ "HEAD" },
|
{ "HEAD" },
|
||||||
{ "color", FIELD_STR, color_atom_parser },
|
{ "color", FIELD_STR, color_atom_parser },
|
||||||
{ "align", FIELD_STR, align_atom_parser },
|
{ "align", FIELD_STR, align_atom_parser },
|
||||||
{ "end" },
|
{ "end" },
|
||||||
|
{ "if", FIELD_STR, if_atom_parser },
|
||||||
|
{ "then" },
|
||||||
|
{ "else" },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REF_FORMATTING_STATE_INIT { 0, NULL }
|
#define REF_FORMATTING_STATE_INIT { 0, NULL }
|
||||||
@ -221,7 +338,7 @@ static struct {
|
|||||||
struct ref_formatting_stack {
|
struct ref_formatting_stack {
|
||||||
struct ref_formatting_stack *prev;
|
struct ref_formatting_stack *prev;
|
||||||
struct strbuf output;
|
struct strbuf output;
|
||||||
void (*at_end)(struct ref_formatting_stack *stack);
|
void (*at_end)(struct ref_formatting_stack **stack);
|
||||||
void *at_end_data;
|
void *at_end_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -232,11 +349,9 @@ struct ref_formatting_state {
|
|||||||
|
|
||||||
struct atom_value {
|
struct atom_value {
|
||||||
const char *s;
|
const char *s;
|
||||||
union {
|
|
||||||
struct align align;
|
|
||||||
} u;
|
|
||||||
void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
|
void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
|
||||||
unsigned long ul; /* used for sorting when not FIELD_STR */
|
unsigned long ul; /* used for sorting when not FIELD_STR */
|
||||||
|
struct used_atom *atom;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -293,7 +408,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
|
|||||||
valid_atom[i].parser(&used_atom[at], arg);
|
valid_atom[i].parser(&used_atom[at], arg);
|
||||||
if (*atom == '*')
|
if (*atom == '*')
|
||||||
need_tagged = 1;
|
need_tagged = 1;
|
||||||
if (!strcmp(used_atom[at].name, "symref"))
|
if (!strcmp(valid_atom[i].name, "symref"))
|
||||||
need_symref = 1;
|
need_symref = 1;
|
||||||
return at;
|
return at;
|
||||||
}
|
}
|
||||||
@ -354,13 +469,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
|
|||||||
*stack = prev;
|
*stack = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void end_align_handler(struct ref_formatting_stack *stack)
|
static void end_align_handler(struct ref_formatting_stack **stack)
|
||||||
{
|
{
|
||||||
struct align *align = (struct align *)stack->at_end_data;
|
struct ref_formatting_stack *cur = *stack;
|
||||||
|
struct align *align = (struct align *)cur->at_end_data;
|
||||||
struct strbuf s = STRBUF_INIT;
|
struct strbuf s = STRBUF_INIT;
|
||||||
|
|
||||||
strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
|
strbuf_utf8_align(&s, align->position, align->width, cur->output.buf);
|
||||||
strbuf_swap(&stack->output, &s);
|
strbuf_swap(&cur->output, &s);
|
||||||
strbuf_release(&s);
|
strbuf_release(&s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +487,115 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s
|
|||||||
push_stack_element(&state->stack);
|
push_stack_element(&state->stack);
|
||||||
new = state->stack;
|
new = state->stack;
|
||||||
new->at_end = end_align_handler;
|
new->at_end = end_align_handler;
|
||||||
new->at_end_data = &atomv->u.align;
|
new->at_end_data = &atomv->atom->u.align;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void if_then_else_handler(struct ref_formatting_stack **stack)
|
||||||
|
{
|
||||||
|
struct ref_formatting_stack *cur = *stack;
|
||||||
|
struct ref_formatting_stack *prev = cur->prev;
|
||||||
|
struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;
|
||||||
|
|
||||||
|
if (!if_then_else->then_atom_seen)
|
||||||
|
die(_("format: %%(if) atom used without a %%(then) atom"));
|
||||||
|
|
||||||
|
if (if_then_else->else_atom_seen) {
|
||||||
|
/*
|
||||||
|
* There is an %(else) atom: we need to drop one state from the
|
||||||
|
* stack, either the %(else) branch if the condition is satisfied, or
|
||||||
|
* the %(then) branch if it isn't.
|
||||||
|
*/
|
||||||
|
if (if_then_else->condition_satisfied) {
|
||||||
|
strbuf_reset(&cur->output);
|
||||||
|
pop_stack_element(&cur);
|
||||||
|
} else {
|
||||||
|
strbuf_swap(&cur->output, &prev->output);
|
||||||
|
strbuf_reset(&cur->output);
|
||||||
|
pop_stack_element(&cur);
|
||||||
|
}
|
||||||
|
} else if (!if_then_else->condition_satisfied) {
|
||||||
|
/*
|
||||||
|
* No %(else) atom: just drop the %(then) branch if the
|
||||||
|
* condition is not satisfied.
|
||||||
|
*/
|
||||||
|
strbuf_reset(&cur->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
*stack = cur;
|
||||||
|
free(if_then_else);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||||
|
{
|
||||||
|
struct ref_formatting_stack *new;
|
||||||
|
struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);
|
||||||
|
|
||||||
|
if_then_else->str = atomv->atom->u.if_then_else.str;
|
||||||
|
if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
|
||||||
|
|
||||||
|
push_stack_element(&state->stack);
|
||||||
|
new = state->stack;
|
||||||
|
new->at_end = if_then_else_handler;
|
||||||
|
new->at_end_data = if_then_else;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_empty(const char *s)
|
||||||
|
{
|
||||||
|
while (*s != '\0') {
|
||||||
|
if (!isspace(*s))
|
||||||
|
return 0;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||||
|
{
|
||||||
|
struct ref_formatting_stack *cur = state->stack;
|
||||||
|
struct if_then_else *if_then_else = NULL;
|
||||||
|
|
||||||
|
if (cur->at_end == if_then_else_handler)
|
||||||
|
if_then_else = (struct if_then_else *)cur->at_end_data;
|
||||||
|
if (!if_then_else)
|
||||||
|
die(_("format: %%(then) atom used without an %%(if) atom"));
|
||||||
|
if (if_then_else->then_atom_seen)
|
||||||
|
die(_("format: %%(then) atom used more than once"));
|
||||||
|
if (if_then_else->else_atom_seen)
|
||||||
|
die(_("format: %%(then) atom used after %%(else)"));
|
||||||
|
if_then_else->then_atom_seen = 1;
|
||||||
|
/*
|
||||||
|
* If the 'equals' or 'notequals' attribute is used then
|
||||||
|
* perform the required comparison. If not, only non-empty
|
||||||
|
* strings satisfy the 'if' condition.
|
||||||
|
*/
|
||||||
|
if (if_then_else->cmp_status == COMPARE_EQUAL) {
|
||||||
|
if (!strcmp(if_then_else->str, cur->output.buf))
|
||||||
|
if_then_else->condition_satisfied = 1;
|
||||||
|
} else if (if_then_else->cmp_status == COMPARE_UNEQUAL) {
|
||||||
|
if (strcmp(if_then_else->str, cur->output.buf))
|
||||||
|
if_then_else->condition_satisfied = 1;
|
||||||
|
} else if (cur->output.len && !is_empty(cur->output.buf))
|
||||||
|
if_then_else->condition_satisfied = 1;
|
||||||
|
strbuf_reset(&cur->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||||
|
{
|
||||||
|
struct ref_formatting_stack *prev = state->stack;
|
||||||
|
struct if_then_else *if_then_else = NULL;
|
||||||
|
|
||||||
|
if (prev->at_end == if_then_else_handler)
|
||||||
|
if_then_else = (struct if_then_else *)prev->at_end_data;
|
||||||
|
if (!if_then_else)
|
||||||
|
die(_("format: %%(else) atom used without an %%(if) atom"));
|
||||||
|
if (!if_then_else->then_atom_seen)
|
||||||
|
die(_("format: %%(else) atom used without a %%(then) atom"));
|
||||||
|
if (if_then_else->else_atom_seen)
|
||||||
|
die(_("format: %%(else) atom used more than once"));
|
||||||
|
if_then_else->else_atom_seen = 1;
|
||||||
|
push_stack_element(&state->stack);
|
||||||
|
state->stack->at_end_data = prev->at_end_data;
|
||||||
|
state->stack->at_end = prev->at_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||||
@ -381,14 +605,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
|
|||||||
|
|
||||||
if (!current->at_end)
|
if (!current->at_end)
|
||||||
die(_("format: %%(end) atom used without corresponding atom"));
|
die(_("format: %%(end) atom used without corresponding atom"));
|
||||||
current->at_end(current);
|
current->at_end(&state->stack);
|
||||||
|
|
||||||
|
/* Stack may have been popped within at_end(), hence reset the current pointer */
|
||||||
|
current = state->stack;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform quote formatting when the stack element is that of
|
* Perform quote formatting when the stack element is that of
|
||||||
* a supporting atom. If nested then perform quote formatting
|
* a supporting atom. If nested then perform quote formatting
|
||||||
* only on the topmost supporting atom.
|
* only on the topmost supporting atom.
|
||||||
*/
|
*/
|
||||||
if (!state->stack->prev->prev) {
|
if (!current->prev->prev) {
|
||||||
quote_formatting(&s, current->output.buf, state->quote_style);
|
quote_formatting(&s, current->output.buf, state->quote_style);
|
||||||
strbuf_swap(¤t->output, &s);
|
strbuf_swap(¤t->output, &s);
|
||||||
}
|
}
|
||||||
@ -465,12 +692,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,
|
|||||||
struct atom_value *v, struct used_atom *atom)
|
struct atom_value *v, struct used_atom *atom)
|
||||||
{
|
{
|
||||||
if (starts_with(name, "objectname")) {
|
if (starts_with(name, "objectname")) {
|
||||||
if (atom->u.objectname == O_SHORT) {
|
if (atom->u.objectname.option == O_SHORT) {
|
||||||
v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
|
v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
|
||||||
return 1;
|
return 1;
|
||||||
} else if (atom->u.objectname == O_FULL) {
|
} else if (atom->u.objectname.option == O_FULL) {
|
||||||
v->s = xstrdup(sha1_to_hex(sha1));
|
v->s = xstrdup(sha1_to_hex(sha1));
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (atom->u.objectname.option == O_LENGTH) {
|
||||||
|
v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));
|
||||||
|
return 1;
|
||||||
} else
|
} else
|
||||||
die("BUG: unknown %%(objectname) option");
|
die("BUG: unknown %%(objectname) option");
|
||||||
}
|
}
|
||||||
@ -887,50 +1117,108 @@ static inline char *copy_advance(char *dst, const char *src)
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *strip_ref_components(const char *refname, const char *nr_arg)
|
static const char *lstrip_ref_components(const char *refname, int len)
|
||||||
{
|
{
|
||||||
char *end;
|
long remaining = len;
|
||||||
long nr = strtol(nr_arg, &end, 10);
|
|
||||||
long remaining = nr;
|
|
||||||
const char *start = refname;
|
const char *start = refname;
|
||||||
|
|
||||||
if (nr < 1 || *end != '\0')
|
if (len < 0) {
|
||||||
die(_(":strip= requires a positive integer argument"));
|
int i;
|
||||||
|
const char *p = refname;
|
||||||
|
|
||||||
while (remaining) {
|
/* Find total no of '/' separated path-components */
|
||||||
|
for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
|
||||||
|
;
|
||||||
|
/*
|
||||||
|
* The number of components we need to strip is now
|
||||||
|
* the total minus the components to be left (Plus one
|
||||||
|
* because we count the number of '/', but the number
|
||||||
|
* of components is one more than the no of '/').
|
||||||
|
*/
|
||||||
|
remaining = i + len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (remaining > 0) {
|
||||||
switch (*start++) {
|
switch (*start++) {
|
||||||
case '\0':
|
case '\0':
|
||||||
die(_("ref '%s' does not have %ld components to :strip"),
|
return "";
|
||||||
refname, nr);
|
|
||||||
case '/':
|
case '/':
|
||||||
remaining--;
|
remaining--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *rstrip_ref_components(const char *refname, int len)
|
||||||
|
{
|
||||||
|
long remaining = len;
|
||||||
|
char *start = xstrdup(refname);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
int i;
|
||||||
|
const char *p = refname;
|
||||||
|
|
||||||
|
/* Find total no of '/' separated path-components */
|
||||||
|
for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
|
||||||
|
;
|
||||||
|
/*
|
||||||
|
* The number of components we need to strip is now
|
||||||
|
* the total minus the components to be left (Plus one
|
||||||
|
* because we count the number of '/', but the number
|
||||||
|
* of components is one more than the no of '/').
|
||||||
|
*/
|
||||||
|
remaining = i + len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (remaining-- > 0) {
|
||||||
|
char *p = strrchr(start, '/');
|
||||||
|
if (p == NULL)
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
p[0] = '\0';
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *show_ref(struct refname_atom *atom, const char *refname)
|
||||||
|
{
|
||||||
|
if (atom->option == R_SHORT)
|
||||||
|
return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
|
||||||
|
else if (atom->option == R_LSTRIP)
|
||||||
|
return lstrip_ref_components(refname, atom->lstrip);
|
||||||
|
else if (atom->option == R_RSTRIP)
|
||||||
|
return rstrip_ref_components(refname, atom->rstrip);
|
||||||
|
else
|
||||||
|
return refname;
|
||||||
|
}
|
||||||
|
|
||||||
static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
|
static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
|
||||||
struct branch *branch, const char **s)
|
struct branch *branch, const char **s)
|
||||||
{
|
{
|
||||||
int num_ours, num_theirs;
|
int num_ours, num_theirs;
|
||||||
if (atom->u.remote_ref == RR_SHORTEN)
|
if (atom->u.remote_ref.option == RR_REF)
|
||||||
*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
|
*s = show_ref(&atom->u.remote_ref.refname, refname);
|
||||||
else if (atom->u.remote_ref == RR_TRACK) {
|
else if (atom->u.remote_ref.option == RR_TRACK) {
|
||||||
if (stat_tracking_info(branch, &num_ours,
|
if (stat_tracking_info(branch, &num_ours,
|
||||||
&num_theirs, NULL))
|
&num_theirs, NULL)) {
|
||||||
return;
|
*s = xstrdup(msgs.gone);
|
||||||
|
} else if (!num_ours && !num_theirs)
|
||||||
if (!num_ours && !num_theirs)
|
|
||||||
*s = "";
|
*s = "";
|
||||||
else if (!num_ours)
|
else if (!num_ours)
|
||||||
*s = xstrfmt("[behind %d]", num_theirs);
|
*s = xstrfmt(msgs.behind, num_theirs);
|
||||||
else if (!num_theirs)
|
else if (!num_theirs)
|
||||||
*s = xstrfmt("[ahead %d]", num_ours);
|
*s = xstrfmt(msgs.ahead, num_ours);
|
||||||
else
|
else
|
||||||
*s = xstrfmt("[ahead %d, behind %d]",
|
*s = xstrfmt(msgs.ahead_behind,
|
||||||
num_ours, num_theirs);
|
num_ours, num_theirs);
|
||||||
} else if (atom->u.remote_ref == RR_TRACKSHORT) {
|
if (!atom->u.remote_ref.nobracket && *s[0]) {
|
||||||
|
const char *to_free = *s;
|
||||||
|
*s = xstrfmt("[%s]", *s);
|
||||||
|
free((void *)to_free);
|
||||||
|
}
|
||||||
|
} else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
|
||||||
if (stat_tracking_info(branch, &num_ours,
|
if (stat_tracking_info(branch, &num_ours,
|
||||||
&num_theirs, NULL))
|
&num_theirs, NULL))
|
||||||
return;
|
return;
|
||||||
@ -943,8 +1231,56 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
|
|||||||
*s = ">";
|
*s = ">";
|
||||||
else
|
else
|
||||||
*s = "<>";
|
*s = "<>";
|
||||||
} else /* RR_NORMAL */
|
} else
|
||||||
*s = refname;
|
die("BUG: unhandled RR_* enum");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)
|
||||||
|
{
|
||||||
|
if (!ref->symref)
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return show_ref(&atom->u.refname, ref->symref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_refname(struct used_atom *atom, struct ref_array_item *ref)
|
||||||
|
{
|
||||||
|
if (ref->kind & FILTER_REFS_DETACHED_HEAD)
|
||||||
|
return get_head_description();
|
||||||
|
return show_ref(&atom->u.refname, ref->refname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -975,10 +1311,10 @@ static void populate_value(struct ref_array_item *ref)
|
|||||||
struct atom_value *v = &ref->value[i];
|
struct atom_value *v = &ref->value[i];
|
||||||
int deref = 0;
|
int deref = 0;
|
||||||
const char *refname;
|
const char *refname;
|
||||||
const char *formatp;
|
|
||||||
struct branch *branch = NULL;
|
struct branch *branch = NULL;
|
||||||
|
|
||||||
v->handler = append_atom;
|
v->handler = append_atom;
|
||||||
|
v->atom = atom;
|
||||||
|
|
||||||
if (*name == '*') {
|
if (*name == '*') {
|
||||||
deref = 1;
|
deref = 1;
|
||||||
@ -986,9 +1322,9 @@ static void populate_value(struct ref_array_item *ref)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (starts_with(name, "refname"))
|
if (starts_with(name, "refname"))
|
||||||
refname = ref->refname;
|
refname = get_refname(atom, ref);
|
||||||
else if (starts_with(name, "symref"))
|
else if (starts_with(name, "symref"))
|
||||||
refname = ref->symref ? ref->symref : "";
|
refname = get_symref(atom, ref);
|
||||||
else if (starts_with(name, "upstream")) {
|
else if (starts_with(name, "upstream")) {
|
||||||
const char *branch_name;
|
const char *branch_name;
|
||||||
/* only local branches may have an upstream */
|
/* only local branches may have an upstream */
|
||||||
@ -1043,30 +1379,27 @@ static void populate_value(struct ref_array_item *ref)
|
|||||||
v->s = " ";
|
v->s = " ";
|
||||||
continue;
|
continue;
|
||||||
} else if (starts_with(name, "align")) {
|
} else if (starts_with(name, "align")) {
|
||||||
v->u.align = atom->u.align;
|
|
||||||
v->handler = align_atom_handler;
|
v->handler = align_atom_handler;
|
||||||
continue;
|
continue;
|
||||||
} else if (!strcmp(name, "end")) {
|
} else if (!strcmp(name, "end")) {
|
||||||
v->handler = end_atom_handler;
|
v->handler = end_atom_handler;
|
||||||
continue;
|
continue;
|
||||||
|
} else if (starts_with(name, "if")) {
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
if (skip_prefix(name, "if:", &s))
|
||||||
|
v->s = xstrdup(s);
|
||||||
|
v->handler = if_atom_handler;
|
||||||
|
continue;
|
||||||
|
} else if (!strcmp(name, "then")) {
|
||||||
|
v->handler = then_atom_handler;
|
||||||
|
continue;
|
||||||
|
} else if (!strcmp(name, "else")) {
|
||||||
|
v->handler = else_atom_handler;
|
||||||
|
continue;
|
||||||
} else
|
} else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
formatp = strchr(name, ':');
|
|
||||||
if (formatp) {
|
|
||||||
const char *arg;
|
|
||||||
|
|
||||||
formatp++;
|
|
||||||
if (!strcmp(formatp, "short"))
|
|
||||||
refname = shorten_unambiguous_ref(refname,
|
|
||||||
warn_ambiguous_refs);
|
|
||||||
else if (skip_prefix(formatp, "strip=", &arg))
|
|
||||||
refname = strip_ref_components(refname, arg);
|
|
||||||
else
|
|
||||||
die(_("unknown %.*s format %s"),
|
|
||||||
(int)(formatp - name), name, formatp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deref)
|
if (!deref)
|
||||||
v->s = refname;
|
v->s = refname;
|
||||||
else
|
else
|
||||||
@ -1635,10 +1968,10 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
|
void format_ref_array_item(struct ref_array_item *info, const char *format,
|
||||||
|
int quote_style, struct strbuf *final_buf)
|
||||||
{
|
{
|
||||||
const char *cp, *sp, *ep;
|
const char *cp, *sp, *ep;
|
||||||
struct strbuf *final_buf;
|
|
||||||
struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
|
struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
|
||||||
|
|
||||||
state.quote_style = quote_style;
|
state.quote_style = quote_style;
|
||||||
@ -1668,9 +2001,17 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
|
|||||||
}
|
}
|
||||||
if (state.stack->prev)
|
if (state.stack->prev)
|
||||||
die(_("format: %%(end) atom missing"));
|
die(_("format: %%(end) atom missing"));
|
||||||
final_buf = &state.stack->output;
|
strbuf_addbuf(final_buf, &state.stack->output);
|
||||||
fwrite(final_buf->buf, 1, final_buf->len, stdout);
|
|
||||||
pop_stack_element(&state.stack);
|
pop_stack_element(&state.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
|
||||||
|
{
|
||||||
|
struct strbuf final_buf = STRBUF_INIT;
|
||||||
|
|
||||||
|
format_ref_array_item(info, format, quote_style, &final_buf);
|
||||||
|
fwrite(final_buf.buf, 1, final_buf.len, stdout);
|
||||||
|
strbuf_release(&final_buf);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,9 @@ int parse_ref_filter_atom(const char *atom, const char *ep);
|
|||||||
int verify_ref_format(const char *format);
|
int verify_ref_format(const char *format);
|
||||||
/* Sort the given ref_array as per the ref_sorting provided */
|
/* Sort the given ref_array as per the ref_sorting provided */
|
||||||
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
|
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
|
||||||
|
/* Based on the given format and quote_style, fill the strbuf */
|
||||||
|
void format_ref_array_item(struct ref_array_item *info, const char *format,
|
||||||
|
int quote_style, struct strbuf *final_buf);
|
||||||
/* Print the ref using the given format and quote_style */
|
/* Print the ref using the given format and quote_style */
|
||||||
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
|
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
|
||||||
/* Callback function for parsing the sort option */
|
/* Callback function for parsing the sort option */
|
||||||
@ -108,6 +111,10 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
|
|||||||
struct ref_sorting *ref_default_sorting(void);
|
struct ref_sorting *ref_default_sorting(void);
|
||||||
/* Function to parse --merged and --no-merged options */
|
/* Function to parse --merged and --no-merged options */
|
||||||
int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);
|
int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);
|
||||||
|
/* Get the current HEAD's description */
|
||||||
|
char *get_head_description(void);
|
||||||
|
/* Set up translated strings in the output. */
|
||||||
|
void setup_ref_filter_porcelain_msg(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print a single ref, outside of any ref-filter. Note that the
|
* Print a single ref, outside of any ref-filter. Note that the
|
||||||
|
@ -194,7 +194,7 @@ test_expect_success 'local-branch symrefs shortened properly' '
|
|||||||
git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&
|
git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&
|
||||||
cat >expect <<-\EOF &&
|
cat >expect <<-\EOF &&
|
||||||
ref-to-branch -> branch-one
|
ref-to-branch -> branch-one
|
||||||
ref-to-remote -> refs/remotes/origin/branch-one
|
ref-to-remote -> origin/branch-one
|
||||||
EOF
|
EOF
|
||||||
git branch >actual.raw &&
|
git branch >actual.raw &&
|
||||||
grep ref-to <actual.raw >actual &&
|
grep ref-to <actual.raw >actual &&
|
||||||
@ -225,4 +225,18 @@ test_expect_success 'sort branches, ignore case' '
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch --format option' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
Refname is (HEAD detached from fromtag)
|
||||||
|
Refname is refs/heads/ambiguous
|
||||||
|
Refname is refs/heads/branch-one
|
||||||
|
Refname is refs/heads/branch-two
|
||||||
|
Refname is refs/heads/master
|
||||||
|
Refname is refs/heads/ref-to-branch
|
||||||
|
Refname is refs/heads/ref-to-remote
|
||||||
|
EOF
|
||||||
|
git branch --format="Refname is %(refname)" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d
|
|||||||
b2 [ahead 1, behind 1] d
|
b2 [ahead 1, behind 1] d
|
||||||
b3 [behind 1] b
|
b3 [behind 1] b
|
||||||
b4 [ahead 2] f
|
b4 [ahead 2] f
|
||||||
b5 g
|
b5 [gone] g
|
||||||
b6 c
|
b6 c
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ test_atom() {
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
head) ref=refs/heads/master ;;
|
head) ref=refs/heads/master ;;
|
||||||
tag) ref=refs/tags/testtag ;;
|
tag) ref=refs/tags/testtag ;;
|
||||||
|
sym) ref=refs/heads/sym ;;
|
||||||
*) ref=$1 ;;
|
*) ref=$1 ;;
|
||||||
esac
|
esac
|
||||||
printf '%s\n' "$3" >expected
|
printf '%s\n' "$3" >expected
|
||||||
@ -50,16 +51,40 @@ test_atom() {
|
|||||||
|
|
||||||
test_atom head refname refs/heads/master
|
test_atom head refname refs/heads/master
|
||||||
test_atom head refname:short master
|
test_atom head refname:short master
|
||||||
|
test_atom head refname:lstrip=1 heads/master
|
||||||
|
test_atom head refname:lstrip=2 master
|
||||||
|
test_atom head refname:lstrip=-1 master
|
||||||
|
test_atom head refname:lstrip=-2 heads/master
|
||||||
|
test_atom head refname:rstrip=1 refs/heads
|
||||||
|
test_atom head refname:rstrip=2 refs
|
||||||
|
test_atom head refname:rstrip=-1 refs
|
||||||
|
test_atom head refname:rstrip=-2 refs/heads
|
||||||
test_atom head refname:strip=1 heads/master
|
test_atom head refname:strip=1 heads/master
|
||||||
test_atom head refname:strip=2 master
|
test_atom head refname:strip=2 master
|
||||||
|
test_atom head refname:strip=-1 master
|
||||||
|
test_atom head refname:strip=-2 heads/master
|
||||||
test_atom head upstream refs/remotes/origin/master
|
test_atom head upstream refs/remotes/origin/master
|
||||||
test_atom head upstream:short origin/master
|
test_atom head upstream:short origin/master
|
||||||
|
test_atom head upstream:lstrip=2 origin/master
|
||||||
|
test_atom head upstream:lstrip=-2 origin/master
|
||||||
|
test_atom head upstream:rstrip=2 refs/remotes
|
||||||
|
test_atom head upstream:rstrip=-2 refs/remotes
|
||||||
|
test_atom head upstream:strip=2 origin/master
|
||||||
|
test_atom head upstream:strip=-2 origin/master
|
||||||
test_atom head push refs/remotes/myfork/master
|
test_atom head push refs/remotes/myfork/master
|
||||||
test_atom head push:short myfork/master
|
test_atom head push:short myfork/master
|
||||||
|
test_atom head push:lstrip=1 remotes/myfork/master
|
||||||
|
test_atom head push:lstrip=-1 master
|
||||||
|
test_atom head push:rstrip=1 refs/remotes/myfork
|
||||||
|
test_atom head push:rstrip=-1 refs
|
||||||
|
test_atom head push:strip=1 remotes/myfork/master
|
||||||
|
test_atom head push:strip=-1 master
|
||||||
test_atom head objecttype commit
|
test_atom head objecttype commit
|
||||||
test_atom head objectsize 171
|
test_atom head objectsize 171
|
||||||
test_atom head objectname $(git rev-parse refs/heads/master)
|
test_atom head objectname $(git rev-parse refs/heads/master)
|
||||||
test_atom head objectname:short $(git rev-parse --short refs/heads/master)
|
test_atom head objectname:short $(git rev-parse --short refs/heads/master)
|
||||||
|
test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
|
||||||
|
test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)
|
||||||
test_atom head tree $(git rev-parse refs/heads/master^{tree})
|
test_atom head tree $(git rev-parse refs/heads/master^{tree})
|
||||||
test_atom head parent ''
|
test_atom head parent ''
|
||||||
test_atom head numparent 0
|
test_atom head numparent 0
|
||||||
@ -99,6 +124,8 @@ test_atom tag objecttype tag
|
|||||||
test_atom tag objectsize 154
|
test_atom tag objectsize 154
|
||||||
test_atom tag objectname $(git rev-parse refs/tags/testtag)
|
test_atom tag objectname $(git rev-parse refs/tags/testtag)
|
||||||
test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
|
test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
|
||||||
|
test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
|
||||||
|
test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)
|
||||||
test_atom tag tree ''
|
test_atom tag tree ''
|
||||||
test_atom tag parent ''
|
test_atom tag parent ''
|
||||||
test_atom tag numparent ''
|
test_atom tag numparent ''
|
||||||
@ -134,16 +161,6 @@ test_expect_success 'Check invalid atoms names are errors' '
|
|||||||
test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
|
test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'arguments to :strip must be positive integers' '
|
|
||||||
test_must_fail git for-each-ref --format="%(refname:strip=0)" &&
|
|
||||||
test_must_fail git for-each-ref --format="%(refname:strip=-1)" &&
|
|
||||||
test_must_fail git for-each-ref --format="%(refname:strip=foo)"
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'stripping refnames too far gives an error' '
|
|
||||||
test_must_fail git for-each-ref --format="%(refname:strip=3)"
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
|
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
|
||||||
git for-each-ref --format="%(authordate)" refs/heads &&
|
git for-each-ref --format="%(authordate)" refs/heads &&
|
||||||
git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
|
git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
|
||||||
@ -164,6 +181,12 @@ test_expect_success 'Check invalid format specifiers are errors' '
|
|||||||
test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads
|
test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'arguments to %(objectname:short=) must be positive integers' '
|
||||||
|
test_must_fail git for-each-ref --format="%(objectname:short=0)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(objectname:short=-1)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(objectname:short=foo)"
|
||||||
|
'
|
||||||
|
|
||||||
test_date () {
|
test_date () {
|
||||||
f=$1 &&
|
f=$1 &&
|
||||||
committer_date=$2 &&
|
committer_date=$2 &&
|
||||||
@ -362,6 +385,8 @@ test_expect_success 'setup for upstream:track[short]' '
|
|||||||
|
|
||||||
test_atom head upstream:track '[ahead 1]'
|
test_atom head upstream:track '[ahead 1]'
|
||||||
test_atom head upstream:trackshort '>'
|
test_atom head upstream:trackshort '>'
|
||||||
|
test_atom head upstream:track,nobracket 'ahead 1'
|
||||||
|
test_atom head upstream:nobracket,track 'ahead 1'
|
||||||
test_atom head push:track '[ahead 1]'
|
test_atom head push:track '[ahead 1]'
|
||||||
test_atom head push:trackshort '>'
|
test_atom head push:trackshort '>'
|
||||||
|
|
||||||
@ -372,7 +397,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' '
|
|||||||
|
|
||||||
test_expect_success 'Check that :track[short] works when upstream is invalid' '
|
test_expect_success 'Check that :track[short] works when upstream is invalid' '
|
||||||
cat >expected <<-\EOF &&
|
cat >expected <<-\EOF &&
|
||||||
|
[gone]
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
test_when_finished "git config branch.master.merge refs/heads/master" &&
|
test_when_finished "git config branch.master.merge refs/heads/master" &&
|
||||||
@ -554,6 +579,7 @@ test_expect_success 'Verify sort with multiple keys' '
|
|||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
||||||
test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
|
test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
|
||||||
test_when_finished "git checkout master" &&
|
test_when_finished "git checkout master" &&
|
||||||
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
|
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
|
||||||
@ -588,4 +614,52 @@ test_expect_success 'basic atom: head contents:trailers' '
|
|||||||
test_cmp expect actual.clean
|
test_cmp expect actual.clean
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Add symbolic ref for the following tests' '
|
||||||
|
git symbolic-ref refs/heads/sym refs/heads/master
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expected <<EOF
|
||||||
|
refs/heads/master
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Verify usage of %(symref) atom' '
|
||||||
|
git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expected <<EOF
|
||||||
|
heads/master
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Verify usage of %(symref:short) atom' '
|
||||||
|
git for-each-ref --format="%(symref:short)" refs/heads/sym >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expected <<EOF
|
||||||
|
master
|
||||||
|
heads/master
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Verify usage of %(symref:lstrip) atom' '
|
||||||
|
git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual &&
|
||||||
|
git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual &&
|
||||||
|
test_cmp expected actual &&
|
||||||
|
|
||||||
|
git for-each-ref --format="%(symref:strip=2)" refs/heads/sym > actual &&
|
||||||
|
git for-each-ref --format="%(symref:strip=-2)" refs/heads/sym >> actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expected <<EOF
|
||||||
|
refs
|
||||||
|
refs/heads
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Verify usage of %(symref:rstrip) atom' '
|
||||||
|
git for-each-ref --format="%(symref:rstrip=2)" refs/heads/sym > actual &&
|
||||||
|
git for-each-ref --format="%(symref:rstrip=-2)" refs/heads/sym >> actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -327,4 +327,98 @@ test_expect_success 'reverse version sort' '
|
|||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' '
|
||||||
|
test_must_fail git for-each-ref --format="%(if)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(then) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(else) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(else) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(then) %(then) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(then) %(else) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(else) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(then) %(else)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(else) %(then) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(then) %(else) %(else) %(end)" &&
|
||||||
|
test_must_fail git for-each-ref --format="%(if) %(end)"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check %(if)...%(then)...%(end) atoms' '
|
||||||
|
git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)" >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
refs/heads/master Author: A U Thor
|
||||||
|
refs/heads/side Author: A U Thor
|
||||||
|
refs/odd/spot Author: A U Thor
|
||||||
|
refs/tags/annotated-tag
|
||||||
|
refs/tags/doubly-annotated-tag
|
||||||
|
refs/tags/doubly-signed-tag
|
||||||
|
refs/tags/foo1.10 Author: A U Thor
|
||||||
|
refs/tags/foo1.3 Author: A U Thor
|
||||||
|
refs/tags/foo1.6 Author: A U Thor
|
||||||
|
refs/tags/four Author: A U Thor
|
||||||
|
refs/tags/one Author: A U Thor
|
||||||
|
refs/tags/signed-tag
|
||||||
|
refs/tags/three Author: A U Thor
|
||||||
|
refs/tags/two Author: A U Thor
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' '
|
||||||
|
git for-each-ref --format="%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)" >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
A U Thor: refs/heads/master
|
||||||
|
A U Thor: refs/heads/side
|
||||||
|
A U Thor: refs/odd/spot
|
||||||
|
No author: refs/tags/annotated-tag
|
||||||
|
No author: refs/tags/doubly-annotated-tag
|
||||||
|
No author: refs/tags/doubly-signed-tag
|
||||||
|
A U Thor: refs/tags/foo1.10
|
||||||
|
A U Thor: refs/tags/foo1.3
|
||||||
|
A U Thor: refs/tags/foo1.6
|
||||||
|
A U Thor: refs/tags/four
|
||||||
|
A U Thor: refs/tags/one
|
||||||
|
No author: refs/tags/signed-tag
|
||||||
|
A U Thor: refs/tags/three
|
||||||
|
A U Thor: refs/tags/two
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
test_expect_success 'ignore spaces in %(if) atom usage' '
|
||||||
|
git for-each-ref --format="%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)" >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
master: Head ref
|
||||||
|
side: Not Head ref
|
||||||
|
odd/spot: Not Head ref
|
||||||
|
annotated-tag: Not Head ref
|
||||||
|
doubly-annotated-tag: Not Head ref
|
||||||
|
doubly-signed-tag: Not Head ref
|
||||||
|
foo1.10: Not Head ref
|
||||||
|
foo1.3: Not Head ref
|
||||||
|
foo1.6: Not Head ref
|
||||||
|
four: Not Head ref
|
||||||
|
one: Not Head ref
|
||||||
|
signed-tag: Not Head ref
|
||||||
|
three: Not Head ref
|
||||||
|
two: Not Head ref
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check %(if:equals=<string>)' '
|
||||||
|
git for-each-ref --format="%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)" refs/heads/ >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
Found master
|
||||||
|
Not master
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check %(if:notequals=<string>)' '
|
||||||
|
git for-each-ref --format="%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)" refs/heads/ >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
Found master
|
||||||
|
Not master
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Reference in New Issue
Block a user