Merge branch 'pb/blame-funcname-range-userdiff'

"git blame -L :funcname -- path" did not work well for a path for
which a userdiff driver is defined.

* pb/blame-funcname-range-userdiff:
  blame: simplify 'setup_blame_bloom_data' interface
  blame: simplify 'setup_scoreboard' interface
  blame: enable funcname blaming with userdiff driver
  line-log: mention both modes in 'blame' and 'log' short help
  doc: add more pointers to gitattributes(5) for userdiff
  blame-options.txt: also mention 'funcname' in '-L' description
  doc: line-range: improve formatting
  doc: log, gitk: move '-L' description to 'line-range-options.txt'
This commit is contained in:
Junio C Hamano 2020-11-18 13:32:53 -08:00
commit 3f6dc9c366
12 changed files with 79 additions and 72 deletions

View File

@ -11,11 +11,12 @@
-L <start>,<end>::
-L :<funcname>::
Annotate only the given line range. May be specified multiple times.
Overlapping ranges are allowed.
Annotate only the line range given by '<start>,<end>',
or by the function name regex '<funcname>'.
May be specified multiple times. Overlapping ranges are allowed.
+
<start> and <end> are optional. ``-L <start>'' or ``-L <start>,'' spans from
<start> to end of file. ``-L ,<end>'' spans from start of file to <end>.
'<start>' and '<end>' are optional. `-L <start>` or `-L <start>,` spans from
'<start>' to end of file. `-L ,<end>` spans from start of file to '<end>'.
+
include::line-range-format.txt[]

View File

@ -704,7 +704,10 @@ endif::git-format-patch[]
-W::
--function-context::
Show whole surrounding functions of changes.
Show whole function as context lines for each change.
The function names are determined in the same way as
`git diff` works out patch hunk headers (see 'Defining a
custom hunk-header' in linkgit:gitattributes[5]).
ifndef::git-format-patch[]
ifndef::git-log[]

View File

@ -241,7 +241,7 @@ providing this option will cause it to die.
--show-function::
Show the preceding line that contains the function name of
the match, unless the matching line is a function name itself.
The name is determined in the same way as 'git diff' works out
The name is determined in the same way as `git diff` works out
patch hunk headers (see 'Defining a custom hunk-header' in
linkgit:gitattributes[5]).
@ -266,7 +266,9 @@ providing this option will cause it to die.
Show the surrounding text from the previous line containing a
function name up to the one before the next function name,
effectively showing the whole function in which the match was
found.
found. The function names are determined in the same way as
`git diff` works out patch hunk headers (see 'Defining a
custom hunk-header' in linkgit:gitattributes[5]).
--threads <num>::
Number of grep worker threads to use.

View File

@ -77,20 +77,7 @@ produced by `--stat`, etc.
Intended to speed up tools that read log messages from `git log`
output by allowing them to allocate space in advance.
-L <start>,<end>:<file>::
-L :<funcname>:<file>::
Trace the evolution of the line range given by "<start>,<end>"
(or the function name regex <funcname>) within the <file>. You may
not give any pathspec limiters. This is currently limited to
a walk starting from a single revision, i.e., you may only
give zero or one positive revision arguments, and
<start> and <end> (or <funcname>) must exist in the starting revision.
You can specify this option more than once. Implies `--patch`.
Patch output can be suppressed using `--no-patch`, but other diff formats
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
`--name-only`, `--name-status`, `--check`) are not currently implemented.
+
include::line-range-format.txt[]
include::line-range-options.txt[]
<revision range>::
Show only commits in the specified revision range. When no

View File

@ -98,25 +98,7 @@ linkgit:git-rev-list[1] for a complete list.
(See "History simplification" in linkgit:git-log[1] for a more
detailed explanation.)
-L<start>,<end>:<file>::
-L:<funcname>:<file>::
Trace the evolution of the line range given by "<start>,<end>"
(or the function name regex <funcname>) within the <file>. You may
not give any pathspec limiters. This is currently limited to
a walk starting from a single revision, i.e., you may only
give zero or one positive revision arguments, and
<start> and <end> (or <funcname>) must exist in the starting revision.
You can specify this option more than once. Implies `--patch`.
Patch output can be suppressed using `--no-patch`, but other diff formats
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
`--name-only`, `--name-status`, `--check`) are not currently implemented.
+
*Note:* gitk (unlike linkgit:git-log[1]) currently only understands
this option if you specify it "glued together" with its argument. Do
*not* put a space after `-L`.
+
include::line-range-format.txt[]
include::line-range-options.txt[]
<revision range>::

