Skip to content

Commit 14d8c63

Browse files
committed
line-log: reorganize and document line-log.c
With the range-set algebra extracted, line-log.c is the "git log -L" machinery proper, but its structure was still hard to see. Group the remaining code by concern behind short section banners (the per-path line_log_data list; parsing -L arguments; computing a commit's diff; and the history walk), moving collect_diff() and the line_log_data helpers next to the code they belong with. Add a file header describing the two traversal modes (eager parent rewriting via line_log_filter(), versus the lazy per-commit walk driven from get_commit_action()), and a comment on the per-commit entry point describing how it dispatches on a commit's shape. Rename line_log_process_ranges_arbitrary_commit() to the shorter line_log_process_commit(): it processes one commit, and "arbitrary" was more puzzling than descriptive. While here, drop two bits of dead code the motion made obvious: a redundant trailing "return;" at the end of fill_blob_sha1(), and a commented-out debug fprintf in diff_might_be_rename(). No functional change intended. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
1 parent e006328 commit 14d8c63

3 files changed

Lines changed: 183 additions & 144 deletions

File tree

line-log.c

Lines changed: 181 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,31 @@
2020
#include "bloom.h"
2121
#include "tree-walk.h"
2222

23+
/*
24+
* "git log -L": follow a set of line ranges through history.
25+
*
26+
* Each interesting commit carries a "struct line_log_data" (a per-path
27+
* list of tracked "struct range_set" line ranges) as a decoration in
28+
* rev->line_log_data. Walking from a commit to its parent maps those
29+
* ranges back across the commit's diff (process_ranges_ordinary_commit()
30+
* via range_set_map_across_diff()), so each parent inherits the ranges
31+
* it is responsible for.
32+
*
33+
* The walk runs in one of two modes:
34+
*
35+
* - Eager, under parent rewriting (want_ancestry(), e.g. --graph):
36+
* line_log_filter() processes every commit in prepare_revision_walk()
37+
* and rewrites the parent links.
38+
*
39+
* - Lazy, for a plain "git log -L": each commit is processed on demand
40+
* during the walk, from get_commit_action() in revision.c.
41+
*
42+
* Both share line_log_process_commit(), which processes
43+
* one commit and reports whether it changed a tracked range.
44+
*/
45+
46+
/* line_log_data: the sorted per-path list of tracked line ranges. */
47+
2348
static void line_log_data_init(struct line_log_data *r)
2449
{
2550
memset(r, 0, sizeof(struct line_log_data));
@@ -92,42 +117,119 @@ static void line_log_data_insert(struct line_log_data **list,
92117
}
93118
}
94119

95-
struct collect_diff_cbdata {
96-
struct diff_ranges *diff;
97-
};
120+
static struct line_log_data *line_log_data_copy_one(struct line_log_data *r)
121+
{
122+
struct line_log_data *ret = xmalloc(sizeof(*ret));
98123

99-
static int collect_diff_cb(long start_a, long count_a,
100-
long start_b, long count_b,
101-
void *data)
124+
assert(r);
125+
line_log_data_init(ret);
126+
range_set_copy(&ret->ranges, &r->ranges);
127+
128+
ret->path = xstrdup(r->path);
129+
130+
return ret;
131+
}
132+
133+
static struct line_log_data *
134+
line_log_data_copy(struct line_log_data *r)
102135
{
103-
struct collect_diff_cbdata *d = data;
136+
struct line_log_data *ret = NULL;
137+
struct line_log_data *tmp = NULL, *prev = NULL;
104138

105-
if (count_a >= 0)
106-
range_set_append(&d->diff->parent, start_a, start_a + count_a);
107-
if (count_b >= 0)
108-
range_set_append(&d->diff->target, start_b, start_b + count_b);
139+
assert(r);
140+
ret = tmp = prev = line_log_data_copy_one(r);
141+
r = r->next;
142+
while (r) {
143+
tmp = line_log_data_copy_one(r);
144+
prev->next = tmp;
145+
prev = tmp;
146+
r = r->next;
147+
}
109148

110-
return 0;
149+
return ret;
111150
}
112151

113-
static int collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
152+
/* merge two range sets across files */
153+
static struct line_log_data *line_log_data_merge(struct line_log_data *a,
154+
struct line_log_data *b)
114155
{
115-
struct collect_diff_cbdata cbdata = {NULL};
116-
xpparam_t xpp;
117-
xdemitconf_t xecfg;
118-
xdemitcb_t ecb;
156+
struct line_log_data *head = NULL, **pp = &head;
119157

120-
memset(&xpp, 0, sizeof(xpp));
121-
memset(&xecfg, 0, sizeof(xecfg));
122-
xecfg.ctxlen = xecfg.interhunkctxlen = 0;
158+
while (a || b) {
159+
struct line_log_data *src;
160+
struct line_log_data *src2 = NULL;
161+
struct line_log_data *d;
162+
int cmp;
163+
if (!a)
164+
cmp = 1;
165+
else if (!b)
166+
cmp = -1;
167+
else
168+
cmp = strcmp(a->path, b->path);
169+
if (cmp < 0) {
170+
src = a;
171+
a = a->next;
172+
} else if (cmp == 0) {
173+
src = a;
174+
a = a->next;
175+
src2 = b;
176+
b = b->next;
177+
} else {
178+
src = b;
179+
b = b->next;
180+
}
181+
d = xmalloc(sizeof(struct line_log_data));
182+
line_log_data_init(d);
183+
d->path = xstrdup(src->path);
184+
*pp = d;
185+
pp = &d->next;
186+
if (src2)
187+
range_set_union(&d->ranges, &src->ranges, &src2->ranges);
188+
else
189+
range_set_copy(&d->ranges, &src->ranges);
190+
}
123191

124-
cbdata.diff = out;
125-
xecfg.hunk_func = collect_diff_cb;
126-
memset(&ecb, 0, sizeof(ecb));
127-
ecb.priv = &cbdata;
128-
return xdi_diff(parent, target, &xpp, &xecfg, &ecb);
192+
return head;
193+
}
194+
195+
static void add_line_range(struct rev_info *revs, struct commit *commit,
196+
struct line_log_data *range)
197+
{
198+
struct line_log_data *old_line = NULL;
199+
struct line_log_data *new_line = NULL;
200+
201+
old_line = lookup_decoration(&revs->line_log_data, &commit->object);
202+
if (old_line && range) {
203+
new_line = line_log_data_merge(old_line, range);
204+
free_line_log_data(old_line);
205+
} else if (range)
206+
new_line = line_log_data_copy(range);
207+
208+
if (new_line)
209+
add_decoration(&revs->line_log_data, &commit->object, new_line);
210+
}
211+
212+
static void clear_commit_line_range(struct rev_info *revs, struct commit *commit)
213+
{
214+
struct line_log_data *r;
215+
r = lookup_decoration(&revs->line_log_data, &commit->object);
216+
if (!r)
217+
return;
218+
free_line_log_data(r);
219+
add_decoration(&revs->line_log_data, &commit->object, NULL);
129220
}
130221

222+
static struct line_log_data *lookup_line_range(struct rev_info *revs,
223+
struct commit *commit)
224+
{
225+
return lookup_decoration(&revs->line_log_data, &commit->object);
226+
}
227+
228+
/*
229+
* Parsing "-L" arguments into an initial set of tracked ranges and the
230+
* pathspec that limits the diff. line_log_init() is the entry point.
231+
*/
232+
131233
static struct commit *check_single_commit(struct rev_info *revs)
132234
{
133235
struct object *commit = NULL;
@@ -164,8 +266,6 @@ static void fill_blob_sha1(struct repository *r, struct commit *commit,
164266
if (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))
165267
die("There is no path %s in the commit", spec->path);
166268
fill_filespec(spec, &oid, 1, mode);
167-
168-
return;
169269
}
170270

171271
static void fill_line_ends(struct repository *r,
@@ -282,114 +382,6 @@ parse_lines(struct repository *r, struct commit *commit,
282382
return ranges;
283383
}
284384

285-
static struct line_log_data *line_log_data_copy_one(struct line_log_data *r)
286-
{
287-
struct line_log_data *ret = xmalloc(sizeof(*ret));
288-
289-
assert(r);
290-
line_log_data_init(ret);
291-
range_set_copy(&ret->ranges, &r->ranges);
292-
293-
ret->path = xstrdup(r->path);
294-
295-
return ret;
296-
}
297-
298-
static struct line_log_data *
299-
line_log_data_copy(struct line_log_data *r)
300-
{
301-
struct line_log_data *ret = NULL;
302-
struct line_log_data *tmp = NULL, *prev = NULL;
303-
304-
assert(r);
305-
ret = tmp = prev = line_log_data_copy_one(r);
306-
r = r->next;
307-
while (r) {
308-
tmp = line_log_data_copy_one(r);
309-
prev->next = tmp;
310-
prev = tmp;
311-
r = r->next;
312-
}
313-
314-
return ret;
315-
}
316-
317-
/* merge two range sets across files */
318-
static struct line_log_data *line_log_data_merge(struct line_log_data *a,
319-
struct line_log_data *b)
320-
{
321-
struct line_log_data *head = NULL, **pp = &head;
322-
323-
while (a || b) {
324-
struct line_log_data *src;
325-
struct line_log_data *src2 = NULL;
326-
struct line_log_data *d;
327-
int cmp;
328-
if (!a)
329-
cmp = 1;
330-
else if (!b)
331-
cmp = -1;
332-
else
333-
cmp = strcmp(a->path, b->path);
334-
if (cmp < 0) {
335-
src = a;
336-
a = a->next;
337-
} else if (cmp == 0) {
338-
src = a;
339-
a = a->next;
340-
src2 = b;
341-
b = b->next;
342-
} else {
343-
src = b;
344-
b = b->next;
345-
}
346-
d = xmalloc(sizeof(struct line_log_data));
347-
line_log_data_init(d);
348-
d->path = xstrdup(src->path);
349-
*pp = d;
350-
pp = &d->next;
351-
if (src2)
352-
range_set_union(&d->ranges, &src->ranges, &src2->ranges);
353-
else
354-
range_set_copy(&d->ranges, &src->ranges);
355-
}
356-
357-
return head;
358-
}
359-
360-
static void add_line_range(struct rev_info *revs, struct commit *commit,
361-
struct line_log_data *range)
362-
{
363-
struct line_log_data *old_line = NULL;
364-
struct line_log_data *new_line = NULL;
365-
366-
old_line = lookup_decoration(&revs->line_log_data, &commit->object);
367-
if (old_line && range) {
368-
new_line = line_log_data_merge(old_line, range);
369-
free_line_log_data(old_line);
370-
} else if (range)
371-
new_line = line_log_data_copy(range);
372-
373-
if (new_line)
374-
add_decoration(&revs->line_log_data, &commit->object, new_line);
375-
}
376-
377-
static void clear_commit_line_range(struct rev_info *revs, struct commit *commit)
378-
{
379-
struct line_log_data *r;
380-
r = lookup_decoration(&revs->line_log_data, &commit->object);
381-
if (!r)
382-
return;
383-
free_line_log_data(r);
384-
add_decoration(&revs->line_log_data, &commit->object, NULL);
385-
}
386-
387-
static struct line_log_data *lookup_line_range(struct rev_info *revs,
388-
struct commit *commit)
389-
{
390-
return lookup_decoration(&revs->line_log_data, &commit->object);
391-
}
392-
393385
static int same_paths_in_pathspec_and_range(struct pathspec *pathspec,
394386
struct line_log_data *range)
395387
{
@@ -434,6 +426,47 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
434426
free_line_log_data(range);
435427
}
436428

429+
/*
430+
* Compute one commit's diff against a parent as a set of changed line
431+
* ranges (collect_diff), then walk the tracked ranges back across it.
432+
*/
433+
434+
struct collect_diff_cbdata {
435+
struct diff_ranges *diff;
436+
};
437+
438+
static int collect_diff_cb(long start_a, long count_a,
439+
long start_b, long count_b,
440+
void *data)
441+
{
442+
struct collect_diff_cbdata *d = data;
443+
444+
if (count_a >= 0)
445+
range_set_append(&d->diff->parent, start_a, start_a + count_a);
446+
if (count_b >= 0)
447+
range_set_append(&d->diff->target, start_b, start_b + count_b);
448+
449+
return 0;
450+
}
451+
452+
static int collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
453+
{
454+
struct collect_diff_cbdata cbdata = {NULL};
455+
xpparam_t xpp;
456+
xdemitconf_t xecfg;
457+
xdemitcb_t ecb;
458+
459+
memset(&xpp, 0, sizeof(xpp));
460+
memset(&xecfg, 0, sizeof(xecfg));
461+
xecfg.ctxlen = xecfg.interhunkctxlen = 0;
462+
463+
cbdata.diff = out;
464+
xecfg.hunk_func = collect_diff_cb;
465+
memset(&ecb, 0, sizeof(ecb));
466+
ecb.priv = &cbdata;
467+
return xdi_diff(parent, target, &xpp, &xecfg, &ecb);
468+
}
469+
437470
static void move_diff_queue(struct diff_queue_struct *dst,
438471
struct diff_queue_struct *src)
439472
{
@@ -475,11 +508,8 @@ static inline int diff_might_be_rename(void)
475508
{
476509
int i;
477510
for (i = 0; i < diff_queued_diff.nr; i++)
478-
if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) {
479-
/* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */
480-
/* diff_queued_diff.queue[i]->two->path); */
511+
if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one))
481512
return 1;
482-
}
483513
return 0;
484514
}
485515

