Skip to content

Commit ed73fe5

Browse files
committed
Merge branch 'tr/line-log'
* tr/line-log: git-log(1): remove --full-line-diff description line-log: fix documentation formatting log -L: improve comments in process_all_files() log -L: store the path instead of a diff_filespec log -L: test merge of parallel modify/rename t4211: pass -M to 'git log -M -L...' test log -L: fix overlapping input ranges log -L: check range set invariants when we look it up Speed up log -L... -M log -L: :pattern:file syntax to find by funcname Implement line-history search (git log -L) Export rewrite_parents() for 'log -L' Refactor parse_loc
2 parents 4de1179 + 4999266 commit ed73fe5

31 files changed

+3374
-123
lines changed

Documentation/blame-options.txt

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,11 @@
99
--show-stats::
1010
Include additional statistics at the end of blame output.
1111

12-
-L <start>,<end>::
12+
-L <start>,<end>, -L :<regex>::
1313
Annotate only the given line range. <start> and <end> can take
1414
one of these forms:
1515

16-
- number
17-
+
18-
If <start> or <end> is a number, it specifies an
19-
absolute line number (lines count from 1).
20-
+
21-
22-
- /regex/
23-
+
24-
This form will use the first line matching the given
25-
POSIX regex. If <end> is a regex, it will search
26-
starting at the line given by <start>.
27-
+
28-
29-
- +offset or -offset
30-
+
31-
This is only valid for <end> and will specify a number
32-
of lines before or after the line given by <start>.
33-
+
16+
include::line-range-format.txt[]
3417

3518
-l::
3619
Show long rev (Default: off).

Documentation/git-blame.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ git-blame - Show what revision and author last modified each line of a file
88
SYNOPSIS
99
--------
1010
[verse]
11-
'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
12-
[-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>]
13-
[<rev> | --contents <file> | --reverse <rev>] [--] <file>
11+
'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
12+
[-L n,m | -L :fn] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
13+
[--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--] <file>
1414

1515
DESCRIPTION
1616
-----------

Documentation/git-log.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,19 @@ produced by --stat etc.
6262
Note that only message is considered, if also a diff is shown
6363
its size is not included.
6464

65+
-L <start>,<end>:<file>, -L :<regex>:<file>::
66+
67+
Trace the evolution of the line range given by "<start>,<end>"
68+
(or the funcname regex <regex>) within the <file>. You may
69+
not give any pathspec limiters. This is currently limited to
70+
a walk starting from a single revision, i.e., you may only
71+
give zero or one positive revision arguments.
72+
You can specify this option more than once.
73+
+
74+
<start> and <end> can take one of these forms:
75+
76+
include::line-range-format.txt[]
77+
6578
<revision range>::
6679
Show only commits in the specified revision range. When no
6780
<revision range> is specified, it defaults to `HEAD` (i.e. the
@@ -140,6 +153,11 @@ Examples
140153
This makes sense only when following a strict policy of merging all
141154
topic branches when staying on a single integration branch.
142155

156+
git log -L '/int main/',/^}/:main.c::
157+
158+
Shows how the function `main()` in the file 'main.c' evolved
159+
over time.
160+
143161
`git log -3`::
144162
Limits the number of commits to show to 3.
145163

Documentation/line-range-format.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
- number
2+
+
3+
If <start> or <end> is a number, it specifies an
4+
absolute line number (lines count from 1).
5+
+
6+
7+
- /regex/
8+
+
9+
This form will use the first line matching the given
10+
POSIX regex. If <end> is a regex, it will search
11+
starting at the line given by <start>.
12+
+
13+
14+
- +offset or -offset
15+
+
16+
This is only valid for <end> and will specify a number
17+
of lines before or after the line given by <start>.
18+
+
19+
20+
- :regex
21+
+
22+
If the option's argument is of the form :regex, it denotes the range
23+
from the first funcname line that matches <regex>, up to the next
24+
funcname line.
25+
+

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,8 @@ LIB_H += help.h
681681
LIB_H += http.h
682682
LIB_H += kwset.h
683683
LIB_H += levenshtein.h
684+
LIB_H += line-log.h
685+
LIB_H += line-range.h
684686
LIB_H += list-objects.h
685687
LIB_H += ll-merge.h
686688
LIB_H += log-tree.h
@@ -808,6 +810,8 @@ LIB_OBJS += hex.o
808810
LIB_OBJS += ident.o
809811
LIB_OBJS += kwset.o
810812
LIB_OBJS += levenshtein.o
813+
LIB_OBJS += line-log.o
814+
LIB_OBJS += line-range.o
811815
LIB_OBJS += list-objects.o
812816
LIB_OBJS += ll-merge.o
813817
LIB_OBJS += lockfile.o

builtin/blame.c

Lines changed: 8 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "parse-options.h"
2222
#include "utf8.h"
2323
#include "userdiff.h"
24+
#include "line-range.h"
2425

2526
static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file");
2627

@@ -566,11 +567,16 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
566567
dst->score = 0;
567568
}
568569

569-
static const char *nth_line(struct scoreboard *sb, int lno)
570+
static const char *nth_line(struct scoreboard *sb, long lno)
570571
{
571572
return sb->final_buf + sb->lineno[lno];
572573
}
573574