View File

@ -1,30 +1,32 @@
<start> and <end> can take one of these forms:
'<start>' and '<end>' can take one of these forms:
- number
+
If <start> or <end> is a number, it specifies an
If '<start>' or '<end>' is a number, it specifies an
absolute line number (lines count from 1).
+
- /regex/
- `/regex/`
+
This form will use the first line matching the given
POSIX regex. If <start> is a regex, it will search from the end of
POSIX regex. If '<start>' is a regex, it will search from the end of
the previous `-L` range, if any, otherwise from the start of file.
If <start> is ``^/regex/'', it will search from the start of file.
If <end> is a regex, it will search
starting at the line given by <start>.
If '<start>' is `^/regex/`, it will search from the start of file.
If '<end>' is a regex, it will search
starting at the line given by '<start>'.
+
- +offset or -offset
+
This is only valid for <end> and will specify a number
of lines before or after the line given by <start>.
This is only valid for '<end>' and will specify a number
of lines before or after the line given by '<start>'.
+
If ``:<funcname>'' is given in place of <start> and <end>, it is a
If `:<funcname>` is given in place of '<start>' and '<end>', it is a
regular expression that denotes the range from the first funcname line
that matches <funcname>, up to the next funcname line. ``:<funcname>''
that matches '<funcname>', up to the next funcname line. `:<funcname>`
searches from the end of the previous `-L` range, if any, otherwise
from the start of file. ``^:<funcname>'' searches from the start of
file.
from the start of file. `^:<funcname>` searches from the start of
file. The function names are determined in the same way as `git diff`
works out patch hunk headers (see 'Defining a custom hunk-header'
in linkgit:gitattributes[5]).

View File

@ -0,0 +1,15 @@
-L<start>,<end>:<file>::
-L:<funcname>:<file>::
Trace the evolution of the line range given by '<start>,<end>',
or by the function name regex '<funcname>', within the '<file>'. You may
not give any pathspec limiters. This is currently limited to
a walk starting from a single revision, i.e., you may only
give zero or one positive revision arguments, and
'<start>' and '<end>' (or '<funcname>') must exist in the starting revision.
You can specify this option more than once. Implies `--patch`.
Patch output can be suppressed using `--no-patch`, but other diff formats
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
`--name-only`, `--name-status`, `--check`) are not currently implemented.
+
include::line-range-format.txt[]

16
blame.c
View File