@@ -695,6 +725,8 @@ static int bloom_filter_check(struct rev_info *rev,
695725
return result;
696726
}
697727

728+
/* Walking history: map each commit's tracked ranges to its parent(s). */
729+
698730
static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit,
699731
struct line_log_data *range)
700732
{
@@ -780,7 +812,15 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
780812
/* NEEDSWORK evil merge detection stuff */
781813
}
782814

783-
int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)
815+
/*
816+
* Entry point for one commit (both traversal modes call this). Dispatch
817+
* on the commit's shape: no tracked ranges (nothing to do); a Bloom
818+
* filter proving the file untouched (carry ranges to the sole parent
819+
* without diffing); a merge (map across each parent, splitting the
820+
* ranges); or an ordinary commit. A commit that changed no tracked
821+
* range is marked TREESAME so the caller can prune it.
822+
*/
823+
int line_log_process_commit(struct rev_info *rev, struct commit *commit)
784824
{
785825
struct line_log_data *range = lookup_line_range(rev, commit);
786826
int changed = 0;
@@ -828,7 +868,7 @@ int line_log_filter(struct rev_info *rev)
828868
while (list) {
829869
struct commit_list *to_free = NULL;
830870
commit = list->item;
831-
if (line_log_process_ranges_arbitrary_commit(rev, commit)) {
871+
if (line_log_process_commit(rev, commit)) {
832872
*pp = list;
833873
pp = &list->next;
834874
} else

0 commit comments

Comments
 (0)