575+
static const char *nth_line_cb(void *data, long lno)
576+
{
577+
return nth_line((struct scoreboard *)data, lno);
578+
}
579+
574580
/*
575581
* It is known that lines between tlno to same came from parent, and e
576582
* has an overlap with that range. it also is known that parent's
@@ -1931,83 +1937,6 @@ static const char *add_prefix(const char *prefix, const char *path)
19311937
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
19321938
}
19331939

1934-
/*
1935-
* Parsing of (comma separated) one item in the -L option
1936-
*/
1937-
static const char *parse_loc(const char *spec,
1938-
struct scoreboard *sb, long lno,
1939-
long begin, long *ret)
1940-
{
1941-
char *term;
1942-
const char *line;
1943-
long num;
1944-
int reg_error;
1945-
regex_t regexp;
1946-
regmatch_t match[1];
1947-
1948-
/* Allow "-L <something>,+20" to mean starting at <something>
1949-
* for 20 lines, or "-L <something>,-5" for 5 lines ending at
1950-
* <something>.
1951-
*/
1952-
if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
1953-
num = strtol(spec + 1, &term, 10);
1954-
if (term != spec + 1) {
1955-
if (spec[0] == '-')
1956-
num = 0 - num;
1957-
if (0 < num)
1958-
*ret = begin + num - 2;
1959-
else if (!num)
1960-
*ret = begin;
1961-
else
1962-
*ret = begin + num;
1963-
return term;
1964-
}
1965-
return spec;
1966-
}
1967-
num = strtol(spec, &term, 10);
1968-
if (term != spec) {
1969-
*ret = num;
1970-
return term;
1971-
}
1972-
if (spec[0] != '/')
1973-
return spec;
1974-
1975-
/* it could be a regexp of form /.../ */
1976-
for (term = (char *) spec + 1; *term && *term != '/'; term++) {
1977-
if (*term == '\\')
1978-
term++;
1979-
}
1980-
if (*term != '/')
1981-
return spec;
1982-
1983-
/* try [spec+1 .. term-1] as regexp */
1984-
*term = 0;
1985-
begin--; /* input is in human terms */
1986-
line = nth_line(sb, begin);
1987-
1988-
if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
1989-
!(reg_error = regexec(&regexp, line, 1, match, 0))) {
1990-
const char *cp = line + match[0].rm_so;
1991-
const char *nline;
1992-
1993-
while (begin++ < lno) {
1994-
nline = nth_line(sb, begin);
1995-
if (line <= cp && cp < nline)
1996-
break;
1997-
line = nline;
1998-
}
1999-
*ret = begin;
2000-
regfree(&regexp);
2001-
*term++ = '/';
2002-
return term;
2003-
}
2004-
else {
2005-
char errbuf[1024];
2006-
regerror(reg_error, &regexp, errbuf, 1024);
2007-
die("-L parameter '%s': %s", spec + 1, errbuf);
2008-
}
2009-
}
2010-
20111940
/*
20121941
* Parsing of -L option
20131942
*/
@@ -2016,15 +1945,7 @@ static void prepare_blame_range(struct scoreboard *sb,
20161945
long lno,
20171946
long *bottom, long *top)
20181947
{
2019-
const char *term;
2020-
2021-
term = parse_loc(bottomtop, sb, lno, 1, bottom);
2022-
if (*term == ',') {
2023-
term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
2024-
if (*term)
2025-
usage(blame_usage);
2026-
}
2027-
if (*term)
1948+
if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path))
20281949
usage(blame_usage);
20291950
}
20301951

@@ -2574,10 +2495,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
25742495
bottom = top = 0;
25752496
if (bottomtop)
25762497
prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
2577-
if (bottom && top && top < bottom) {
2578-
long tmp;
2579-
tmp = top; top = bottom; bottom = tmp;
2580-
}
25812498
if (bottom < 1)
25822499
bottom = 1;
25832500
if (top < 1)

builtin/log.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "remote.h"
2020
#include "string-list.h"
2121
#include "parse-options.h"
22+
#include "line-log.h"
2223
#include "branch.h"
2324
#include "streaming.h"
2425
#include "version.h"
@@ -42,6 +43,12 @@ static const char * const builtin_log_usage[] = {
4243
NULL
4344
};
4445

46+
struct line_opt_callback_data {
47+
struct rev_info *rev;
48+
const char *prefix;
49+
struct string_list args;
50+
};
51+
4552
static int parse_decoration_style(const char *var, const char *value)
4653
{
4754
switch (git_config_maybe_bool(var, value)) {
@@ -76,6 +83,19 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
7683
return 0;
7784
}
7885

86+
static int log_line_range_callback(const struct option *option, const char *arg, int unset)
87+
{
88+
struct line_opt_callback_data *data = option->value;
89+
90+
if (!arg)
91+
return -1;
92+
93+
data->rev->line_level_traverse = 1;
94+
string_list_append(&data->args, arg);
95+
96+
return 0;
97+
}
98+
7999
static void cmd_log_init_defaults(struct rev_info *rev)
80100
{
81101
if (fmt_pretty)
@@ -98,16 +118,23 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
98118
{
99119
struct userformat_want w;
100120
int quiet = 0, source = 0, mailmap = 0;
121+
static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
101122

102123
const struct option builtin_log_options[] = {
103124
OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
104125
OPT_BOOL(0, "source", &source, N_("show source")),
105126
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
106127
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
107128
PARSE_OPT_OPTARG, decorate_callback},
129+
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
130+
"Process line range n,m in file, counting from 1",
131+
log_line_range_callback),
108132
OPT_END()
109133
};
110134

135+
line_cb.rev = rev;
136+
line_cb.prefix = prefix;
137+
111138
mailmap = use_mailmap_config;
112139
argc = parse_options(argc, argv, prefix,
113140
builtin_log_options, builtin_log_usage,
@@ -161,6 +188,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
161188
rev->show_decorations = 1;
162189
load_ref_decorations(decoration_style);
163190
}
191+
192+
if (rev->line_level_traverse)
193+
line_log_init(rev, line_cb.prefix, &line_cb.args);
194+
164195
setup_pager();
165196
}
166197

0 commit comments

Comments
 (0)