mirror of
https://github.com/git-for-windows/git.git
synced 2026-04-13 02:23:06 -05:00
Merge branch 'kh/name-rev-custom-format' into seen
"git name-rev" learned to use custom format instead of the object name in an extended SHA-1 expression form. Comments? * kh/name-rev-custom-format: name-rev: learn --format=<pretty> name-rev: wrap both blocks in braces
This commit is contained in:
@@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git name-rev' [--tags] [--refs=<pattern>]
|
||||
'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
|
||||
( --all | --annotate-stdin | <commit-ish>... )
|
||||
|
||||
DESCRIPTION
|
||||
@@ -21,6 +21,14 @@ format parsable by 'git rev-parse'.
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
--format=<pretty>::
|
||||
--no-format::
|
||||
Format revisions instead of outputting symbolic names. The
|
||||
default is `--no-format`.
|
||||
+
|
||||
Implies `--name-only`. The negation `--no-format` implies
|
||||
`--no-name-only` (the default for the command).
|
||||
|
||||
--tags::
|
||||
Do not use branch names, but only tags to name the commits
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include "commit-graph.h"
|
||||
#include "wildmatch.h"
|
||||
#include "mem-pool.h"
|
||||
#include "pretty.h"
|
||||
#include "revision.h"
|
||||
#include "notes.h"
|
||||
|
||||
/*
|
||||
* One day. See the 'name a rev shortly after epoch' test in t6120 when
|
||||
@@ -32,6 +35,16 @@ struct rev_name {
|
||||
int from_tag;
|
||||
};
|
||||
|
||||
struct pretty_format {
|
||||
struct pretty_print_context ctx;
|
||||
struct userformat_want want;
|
||||
};
|
||||
|
||||
struct format_cb_data {
|
||||
const char *format;
|
||||
int *name_only;
|
||||
};
|
||||
|
||||
define_commit_slab(commit_rev_name, struct rev_name);
|
||||
|
||||
static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
|
||||
@@ -452,7 +465,9 @@ static const char *get_exact_ref_match(const struct object *o)
|
||||
}
|
||||
|
||||
/* may return a constant string or use "buf" as scratch space */
|
||||
static const char *get_rev_name(const struct object *o, struct strbuf *buf)
|
||||
static const char *get_rev_name(const struct object *o,
|
||||
struct pretty_format *format_ctx,
|
||||
struct strbuf *buf)
|
||||
{
|
||||
struct rev_name *n;
|
||||
const struct commit *c;
|
||||
@@ -460,13 +475,32 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
|
||||
if (o->type != OBJ_COMMIT)
|
||||
return get_exact_ref_match(o);
|
||||
c = (const struct commit *) o;
|
||||
|
||||
if (format_ctx) {
|
||||
strbuf_reset(buf);
|
||||
|
||||
if (format_ctx->want.notes) {
|
||||
struct strbuf notebuf = STRBUF_INIT;
|
||||
|
||||
format_display_notes(&c->object.oid, ¬ebuf,
|
||||
get_log_output_encoding(),
|
||||
format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
|
||||
format_ctx->ctx.notes_message = strbuf_detach(¬ebuf, NULL);
|
||||
}
|
||||
|
||||
pretty_print_commit(&format_ctx->ctx, c, buf);
|
||||
FREE_AND_NULL(format_ctx->ctx.notes_message);
|
||||
|
||||
return buf->buf;
|
||||
}
|
||||
|
||||
n = get_commit_rev_name(c);
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
if (!n->generation)
|
||||
if (!n->generation) {
|
||||
return n->tip_name;
|
||||
else {
|
||||
} else {
|
||||
strbuf_reset(buf);
|
||||
strbuf_addstr(buf, n->tip_name);
|
||||
strbuf_strip_suffix(buf, "^0");
|
||||
@@ -477,6 +511,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
|
||||
|
||||
static void show_name(const struct object *obj,
|
||||
const char *caller_name,
|
||||
struct pretty_format *format_ctx,
|
||||
int always, int allow_undefined, int name_only)
|
||||
{
|
||||
const char *name;
|
||||
@@ -485,7 +520,7 @@ static void show_name(const struct object *obj,
|
||||
|
||||
if (!name_only)
|
||||
printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
|
||||
name = get_rev_name(obj, &buf);
|
||||
name = get_rev_name(obj, format_ctx, &buf);
|
||||
if (name)
|
||||
printf("%s\n", name);
|
||||
else if (allow_undefined)
|
||||
@@ -505,7 +540,9 @@ static char const * const name_rev_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void name_rev_line(char *p, struct name_ref_data *data)
|
||||
static void name_rev_line(char *p,
|
||||
struct name_ref_data *data,
|
||||
struct pretty_format *format_ctx)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int counter = 0;
|
||||
@@ -514,9 +551,9 @@ static void name_rev_line(char *p, struct name_ref_data *data)
|
||||
|
||||
for (p_start = p; *p; p++) {
|
||||
#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
|
||||
if (!ishex(*p))
|
||||
if (!ishex(*p)) {
|
||||
counter = 0;
|
||||
else if (++counter == hexsz &&
|
||||
} else if (++counter == hexsz &&
|
||||
!ishex(*(p+1))) {
|
||||
struct object_id oid;
|
||||
const char *name = NULL;
|
||||
@@ -530,7 +567,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
|
||||
struct object *o =
|
||||
lookup_object(the_repository, &oid);
|
||||
if (o)
|
||||
name = get_rev_name(o, &buf);
|
||||
name = get_rev_name(o, format_ctx, &buf);
|
||||
}
|
||||
*(p+1) = c;
|
||||
|
||||
@@ -552,6 +589,16 @@ static void name_rev_line(char *p, struct name_ref_data *data)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static int format_cb(const struct option *option,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
struct format_cb_data *data = option->value;
|
||||
data->format = arg;
|
||||
*data->name_only = !unset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_name_rev(int argc,
|
||||
const char **argv,
|
||||
const char *prefix,
|
||||
@@ -565,6 +612,12 @@ int cmd_name_rev(int argc,
|
||||
#endif
|
||||
int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
|
||||
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
|
||||
static struct format_cb_data format_cb_data = { 0 };
|
||||
struct display_notes_opt format_notes_opt;
|
||||
struct rev_info format_rev = REV_INFO_INIT;
|
||||
struct pretty_format *format_ctx = NULL;
|
||||
struct pretty_format format_pp = { 0 };
|
||||
struct string_list notes = STRING_LIST_INIT_NODUP;
|
||||
struct option opts[] = {
|
||||
OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
|
||||
OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
|
||||
@@ -582,6 +635,10 @@ int cmd_name_rev(int argc,
|
||||
PARSE_OPT_HIDDEN),
|
||||
#endif /* WITH_BREAKING_CHANGES */
|
||||
OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
|
||||
OPT_CALLBACK(0, "format", &format_cb_data, N_("format"),
|
||||
N_("pretty-print output instead"), format_cb),
|
||||
OPT_STRING_LIST(0, "notes", ¬es, N_("notes"),
|
||||
N_("display notes for --format")),
|
||||
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
|
||||
OPT_BOOL(0, "always", &always,
|
||||
N_("show abbreviated commit object as fallback")),
|
||||
@@ -590,6 +647,8 @@ int cmd_name_rev(int argc,
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
init_display_notes(&format_notes_opt);
|
||||
format_cb_data.name_only = &data.name_only;
|
||||
mem_pool_init(&string_pool, 0);
|
||||
init_commit_rev_name(&rev_names);
|
||||
repo_config(the_repository, git_default_config, NULL);
|
||||
@@ -604,6 +663,31 @@ int cmd_name_rev(int argc,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (format_cb_data.format) {
|
||||
get_commit_format(format_cb_data.format, &format_rev);
|
||||
format_pp.ctx.rev = &format_rev;
|
||||
format_pp.ctx.fmt = format_rev.commit_format;
|
||||
format_pp.ctx.abbrev = format_rev.abbrev;
|
||||
format_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;
|
||||
format_pp.ctx.date_mode = format_rev.date_mode;
|
||||
format_pp.ctx.color = GIT_COLOR_AUTO;
|
||||
|
||||
userformat_find_requirements(format_cb_data.format,
|
||||
&format_pp.want);
|
||||
if (format_pp.want.notes) {
|
||||
int ignore_show_notes = 0;
|
||||
struct string_list_item *n;
|
||||
|
||||
for_each_string_list_item(n, ¬es)
|
||||
enable_ref_display_notes(&format_notes_opt,
|
||||
&ignore_show_notes,
|
||||
n->string);
|
||||
load_display_notes(&format_notes_opt);
|
||||
}
|
||||
|
||||
format_ctx = &format_pp;
|
||||
}
|
||||
|
||||
if (all + annotate_stdin + !!argc > 1) {
|
||||
error("Specify either a list, or --all, not both!");
|
||||
usage_with_options(name_rev_usage, opts);
|
||||
@@ -661,7 +745,7 @@ int cmd_name_rev(int argc,
|
||||
|
||||
while (strbuf_getline(&sb, stdin) != EOF) {
|
||||
strbuf_addch(&sb, '\n');
|
||||
name_rev_line(sb.buf, &data);
|
||||
name_rev_line(sb.buf, &data, format_ctx);
|
||||
}
|
||||
strbuf_release(&sb);
|
||||
} else if (all) {
|
||||
@@ -672,18 +756,20 @@ int cmd_name_rev(int argc,
|
||||
struct object *obj = get_indexed_object(the_repository, i);
|
||||
if (!obj || obj->type != OBJ_COMMIT)
|
||||
continue;
|
||||
show_name(obj, NULL,
|
||||
show_name(obj, NULL, format_ctx,
|
||||
always, allow_undefined, data.name_only);
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < revs.nr; i++)
|
||||
show_name(revs.objects[i].item, revs.objects[i].name,
|
||||
show_name(revs.objects[i].item, revs.objects[i].name, format_ctx,
|
||||
always, allow_undefined, data.name_only);
|
||||
}
|
||||
|
||||
string_list_clear(&data.ref_filters, 0);
|
||||
string_list_clear(&data.exclude_filters, 0);
|
||||
string_list_clear(¬es, 0);
|
||||
release_display_notes(&format_notes_opt);
|
||||
mem_pool_discard(&string_pool, 0);
|
||||
object_array_clear(&revs);
|
||||
return 0;
|
||||
|
||||
@@ -658,6 +658,102 @@ test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'name-rev --format setup' '
|
||||
mkdir repo-format &&
|
||||
git -C repo-format init &&
|
||||
test_commit -C repo-format first &&
|
||||
test_commit -C repo-format second &&
|
||||
test_commit -C repo-format third &&
|
||||
test_commit -C repo-format fourth &&
|
||||
test_commit -C repo-format fifth &&
|
||||
test_commit -C repo-format sixth &&
|
||||
test_commit -C repo-format seventh &&
|
||||
test_commit -C repo-format eighth
|
||||
'
|
||||
|
||||
test_expect_success 'name-rev --format --no-name-only' '
|
||||
cat >expect <<-\EOF &&
|
||||
HEAD~3 [fifth]
|
||||
HEAD [eighth]
|
||||
HEAD~5 [third]
|
||||
EOF
|
||||
git -C repo-format name-rev --format="[%s]" \
|
||||
--no-name-only HEAD~3 HEAD HEAD~5 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'name-rev --format --no-format is the same as regular name-rev' '
|
||||
git -C repo-format name-rev HEAD~2 HEAD~3 >expect &&
|
||||
test_file_not_empty expect &&
|
||||
git -C repo-format name-rev --format="huh?" \
|
||||
--no-format HEAD~2 HEAD~3 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'name-rev --format=%s for argument revs' '
|
||||
cat >expect <<-\EOF &&
|
||||
eighth
|
||||
seventh
|
||||
fifth
|
||||
EOF
|
||||
git -C repo-format name-rev --format=%s \
|
||||
HEAD HEAD~ HEAD~3 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--name-rev --format=reference --annotate-stdin from rev-list same as log' '
|
||||
git -C repo-format log --format=reference >expect &&
|
||||
test_file_not_empty expect &&
|
||||
git -C repo-format rev-list HEAD >list &&
|
||||
git -C repo-format name-rev --format=reference \
|
||||
--annotate-stdin <list >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--name-rev --format=<pretty> --annotate-stdin with running text and tree oid' '
|
||||
cmit_oid=$(git -C repo-format rev-parse :/fifth) &&
|
||||
reference=$(git -C repo-format log -n1 --format=reference :/fifth) &&
|
||||
tree=$(git -C repo-format rev-parse HEAD^{tree}) &&
|
||||
cat >expect <<-EOF &&
|
||||
We thought we fixed this in ${reference}.
|
||||
But look at this tree: ${tree}.
|
||||
EOF
|
||||
git -C repo-format name-rev --format=reference --annotate-stdin \
|
||||
>actual <<-EOF &&
|
||||
We thought we fixed this in ${cmit_oid}.
|
||||
But look at this tree: ${tree}.
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'name-rev --format=<pretty> with %N (note)' '
|
||||
test_when_finished "git -C repo-format notes remove" &&
|
||||
git -C repo-format notes add -m"Make a note" &&
|
||||
printf "Make a note\n\n\n" >expect &&
|
||||
git -C repo-format name-rev --format="tformat:%N" \
|
||||
HEAD HEAD~ >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'name-rev --format=<pretty> --notes<ref>' '
|
||||
# One custom notes ref
|
||||
test_when_finished "git -C repo-format notes remove" &&
|
||||
test_when_finished "git -C repo-format notes --ref=word remove" &&
|
||||
git -C repo-format notes add -m"default" &&
|
||||
git -C repo-format notes --ref=word add -m"custom" &&
|
||||
printf "custom\n\n" >expect &&
|
||||
git -C repo-format name-rev --format="tformat:%N" \
|
||||
--notes=word \
|
||||
HEAD >actual &&
|
||||
test_cmp expect actual &&
|
||||
# Glob all
|
||||
printf "default\ncustom\n\n" >expect &&
|
||||
git -C repo-format name-rev --format="tformat:%N" \
|
||||
--notes=* \
|
||||
HEAD >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
# B
|
||||
# o
|
||||
# H \
|
||||
|
||||
Reference in New Issue
Block a user