@ -2764,7 +2764,6 @@ void init_scoreboard(struct blame_scoreboard *sb)
}
void setup_scoreboard(struct blame_scoreboard *sb,
const char *path,
struct blame_origin **orig)
{
const char *final_commit_name = NULL;
@ -2803,7 +2802,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
setup_work_tree();
sb->final = fake_working_tree_commit(sb->repo,
&sb->revs->diffopt,
path, sb->contents_from);
sb->path, sb->contents_from);
add_pending_object(sb->revs, &(sb->final->object), ":");
}
@ -2846,12 +2845,12 @@ void setup_scoreboard(struct blame_scoreboard *sb,
sb->final_buf_size = o->file.size;
}
else {
o = get_origin(sb->final, path);
o = get_origin(sb->final, sb->path);
if (fill_blob_sha1_and_mode(sb->repo, o))
die(_("no such path %s in %s"), path, final_commit_name);
die(_("no such path %s in %s"), sb->path, final_commit_name);
if (sb->revs->diffopt.flags.allow_textconv &&
textconv_object(sb->repo, path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
textconv_object(sb->repo, sb->path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
&sb->final_buf_size))
;
else
@ -2861,7 +2860,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
if (!sb->final_buf)
die(_("cannot read blob %s for path %s"),
oid_to_hex(&o->blob_oid),
path);
sb->path);
}
sb->num_read_blob++;
prepare_lines(sb);
@ -2888,8 +2887,7 @@ struct blame_entry *blame_entry_prepend(struct blame_entry *head,
return new_head;
}
void setup_blame_bloom_data(struct blame_scoreboard *sb,
const char *path)
void setup_blame_bloom_data(struct blame_scoreboard *sb)
{
struct blame_bloom_data *bd;
struct bloom_filter_settings *bs;
@ -2909,7 +2907,7 @@ void setup_blame_bloom_data(struct blame_scoreboard *sb,
bd->nr = 0;
ALLOC_ARRAY(bd->keys, bd->alloc);
add_bloom_key(bd, path);
add_bloom_key(bd, sb->path);
sb->bloom_data = bd;
}

View File

@ -181,10 +181,8 @@ const char *blame_nth_line(struct blame_scoreboard *sb, long lno);
void init_scoreboard(struct blame_scoreboard *sb);
void setup_scoreboard(struct blame_scoreboard *sb,
const char *path,
struct blame_origin **orig);
void setup_blame_bloom_data(struct blame_scoreboard *sb,
const char *path);
void setup_blame_bloom_data(struct blame_scoreboard *sb);
void cleanup_scoreboard(struct blame_scoreboard *sb);
struct blame_entry *blame_entry_prepend(struct blame_entry *head,

View File

@ -891,7 +891,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback),
OPT_CALLBACK_F('M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback),
OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")),
OPT_STRING_LIST('L', NULL, &range_list, N_("range"),
N_("Process only line range <start>,<end> or function :<funcname>")),
OPT__ABBREV(&abbrev),
OPT_END()
};
@ -1082,17 +1083,18 @@ parse_done:
sb.contents_from = contents_from;
sb.reverse = reverse;
sb.repo = the_repository;
sb.path = path;
build_ignorelist(&sb, &ignore_revs_file_list, &ignore_rev_list);
string_list_clear(&ignore_revs_file_list, 0);
string_list_clear(&ignore_rev_list, 0);
setup_scoreboard(&sb, path, &o);
setup_scoreboard(&sb, &o);
/*
* Changed-path Bloom filters are disabled when looking
* for copies.
*/
if (!(opt & PICKAXE_BLAME_COPY))
setup_blame_bloom_data(&sb, path);
setup_blame_bloom_data(&sb);
lno = sb.num_lines;
@ -1111,7 +1113,7 @@ parse_done:
if ((!lno && (top || bottom)) || lno < bottom)
die(Q_("file %s has only %lu line",
"file %s has only %lu lines",
lno), path, lno);
lno), sb.path, lno);
if (bottom < 1)
bottom = 1;
if (top < 1 || lno < top)
@ -1136,7 +1138,6 @@ parse_done:
string_list_clear(&range_list, 0);
sb.ent = NULL;
sb.path = path;
if (blame_move_score)
sb.move_score = blame_move_score;

View File

@ -183,8 +183,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
N_("pattern"), N_("do not decorate refs that match <pattern>")),
OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback),
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
N_("Process line range n,m in file, counting from 1"),
OPT_CALLBACK('L', NULL, &line_cb, "range:file",
N_("Trace the evolution of line range <start>,<end> or function :<funcname> in <file>"),
log_line_range_callback),
OPT_END()
};

View File

@ -479,6 +479,24 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
'
test_expect_success 'setup -L :funcname with userdiff driver' '
echo "fortran-* diff=fortran" >.gitattributes &&
fortran_file=fortran-external-function &&
orig_file="$TEST_DIRECTORY/t4018/$fortran_file" &&
cp $orig_file . &&
git add $fortran_file &&
GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
git commit -m "add fortran file" &&
sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >$fortran_file &&
git add $fortran_file &&
GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
git commit -m "change fortran file"
'
test_expect_success 'blame -L :funcname with userdiff driver' '
check_count -f fortran-external-function -L:RIGHT A 7 B 1
'
test_expect_success 'setup incremental' '
(
GIT_AUTHOR_NAME=I &&