зеркало из https://github.com/microsoft/git.git
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:
Коммит
93e8cd8b6e
|
@ -12,7 +12,7 @@ SYNOPSIS
|
|||
[--list] [-v [--abbrev=<length> | --no-abbrev]]
|
||||
[--column[=<options>] | --no-column]
|
||||
[(--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-to=<upstream> | -u <upstream>) [<branchname>]
|
||||
'git branch' --unset-upstream [<branchname>]
|
||||
|
@ -253,6 +253,11 @@ start-point is either a local or remote-tracking branch.
|
|||
--points-at <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
|
||||
--------
|
||||
|
||||
|
|
|
@ -95,11 +95,20 @@ refname::
|
|||
The name of the ref (the part after $GIT_DIR/).
|
||||
For a non-ambiguous short name of the ref append `:short`.
|
||||
The option core.warnAmbiguousRefs is used to select the strict
|
||||
abbreviation mode. If `strip=<N>` is appended, strips `<N>`
|
||||
slash-separated path components from the front of the refname
|
||||
(e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.
|
||||
`<N>` must be a positive integer. If a displayed ref has fewer
|
||||
components than `<N>`, the command aborts with an error.
|
||||
abbreviation mode. If `lstrip=<N>` (`rstrip=<N>`) is appended, strips `<N>`
|
||||
slash-separated path components from the front (back) of the refname
|
||||
(e.g. `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and
|
||||
`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).
|
||||
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::
|
||||
The type of the object (`blob`, `tree`, `commit`, `tag`).
|
||||
|
@ -110,21 +119,31 @@ objectsize::
|
|||
objectname::
|
||||
The object name (aka SHA-1).
|
||||
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::
|
||||
The name of a local ref which can be considered ``upstream''
|
||||
from the displayed ref. Respects `:short` in the same way as
|
||||
`refname` above. Additionally respects `:track` to show
|
||||
"[ahead N, behind M]" and `:trackshort` to show the terse
|
||||
version: ">" (ahead), "<" (behind), "<>" (ahead and behind),
|
||||
or "=" (in sync). Has no effect if the ref does not have
|
||||
tracking information associated with it.
|
||||
from the displayed ref. Respects `:short`, `:lstrip` and
|
||||
`:rstrip` in the same way as `refname` above. Additionally
|
||||
respects `:track` to show "[ahead N, behind M]" and
|
||||
`:trackshort` to show the terse version: ">" (ahead), "<"
|
||||
(behind), "<>" (ahead and behind), or "=" (in sync). `:track`
|
||||
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::
|
||||
The name of a local ref which represents the `@{push}` location
|
||||
for the displayed ref. Respects `:short`, `:track`, and
|
||||
`:trackshort` options as `upstream` does. Produces an empty
|
||||
string if no `@{push}` ref is configured.
|
||||
The name of a local ref which represents the `@{push}`
|
||||
location for the displayed ref. Respects `:short`, `:lstrip`,
|
||||
`:rstrip`, `:track`, and `:trackshort` options as `upstream`
|
||||
does. Produces an empty string if no `@{push}` ref is
|
||||
configured.
|
||||
|
||||
HEAD::
|
||||
'*' if HEAD matches current ref (the checked out branch), ' '
|
||||
|
@ -149,6 +168,25 @@ align::
|
|||
quoted, but if nested then only the topmost level performs
|
||||
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
|
||||
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
|
||||
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
|
||||
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
|
||||
--------
|
||||
|
@ -273,6 +319,22 @@ eval=`git for-each-ref --shell --format="$fmt" \
|
|||
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
|
||||
--------
|
||||
linkgit:git-show-ref[1]
|
||||
|
|
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));
|
||||
|
|
493
ref-filter.c
493
ref-filter.c
|
@ -14,14 +14,50 @@
|
|||
#include "git-compat-util.h"
|
||||
#include "version.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 { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;
|
||||
|
||||
struct align {
|
||||
align_type position;
|
||||
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
|
||||
* a "*" to denote deref_tag().
|
||||
|
@ -38,13 +74,24 @@ static struct used_atom {
|
|||
union {
|
||||
char color[COLOR_MAXLEN];
|
||||
struct align align;
|
||||
enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
|
||||
remote_ref;
|
||||
struct {
|
||||
enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
|
||||
struct refname_atom refname;
|
||||
unsigned int nobracket : 1;
|
||||
} remote_ref;
|
||||
struct {
|
||||
enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
|
||||
unsigned int nlines;
|
||||
} 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;
|
||||
} *used_atom;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
atom->u.remote_ref = RR_NORMAL;
|
||||
atom->option = R_NORMAL;
|
||||
else if (!strcmp(arg, "short"))
|
||||
atom->u.remote_ref = RR_SHORTEN;
|
||||
else if (!strcmp(arg, "track"))
|
||||
atom->u.remote_ref = RR_TRACK;
|
||||
else if (!strcmp(arg, "trackshort"))
|
||||
atom->u.remote_ref = RR_TRACKSHORT;
|
||||
else
|
||||
die(_("unrecognized format: %%(%s)"), atom->name);
|
||||
atom->option = R_SHORT;
|
||||
else if (skip_prefix(arg, "lstrip=", &arg) ||
|
||||
skip_prefix(arg, "strip=", &arg)) {
|
||||
atom->option = R_LSTRIP;
|
||||
if (strtol_i(arg, 10, &atom->lstrip))
|
||||
die(_("Integer value expected refname:lstrip=%s"), arg);
|
||||
} 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)
|
||||
|
@ -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)
|
||||
{
|
||||
if (!arg)
|
||||
atom->u.objectname = O_FULL;
|
||||
atom->u.objectname.option = O_FULL;
|
||||
else if (!strcmp(arg, "short"))
|
||||
atom->u.objectname = O_SHORT;
|
||||
else
|
||||
atom->u.objectname.option = O_SHORT;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
const char *name;
|
||||
cmp_type cmp_type;
|
||||
void (*parser)(struct used_atom *atom, const char *arg);
|
||||
} valid_atom[] = {
|
||||
{ "refname" },
|
||||
{ "refname" , FIELD_STR, refname_atom_parser },
|
||||
{ "objecttype" },
|
||||
{ "objectsize", FIELD_ULONG },
|
||||
{ "objectname", FIELD_STR, objectname_atom_parser },
|
||||
|
@ -208,12 +322,15 @@ static struct {
|
|||
{ "contents", FIELD_STR, contents_atom_parser },
|
||||
{ "upstream", FIELD_STR, remote_ref_atom_parser },
|
||||
{ "push", FIELD_STR, remote_ref_atom_parser },
|
||||
{ "symref" },
|
||||
{ "symref", FIELD_STR, refname_atom_parser },
|
||||
{ "flag" },
|
||||
{ "HEAD" },
|
||||
{ "color", FIELD_STR, color_atom_parser },
|
||||
{ "align", FIELD_STR, align_atom_parser },
|
||||
{ "end" },
|
||||
{ "if", FIELD_STR, if_atom_parser },
|
||||
{ "then" },
|
||||
{ "else" },
|
||||
};
|
||||
|
||||
#define REF_FORMATTING_STATE_INIT { 0, NULL }
|
||||
|
@ -221,7 +338,7 @@ static struct {
|
|||
struct ref_formatting_stack {
|
||||
struct ref_formatting_stack *prev;
|
||||
struct strbuf output;
|
||||
void (*at_end)(struct ref_formatting_stack *stack);
|
||||
void (*at_end)(struct ref_formatting_stack **stack);
|
||||
void *at_end_data;
|
||||
};
|
||||
|
||||
|
@ -232,11 +349,9 @@ struct ref_formatting_state {
|
|||
|
||||
struct atom_value {
|
||||
const char *s;
|
||||
union {
|
||||
struct align align;
|
||||
} u;
|
||||
void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
|
||||
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);
|
||||
if (*atom == '*')
|
||||
need_tagged = 1;
|
||||
if (!strcmp(used_atom[at].name, "symref"))
|
||||
if (!strcmp(valid_atom[i].name, "symref"))
|
||||
need_symref = 1;
|
||||
return at;
|
||||
}
|
||||
|
@ -354,13 +469,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
|
|||
*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;
|
||||
|
||||
strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
|
||||
strbuf_swap(&stack->output, &s);
|
||||
strbuf_utf8_align(&s, align->position, align->width, cur->output.buf);
|
||||
strbuf_swap(&cur->output, &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);
|
||||
new = state->stack;
|
||||
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)
|
||||
|
@ -381,14 +605,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
|
|||
|
||||
if (!current->at_end)
|
||||
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
|
||||
* a supporting atom. If nested then perform quote formatting
|
||||
* only on the topmost supporting atom.
|
||||
*/
|
||||
if (!state->stack->prev->prev) {
|
||||
if (!current->prev->prev) {
|
||||
quote_formatting(&s, current->output.buf, state->quote_style);
|
||||
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)
|
||||
{
|
||||
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));
|
||||
return 1;
|
||||
} else if (atom->u.objectname == O_FULL) {
|
||||
} else if (atom->u.objectname.option == O_FULL) {
|
||||
v->s = xstrdup(sha1_to_hex(sha1));
|
||||
return 1;
|
||||
} else if (atom->u.objectname.option == O_LENGTH) {
|
||||
v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));
|
||||
return 1;
|
||||
} else
|
||||
die("BUG: unknown %%(objectname) option");
|
||||
}
|
||||
|
@ -887,50 +1117,108 @@ static inline char *copy_advance(char *dst, const char *src)
|
|||
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 nr = strtol(nr_arg, &end, 10);
|
||||
long remaining = nr;
|
||||
long remaining = len;
|
||||
const char *start = refname;
|
||||
|
||||
if (nr < 1 || *end != '\0')
|
||||
die(_(":strip= requires a positive integer argument"));
|
||||
if (len < 0) {
|
||||
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++) {
|
||||
case '\0':
|
||||
die(_("ref '%s' does not have %ld components to :strip"),
|
||||
refname, nr);
|
||||
return "";
|
||||
case '/':
|
||||
remaining--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
struct branch *branch, const char **s)
|
||||
{
|
||||
int num_ours, num_theirs;
|
||||
if (atom->u.remote_ref == RR_SHORTEN)
|
||||
*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
|
||||
else if (atom->u.remote_ref == RR_TRACK) {
|
||||
if (atom->u.remote_ref.option == RR_REF)
|
||||
*s = show_ref(&atom->u.remote_ref.refname, refname);
|
||||
else if (atom->u.remote_ref.option == RR_TRACK) {
|
||||
if (stat_tracking_info(branch, &num_ours,
|
||||
&num_theirs, NULL))
|
||||
return;
|
||||
|
||||
if (!num_ours && !num_theirs)
|
||||
&num_theirs, NULL)) {
|
||||
*s = xstrdup(msgs.gone);
|
||||
} else if (!num_ours && !num_theirs)
|
||||
*s = "";
|
||||
else if (!num_ours)
|
||||
*s = xstrfmt("[behind %d]", num_theirs);
|
||||
*s = xstrfmt(msgs.behind, num_theirs);
|
||||
else if (!num_theirs)
|
||||
*s = xstrfmt("[ahead %d]", num_ours);
|
||||
*s = xstrfmt(msgs.ahead, num_ours);
|
||||
else
|
||||
*s = xstrfmt("[ahead %d, behind %d]",
|
||||
*s = xstrfmt(msgs.ahead_behind,
|
||||
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,
|
||||
&num_theirs, NULL))
|
||||
return;
|
||||
|
@ -943,8 +1231,56 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
|
|||
*s = ">";
|
||||
else
|
||||
*s = "<>";
|
||||
} else /* RR_NORMAL */
|
||||
*s = refname;
|
||||
} else
|
||||
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];
|
||||
int deref = 0;
|
||||
const char *refname;
|
||||
const char *formatp;
|
||||
struct branch *branch = NULL;
|
||||
|
||||
v->handler = append_atom;
|
||||
v->atom = atom;
|
||||
|
||||
if (*name == '*') {
|
||||
deref = 1;
|
||||
|
@ -986,9 +1322,9 @@ static void populate_value(struct ref_array_item *ref)
|
|||
}
|
||||
|
||||
if (starts_with(name, "refname"))
|
||||
refname = ref->refname;
|
||||
refname = get_refname(atom, ref);
|
||||
else if (starts_with(name, "symref"))
|
||||
refname = ref->symref ? ref->symref : "";
|
||||
refname = get_symref(atom, ref);
|
||||
else if (starts_with(name, "upstream")) {
|
||||
const char *branch_name;
|
||||
/* only local branches may have an upstream */
|
||||
|
@ -1043,30 +1379,27 @@ static void populate_value(struct ref_array_item *ref)
|
|||
v->s = " ";
|
||||
continue;
|
||||
} else if (starts_with(name, "align")) {
|
||||
v->u.align = atom->u.align;
|
||||
v->handler = align_atom_handler;
|
||||
continue;
|
||||
} else if (!strcmp(name, "end")) {
|
||||
v->handler = end_atom_handler;
|
||||
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
|
||||
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)
|
||||
v->s = refname;
|
||||
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;
|
||||
struct strbuf *final_buf;
|
||||
struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
|
||||
|
||||
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)
|
||||
die(_("format: %%(end) atom missing"));
|
||||
final_buf = &state.stack->output;
|
||||
fwrite(final_buf->buf, 1, final_buf->len, stdout);
|
||||
strbuf_addbuf(final_buf, &state.stack->output);
|
||||
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');
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,9 @@ int parse_ref_filter_atom(const char *atom, const char *ep);
|
|||
int verify_ref_format(const char *format);
|
||||
/* Sort the given ref_array as per the ref_sorting provided */
|
||||
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 */
|
||||
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
|
||||
/* 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);
|
||||
/* Function to parse --merged and --no-merged options */
|
||||
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
|
||||
|
|
|
@ -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 &&
|
||||
cat >expect <<-\EOF &&
|
||||
ref-to-branch -> branch-one
|
||||
ref-to-remote -> refs/remotes/origin/branch-one
|
||||
ref-to-remote -> origin/branch-one
|
||||
EOF
|
||||
git branch >actual.raw &&
|
||||
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
|
||||
|
|
|
@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d
|
|||
b2 [ahead 1, behind 1] d
|
||||
b3 [behind 1] b
|
||||
b4 [ahead 2] f
|
||||
b5 g
|
||||
b5 [gone] g
|
||||
b6 c
|
||||
EOF
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ test_atom() {
|
|||
case "$1" in
|
||||
head) ref=refs/heads/master ;;
|
||||
tag) ref=refs/tags/testtag ;;
|
||||
sym) ref=refs/heads/sym ;;
|
||||
*) ref=$1 ;;
|
||||
esac
|
||||
printf '%s\n' "$3" >expected
|
||||
|
@ -50,16 +51,40 @@ test_atom() {
|
|||
|
||||
test_atom head refname refs/heads/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=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: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: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 objectsize 171
|
||||
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=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 parent ''
|
||||
test_atom head numparent 0
|
||||
|
@ -99,6 +124,8 @@ test_atom tag objecttype tag
|
|||
test_atom tag objectsize 154
|
||||
test_atom tag objectname $(git rev-parse 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 parent ''
|
||||
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_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' '
|
||||
git for-each-ref --format="%(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_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 () {
|
||||
f=$1 &&
|
||||
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: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: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' '
|
||||
cat >expected <<-\EOF &&
|
||||
|
||||
[gone]
|
||||
|
||||
EOF
|
||||
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_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
|
||||
test_when_finished "git checkout master" &&
|
||||
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_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
|
||||
|
|
|
@ -327,4 +327,98 @@ test_expect_success 'reverse version sort' '
|
|||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче