summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2026-04-07 14:59:27 -0700
committerJunio C Hamano <gitster@pobox.com>2026-04-07 14:59:27 -0700
commit1678b7de977043f31242d2029259797eee620236 (patch)
tree749d1f5c103743b03ab9862d34e104deae16f397
parentfb5516997ef3f882d8e53ce70ba6077533683621 (diff)
parent512536a09ea2964e93226f219898ee0a09d85a70 (diff)
downloadgit-1678b7de977043f31242d2029259797eee620236.tar.xz
Merge branch 'mm/line-log-use-standard-diff-output'
The way the "git log -L<range>:<file>" feature is bolted onto the log/diff machinery is being reworked a bit to make the feature compatible with more diff options, like -S/G. * mm/line-log-use-standard-diff-output: doc: note that -L supports patch formatting and pickaxe options t4211: add tests for -L with standard diff options line-log: route -L output through the standard diff pipeline line-log: fix crash when combined with pickaxe options
-rw-r--r--Documentation/line-range-options.adoc4
-rw-r--r--diff.c279
-rw-r--r--diffcore.h16
-rw-r--r--line-log.c196
-rw-r--r--line-log.h14
-rw-r--r--revision.c2
-rwxr-xr-xt/t4211-line-log.sh348
-rw-r--r--t/t4211/sha1/expect.beginning-of-file4
-rw-r--r--t/t4211/sha1/expect.end-of-file11
-rw-r--r--t/t4211/sha1/expect.move-support-f5
-rw-r--r--t/t4211/sha1/expect.multiple10
-rw-r--r--t/t4211/sha1/expect.multiple-overlapping7
-rw-r--r--t/t4211/sha1/expect.multiple-superset7
-rw-r--r--t/t4211/sha1/expect.no-assertion-error12
-rw-r--r--t/t4211/sha1/expect.parallel-change-f-to-main7
-rw-r--r--t/t4211/sha1/expect.simple-f4
-rw-r--r--t/t4211/sha1/expect.simple-f-to-main5
-rw-r--r--t/t4211/sha1/expect.simple-main11
-rw-r--r--t/t4211/sha1/expect.simple-main-to-end11
-rw-r--r--t/t4211/sha1/expect.two-ranges10
-rw-r--r--t/t4211/sha1/expect.vanishes-early10
-rw-r--r--t/t4211/sha256/expect.beginning-of-file4
-rw-r--r--t/t4211/sha256/expect.end-of-file11
-rw-r--r--t/t4211/sha256/expect.move-support-f5
-rw-r--r--t/t4211/sha256/expect.multiple10
-rw-r--r--t/t4211/sha256/expect.multiple-overlapping7
-rw-r--r--t/t4211/sha256/expect.multiple-superset7
-rw-r--r--t/t4211/sha256/expect.no-assertion-error12
-rw-r--r--t/t4211/sha256/expect.parallel-change-f-to-main7
-rw-r--r--t/t4211/sha256/expect.simple-f4
-rw-r--r--t/t4211/sha256/expect.simple-f-to-main5
-rw-r--r--t/t4211/sha256/expect.simple-main11
-rw-r--r--t/t4211/sha256/expect.simple-main-to-end11
-rw-r--r--t/t4211/sha256/expect.two-ranges10
-rw-r--r--t/t4211/sha256/expect.vanishes-early10
35 files changed, 870 insertions, 217 deletions
diff --git a/Documentation/line-range-options.adoc b/Documentation/line-range-options.adoc
index c44ba05320..ecb2c79fb9 100644
--- a/Documentation/line-range-options.adoc
+++ b/Documentation/line-range-options.adoc
@@ -12,4 +12,8 @@
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
`--name-only`, `--name-status`, `--check`) are not currently implemented.
+
+Patch formatting options such as `--word-diff`, `--color-moved`,
+`--no-prefix`, and whitespace options (`-w`, `-b`) are supported,
+as are pickaxe options (`-S`, `-G`).
++
include::line-range-format.adoc[]
diff --git a/diff.c b/diff.c
index e87847fa4b..397e38b41c 100644
--- a/diff.c
+++ b/diff.c
@@ -608,6 +608,52 @@ struct emit_callback {
struct strbuf *header;
};
+/*
+ * State for the line-range callback wrappers that sit between
+ * xdi_diff_outf() and fn_out_consume(). xdiff produces a normal,
+ * unfiltered diff; the wrappers intercept each hunk header and line,
+ * track post-image position, and forward only lines that fall within
+ * the requested ranges. Contiguous in-range lines are collected into
+ * range hunks and flushed with a synthetic @@ header so that
+ * fn_out_consume() sees well-formed unified-diff fragments.
+ *
+ * Removal lines ('-') cannot be classified by post-image position, so
+ * they are buffered in pending_rm until the next '+' or ' ' line
+ * reveals whether they precede an in-range line (flush into range hunk) or
+ * an out-of-range line (discard).
+ */
+struct line_range_callback {
+ xdiff_emit_line_fn orig_line_fn;
+ void *orig_cb_data;
+ const struct range_set *ranges; /* 0-based [start, end) */
+ unsigned int cur_range; /* index into the range_set */
+
+ /* Post/pre-image line counters (1-based, set from hunk headers) */
+ long lno_post;
+ long lno_pre;
+
+ /*
+ * Function name from most recent xdiff hunk header;
+ * size matches struct func_line.buf in xdiff/xemit.c.
+ */
+ char func[80];
+ long funclen;
+
+ /* Range hunk being accumulated for the current range */
+ struct strbuf rhunk;
+ long rhunk_old_begin, rhunk_old_count;
+ long rhunk_new_begin, rhunk_new_count;
+ int rhunk_active;
+ int rhunk_has_changes; /* any '+' or '-' lines? */
+
+ /* Removal lines not yet known to be in-range */
+ struct strbuf pending_rm;
+ int pending_rm_count;
+ long pending_rm_pre_begin; /* pre-image line of first pending */
+
+ int ret; /* latched error from orig_line_fn */
+};
+
static int count_lines(const char *data, int size)
{
int count, ch, completely_empty = 1, nl_just_seen = 0;
@@ -2493,6 +2539,188 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED
return 1;
}
+static void discard_pending_rm(struct line_range_callback *s)
+{
+ strbuf_reset(&s->pending_rm);
+ s->pending_rm_count = 0;
+}
+
+static void flush_rhunk(struct line_range_callback *s)
+{
+ struct strbuf hdr = STRBUF_INIT;
+ const char *p, *end;
+
+ if (!s->rhunk_active || s->ret)
+ return;
+
+ /* Drain any pending removal lines into the range hunk */
+ if (s->pending_rm_count) {
+ strbuf_addbuf(&s->rhunk, &s->pending_rm);
+ s->rhunk_old_count += s->pending_rm_count;
+ s->rhunk_has_changes = 1;
+ discard_pending_rm(s);
+ }
+
+ /*
+ * Suppress context-only hunks: they contain no actual changes
+ * and would just be noise. This can happen when the inflated
+ * ctxlen causes xdiff to emit context covering a range that
+ * has no changes in this commit.
+ */
+ if (!s->rhunk_has_changes) {
+ s->rhunk_active = 0;
+ strbuf_reset(&s->rhunk);
+ return;
+ }
+
+ strbuf_addf(&hdr, "@@ -%ld,%ld +%ld,%ld @@",
+ s->rhunk_old_begin, s->rhunk_old_count,
+ s->rhunk_new_begin, s->rhunk_new_count);
+ if (s->funclen > 0) {
+ strbuf_addch(&hdr, ' ');
+ strbuf_add(&hdr, s->func, s->funclen);
+ }
+ strbuf_addch(&hdr, '\n');
+
+ s->ret = s->orig_line_fn(s->orig_cb_data, hdr.buf, hdr.len);
+ strbuf_release(&hdr);
+
+ /*
+ * Replay buffered lines one at a time through fn_out_consume.
+ * The cast discards const because xdiff_emit_line_fn takes
+ * char *, though fn_out_consume does not modify the buffer.
+ */
+ p = s->rhunk.buf;
+ end = p + s->rhunk.len;
+ while (!s->ret && p < end) {
+ const char *eol = memchr(p, '\n', end - p);
+ unsigned long line_len = eol ? (unsigned long)(eol - p + 1)
+ : (unsigned long)(end - p);
+ s->ret = s->orig_line_fn(s->orig_cb_data, (char *)p, line_len);
+ p += line_len;
+ }
+
+ s->rhunk_active = 0;
+ strbuf_reset(&s->rhunk);
+}
+
+static void line_range_hunk_fn(void *data,
+ long old_begin, long old_nr UNUSED,
+ long new_begin, long new_nr UNUSED,
+ const char *func, long funclen)
+{
+ struct line_range_callback *s = data;
+
+ /*
+ * When count > 0, begin is 1-based. When count == 0, begin is
+ * adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of
+ * that type will arrive, so the value is unused.
+ *
+ * Any pending removal lines from the previous xdiff hunk are
+ * intentionally left in pending_rm: the line callback will
+ * flush or discard them when the next content line reveals
+ * whether the removals precede in-range content.
+ */
+ s->lno_post = new_begin;
+ s->lno_pre = old_begin;
+
+ if (funclen > 0) {
+ if (funclen > (long)sizeof(s->func))
+ funclen = sizeof(s->func);
+ memcpy(s->func, func, funclen);
+ }
+ s->funclen = funclen;
+}
+
+static int line_range_line_fn(void *priv, char *line, unsigned long len)
+{
+ struct line_range_callback *s = priv;
+ const struct range *cur;
+ long lno_0, cur_pre;
+
+ if (s->ret)
+ return s->ret;
+
+ if (line[0] == '-') {
+ if (!s->pending_rm_count)
+ s->pending_rm_pre_begin = s->lno_pre;
+ s->lno_pre++;
+ strbuf_add(&s->pending_rm, line, len);
+ s->pending_rm_count++;
+ return s->ret;
+ }
+
+ if (line[0] == '\\') {
+ if (s->pending_rm_count)
+ strbuf_add(&s->pending_rm, line, len);
+ else if (s->rhunk_active)
+ strbuf_add(&s->rhunk, line, len);
+ /* otherwise outside tracked range; drop silently */
+ return s->ret;
+ }
+
+ if (line[0] != '+' && line[0] != ' ')
+ BUG("unexpected diff line type '%c'", line[0]);
+
+ lno_0 = s->lno_post - 1;
+ cur_pre = s->lno_pre; /* save before advancing for context lines */
+ s->lno_post++;
+ if (line[0] == ' ')
+ s->lno_pre++;
+
+ /* Advance past ranges we've passed */
+ while (s->cur_range < s->ranges->nr &&
+ lno_0 >= s->ranges->ranges[s->cur_range].end) {
+ if (s->rhunk_active)
+ flush_rhunk(s);
+ discard_pending_rm(s);
+ s->cur_range++;
+ }
+
+ /* Past all ranges */
+ if (s->cur_range >= s->ranges->nr) {
+ discard_pending_rm(s);
+ return s->ret;
+ }
+
+ cur = &s->ranges->ranges[s->cur_range];
+
+ /* Before current range */
+ if (lno_0 < cur->start) {
+ discard_pending_rm(s);
+ return s->ret;
+ }
+
+ /* In range so start a new range hunk if needed */
+ if (!s->rhunk_active) {
+ s->rhunk_active = 1;
+ s->rhunk_has_changes = 0;
+ s->rhunk_new_begin = lno_0 + 1;
+ s->rhunk_old_begin = s->pending_rm_count
+ ? s->pending_rm_pre_begin : cur_pre;
+ s->rhunk_old_count = 0;
+ s->rhunk_new_count = 0;
+ strbuf_reset(&s->rhunk);
+ }
+
+ /* Flush pending removals into range hunk */
+ if (s->pending_rm_count) {
+ strbuf_addbuf(&s->rhunk, &s->pending_rm);
+ s->rhunk_old_count += s->pending_rm_count;
+ s->rhunk_has_changes = 1;
+ discard_pending_rm(s);
+ }
+
+ strbuf_add(&s->rhunk, line, len);
+ s->rhunk_new_count++;
+ if (line[0] == '+')
+ s->rhunk_has_changes = 1;
+ else
+ s->rhunk_old_count++;
+
+ return s->ret;
+}
+
static void pprint_rename(struct strbuf *name, const char *a, const char *b)
{
const char *old_name = a;
@@ -3592,7 +3820,8 @@ static void builtin_diff(const char *name_a,
const char *xfrm_msg,
int must_show_header,
struct diff_options *o,
- int complete_rewrite)
+ int complete_rewrite,
+ const struct range_set *line_ranges)
{
mmfile_t mf1, mf2;
const char *lbl[2];
@@ -3833,6 +4062,52 @@ static void builtin_diff(const char *name_a,
*/
xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
&ecbdata, &xpp, &xecfg);
+ } else if (line_ranges) {
+ struct line_range_callback lr_state;
+ unsigned int i;
+ long max_span = 0;
+
+ memset(&lr_state, 0, sizeof(lr_state));
+ lr_state.orig_line_fn = fn_out_consume;
+ lr_state.orig_cb_data = &ecbdata;
+ lr_state.ranges = line_ranges;
+ strbuf_init(&lr_state.rhunk, 0);
+ strbuf_init(&lr_state.pending_rm, 0);
+
+ /*
+ * Inflate ctxlen so that all changes within
+ * any single range are merged into one xdiff
+ * hunk and the inter-change context is emitted.
+ * The callback clips back to range boundaries.
+ *
+ * The optimal ctxlen depends on where changes
+ * fall within the range, which is only known
+ * after xdiff runs; the max range span is the
+ * upper bound that guarantees correctness in a
+ * single pass.
+ */
+ for (i = 0; i < line_ranges->nr; i++) {
+ long span = line_ranges->ranges[i].end -
+ line_ranges->ranges[i].start;
+ if (span > max_span)
+ max_span = span;
+ }
+ if (max_span > xecfg.ctxlen)
+ xecfg.ctxlen = max_span;
+
+ if (xdi_diff_outf(&mf1, &mf2,
+ line_range_hunk_fn,
+ line_range_line_fn,
+ &lr_state, &xpp, &xecfg))
+ die("unable to generate diff for %s",
+ one->path);
+
+ flush_rhunk(&lr_state);
+ if (lr_state.ret)
+ die("unable to generate diff for %s",
+ one->path);
+ strbuf_release(&lr_state.rhunk);
+ strbuf_release(&lr_state.pending_rm);
} else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
&ecbdata, &xpp, &xecfg))
die("unable to generate diff for %s", one->path);
@@ -4674,7 +4949,7 @@ static void run_diff_cmd(const struct external_diff *pgm,
builtin_diff(name, other ? other : name,
one, two, xfrm_msg, must_show_header,
- o, complete_rewrite);
+ o, complete_rewrite, p->line_ranges);
if (p->status == DIFF_STATUS_COPIED ||
p->status == DIFF_STATUS_RENAMED)
o->found_changes = 1;
diff --git a/diffcore.h b/diffcore.h
index 9c0a0e7aaf..d75038d1b3 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -19,6 +19,17 @@ struct userdiff_driver;
* in anything else.
*/
+/* A range [start, end). Lines are numbered starting at 0. */
+struct range {
+ long start, end;
+};
+
+/* A set of ranges. The ranges must always be disjoint and sorted. */
+struct range_set {
+ unsigned int alloc, nr;
+ struct range *ranges;
+};
+
/* We internally use unsigned short as the score value,
* and rely on an int capable to hold 32-bits. -B can take
* -Bmerge_score/break_score format and the two scores are
@@ -106,6 +117,11 @@ int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
struct diff_filepair {
struct diff_filespec *one;
struct diff_filespec *two;
+ /*
+ * Tracked line ranges for -L filtering; borrowed from
+ * line_log_data and must not be freed.
+ */
+ const struct range_set *line_ranges;
unsigned short int score;
char status; /* M C R A D U etc. (see Documentation/diff-format.adoc or DIFF_STATUS_* in diff.h) */
unsigned broken_pair : 1;
diff --git a/line-log.c b/line-log.c
index eeaf68454e..858a899cd2 100644
--- a/line-log.c
+++ b/line-log.c
@@ -858,173 +858,33 @@ static void queue_diffs(struct line_log_data *range,
diff_queue_clear(&diff_queued_diff);
diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
if (opt->detect_rename && diff_might_be_rename()) {
+ struct diff_options rename_opts;
+
+ /*
+ * Build a private diff_options for rename detection so
+ * that any user-specified options on the original opts
+ * (e.g. pickaxe) cannot discard diff pairs needed for
+ * rename tracking. Similar to blame's find_rename().
+ */
+ repo_diff_setup(opt->repo, &rename_opts);
+ rename_opts.flags.recursive = 1;
+ rename_opts.detect_rename = opt->detect_rename;
+ rename_opts.rename_score = opt->rename_score;
+ rename_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_setup_done(&rename_opts);
+
/* must look at the full tree diff to detect renames */
- clear_pathspec(&opt->pathspec);
diff_queue_clear(&diff_queued_diff);
-
- diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
+ diff_tree_oid(parent_tree_oid, tree_oid, "", &rename_opts);
filter_diffs_for_paths(range, 1);
- diffcore_std(opt);
+ diffcore_std(&rename_opts);
filter_diffs_for_paths(range, 0);
+ diff_free(&rename_opts);
}
move_diff_queue(queue, &diff_queued_diff);
}
-static char *get_nth_line(long line, unsigned long *ends, void *data)
-{
- if (line == 0)
- return (char *)data;
- else
- return (char *)data + ends[line] + 1;
-}
-
-static void print_line(const char *prefix, char first,
- long line, unsigned long *ends, void *data,
- const char *color, const char *reset, FILE *file)
-{
- char *begin = get_nth_line(line, ends, data);
- char *end = get_nth_line(line+1, ends, data);
- int had_nl = 0;
-
- if (end > begin && end[-1] == '\n') {
- end--;
- had_nl = 1;
- }
-
- fputs(prefix, file);
- fputs(color, file);
- putc(first, file);
- fwrite(begin, 1, end-begin, file);
- fputs(reset, file);
- putc('\n', file);
- if (!had_nl)
- fputs("\\ No newline at end of file\n", file);
-}
-
-static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
-{
- unsigned int i, j = 0;
- long p_lines, t_lines;
- unsigned long *p_ends = NULL, *t_ends = NULL;
- struct diff_filepair *pair = range->pair;
- struct diff_ranges *diff = &range->diff;
-
- struct diff_options *opt = &rev->diffopt;
- const char *prefix = diff_line_prefix(opt);
- const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
- const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
- const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
- const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
- const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
- const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
-
- if (!pair || !diff)
- goto out;
-
- if (pair->one->oid_valid)
- fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
- fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends);
-
- fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
- fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
- pair->one->oid_valid ? "a/" : "",
- pair->one->oid_valid ? pair->one->path : "/dev/null",
- c_reset);
- fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
- for (i = 0; i < range->ranges.nr; i++) {
- long p_start, p_end;
- long t_start = range->ranges.ranges[i].start;
- long t_end = range->ranges.ranges[i].end;
- long t_cur = t_start;
- unsigned int j_last;
-
- /*
- * If a diff range touches multiple line ranges, then all
- * those line ranges should be shown, so take a step back if
- * the current line range is still in the previous diff range
- * (even if only partially).
- */
- if (j > 0 && diff->target.ranges[j-1].end > t_start)
- j--;
-
- while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
- j++;
- if (j == diff->target.nr || diff->target.ranges[j].start >= t_end)
- continue;
-
- /* Scan ahead to determine the last diff that falls in this range */
- j_last = j;
- while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end)
- j_last++;
- if (j_last > j)
- j_last--;
-
- /*
- * Compute parent hunk headers: we know that the diff
- * has the correct line numbers (but not all hunks).
- * So it suffices to shift the start/end according to
- * the line numbers of the first/last hunk(s) that
- * fall in this range.
- */
- if (t_start < diff->target.ranges[j].start)
- p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start);
- else
- p_start = diff->parent.ranges[j].start;
- if (t_end > diff->target.ranges[j_last].end)
- p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end);
- else
- p_end = diff->parent.ranges[j_last].end;
-
- if (!p_start && !p_end) {
- p_start = -1;
- p_end = -1;
- }
-
- /* Now output a diff hunk for this range */
- fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
- prefix, c_frag,
- p_start+1, p_end-p_start, t_start+1, t_end-t_start,
- c_reset);
- while (j < diff->target.nr && diff->target.ranges[j].start < t_end) {
- int k;
- for (; t_cur < diff->target.ranges[j].start; t_cur++)
- print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
- c_context, c_reset, opt->file);
- for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
- print_line(prefix, '-', k, p_ends, pair->one->data,
- c_old, c_reset, opt->file);
- for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
- print_line(prefix, '+', t_cur, t_ends, pair->two->data,
- c_new, c_reset, opt->file);
- j++;
- }
- for (; t_cur < t_end; t_cur++)
- print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
- c_context, c_reset, opt->file);
- }
-
-out:
- free(p_ends);
- free(t_ends);
-}
-
-/*
- * NEEDSWORK: manually building a diff here is not the Right
- * Thing(tm). log -L should be built into the diff pipeline.
- */
-static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
-{
- const char *prefix = diff_line_prefix(&rev->diffopt);
-
- fprintf(rev->diffopt.file, "%s\n", prefix);
-
- while (range) {
- dump_diff_hacky_one(rev, range);
- range = range->next;
- }
-}
-
/*
* Unlike most other functions, this destructively operates on
* 'range'.
@@ -1088,7 +948,7 @@ static int process_diff_filepair(struct rev_info *rev,
static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
{
- struct diff_filepair *new_filepair = xmalloc(sizeof(struct diff_filepair));
+ struct diff_filepair *new_filepair = xcalloc(1, sizeof(struct diff_filepair));
new_filepair->one = pair->one;
new_filepair->two = pair->two;
new_filepair->one->count++;
@@ -1146,11 +1006,25 @@ static int process_all_files(struct line_log_data **range_out,
int line_log_print(struct rev_info *rev, struct commit *commit)
{
-
show_log(rev);
if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) {
struct line_log_data *range = lookup_line_range(rev, commit);
- dump_diff_hacky(rev, range);
+ struct line_log_data *r;
+ const char *prefix = diff_line_prefix(&rev->diffopt);
+
+ fprintf(rev->diffopt.file, "%s\n", prefix);
+
+ for (r = range; r; r = r->next) {
+ if (r->pair) {
+ struct diff_filepair *p =
+ diff_filepair_dup(r->pair);
+ p->line_ranges = &r->ranges;
+ diff_q(&diff_queued_diff, p);
+ }
+ }
+
+ diffcore_std(&rev->diffopt);
+ diff_flush(&rev->diffopt);
}
return 1;
}
diff --git a/line-log.h b/line-log.h
index e9dadbc1a5..04a6ea64d3 100644
--- a/line-log.h
+++ b/line-log.h
@@ -1,22 +1,12 @@
#ifndef LINE_LOG_H
#define LINE_LOG_H
+#include "diffcore.h" /* struct range, struct range_set */
+
struct rev_info;
struct commit;
struct string_list;
-/* A range [start,end]. Lines are numbered starting at 0, and the
- * ranges include start but exclude end. */
-struct range {
- long start, end;
-};
-
-/* A set of ranges. The ranges must always be disjoint and sorted. */
-struct range_set {
- unsigned int alloc, nr;
- struct range *ranges;
-};
-
/* A diff, encoded as the set of pre- and post-image ranges where the
* files differ. A pair of ranges corresponds to a hunk. */
struct diff_ranges {
diff --git a/revision.c b/revision.c
index fda405bf65..599b3a66c3 100644
--- a/revision.c
+++ b/revision.c
@@ -3129,6 +3129,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (want_ancestry(revs))
revs->limited = 1;
revs->topo_order = 1;
+ if (!revs->diffopt.output_format)
+ revs->diffopt.output_format = DIFF_FORMAT_PATCH;
}
if (revs->topo_order && !generation_numbers_enabled(the_repository))
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 0a7c3ca42f..aaf197d2ed 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -129,7 +129,7 @@ test_expect_success '-L with --output' '
git checkout parallel-change &&
git log --output=log -L :main:b.c >output &&
test_must_be_empty output &&
- test_line_count = 70 log
+ test_line_count = 75 log
'
test_expect_success 'range_set_union' '
@@ -339,14 +339,106 @@ test_expect_success 'zero-width regex .* matches any function name' '
test_cmp expect actual
'
+test_expect_success 'setup for diff pipeline tests' '
+ git checkout parent-oids &&
+
+ head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
+ head_blob_new=$(git rev-parse --short HEAD:file.c) &&
+ root_blob=$(git rev-parse --short HEAD~4:file.c) &&
+ null_blob=$(test_oid zero | cut -c1-7) &&
+ head_blob_old_full=$(git rev-parse HEAD^:file.c) &&
+ head_blob_new_full=$(git rev-parse HEAD:file.c) &&
+ root_blob_full=$(git rev-parse HEAD~4:file.c) &&
+ null_blob_full=$(test_oid zero)
+'
+
+test_expect_success '-L diff output includes index and new file mode' '
+ git log -L:func2:file.c --format= >actual &&
+
+ # Output should contain index headers (not present in old code path)
+ grep "^index $head_blob_old\.\.$head_blob_new 100644" actual &&
+
+ # Root commit should show new file mode and null index
+ grep "^new file mode 100644" actual &&
+ grep "^index $null_blob\.\.$root_blob$" actual &&
+
+ # Hunk headers should include funcname context
+ grep "^@@ .* @@ int func1()" actual
+'
+
+test_expect_success '-L with --word-diff' '
+ cat >expect <<-\EOF &&
+
+ diff --git a/file.c b/file.c
+ --- a/file.c
+ +++ b/file.c
+ @@ -6,4 +6,4 @@ int func1()
+ int func2()
+ {
+ return [-F2;-]{+F2 + 2;+}
+ }
+
+ diff --git a/file.c b/file.c
+ new file mode 100644
+ --- /dev/null
+ +++ b/file.c
+ @@ -0,0 +6,4 @@
+ {+int func2()+}
+ {+{+}
+ {+ return F2;+}
+ {+}+}
+ EOF
+ git log -L:func2:file.c --word-diff --format= >actual &&
+ grep -v "^index " actual >actual.filtered &&
+ grep -v "^index " expect >expect.filtered &&
+ test_cmp expect.filtered actual.filtered
+'
+
+test_expect_success '-L with --no-prefix' '
+ git log -L:func2:file.c --no-prefix --format= >actual &&
+ grep "^diff --git file.c file.c" actual &&
+ grep "^--- file.c" actual &&
+ ! grep "^--- a/" actual
+'
+
+test_expect_success '-L with --full-index' '
+ git log -L:func2:file.c --full-index --format= >actual &&
+ grep "^index $head_blob_old_full\.\.$head_blob_new_full 100644" actual &&
+ grep "^index $null_blob_full\.\.$root_blob_full$" actual
+'
+
+test_expect_success 'setup -L with whitespace change' '
+ git checkout -b ws-change parent-oids &&
+ sed "s/ return F2 + 2;/ return F2 + 2;/" file.c >tmp &&
+ mv tmp file.c &&
+ git commit -a -m "Whitespace change in func2()"
+'
+
+test_expect_success '-L with --ignore-all-space suppresses whitespace-only diff' '
+ git log -L:func2:file.c --format= >without_w &&
+ git log -L:func2:file.c --format= -w >with_w &&
+
+ # Without -w: three commits produce diffs (whitespace, modify, root)
+ test $(grep -c "^diff --git" without_w) = 3 &&
+
+ # With -w: whitespace-only commit produces no hunk, so only two diffs
+ test $(grep -c "^diff --git" with_w) = 2
+'
+
test_expect_success 'show line-log with graph' '
+ git checkout parent-oids &&
+ head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
+ head_blob_new=$(git rev-parse --short HEAD:file.c) &&
+ root_blob=$(git rev-parse --short HEAD~4:file.c) &&
+ null_blob=$(test_oid zero | cut -c1-7) &&
qz_to_tab_space >expect <<-EOF &&
* $head_oid Modify func2() in file.c
|Z
| diff --git a/file.c b/file.c
+ | index $head_blob_old..$head_blob_new 100644
| --- a/file.c
| +++ b/file.c
- | @@ -6,4 +6,4 @@
+ | @@ -6,4 +6,4 @@ int func1()
| int func2()
| {
| - return F2;
@@ -355,6 +447,8 @@ test_expect_success 'show line-log with graph' '
* $root_oid Add func1() and func2() in file.c
ZZ
diff --git a/file.c b/file.c
+ new file mode 100644
+ index $null_blob..$root_blob
--- /dev/null
+++ b/file.c
@@ -0,0 +6,4 @@
@@ -367,4 +461,254 @@ test_expect_success 'show line-log with graph' '
test_cmp expect actual
'
+test_expect_success 'setup for -L with -G/-S/--find-object and a merge with rename' '
+ git checkout --orphan pickaxe-rename &&
+ git reset --hard &&
+
+ echo content >file &&
+ git add file &&
+ git commit -m "add file" &&
+
+ git checkout -b pickaxe-rename-side &&
+ git mv file renamed-file &&
+ git commit -m "rename file" &&
+
+ git checkout pickaxe-rename &&
+ git commit --allow-empty -m "diverge" &&
+ git merge --no-edit pickaxe-rename-side &&
+
+ git mv renamed-file file &&
+ git commit -m "rename back"
+'
+
+test_expect_success '-L -G does not crash with merge and rename' '
+ git log --format="%s" --no-patch -L 1,1:file -G "." >actual
+'
+
+test_expect_success '-L -S does not crash with merge and rename' '
+ git log --format="%s" --no-patch -L 1,1:file -S content >actual
+'
+
+test_expect_success '-L --find-object does not crash with merge and rename' '
+ git log --format="%s" --no-patch -L 1,1:file \
+ --find-object=$(git rev-parse HEAD:file) >actual
+'
+
+# Commit-level filtering with pickaxe does not yet work for -L.
+# show_log() prints the commit header before diffcore_std() runs
+# pickaxe, so commits cannot be suppressed even when no diff pairs
+# survive filtering. Fixing this would require deferring show_log()
+# until after diffcore_std(), which is a larger restructuring of the
+# log-tree output pipeline.
+test_expect_failure '-L -G should filter commits by pattern' '
+ git log --format="%s" --no-patch -L 1,1:file -G "nomatch" >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_failure '-L -S should filter commits by pattern' '
+ git log --format="%s" --no-patch -L 1,1:file -S "nomatch" >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_failure '-L --find-object should filter commits by object' '
+ git log --format="%s" --no-patch -L 1,1:file \
+ --find-object=$ZERO_OID >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success '-L with --word-diff-regex' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c --word-diff \
+ --word-diff-regex="[a-zA-Z0-9_]+" --format= >actual &&
+ # Word-diff markers must be present
+ grep "{+" actual &&
+ grep "+}" actual &&
+ # No line-level +/- markers (word-diff replaces them);
+ # exclude --- header lines from the check
+ ! grep "^+[^+]" actual &&
+ ! grep "^-[^-]" actual
+'
+
+test_expect_success '-L with --src-prefix and --dst-prefix' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c --src-prefix=old/ --dst-prefix=new/ \
+ --format= >actual &&
+ grep "^diff --git old/file.c new/file.c" actual &&
+ grep "^--- old/file.c" actual &&
+ grep "^+++ new/file.c" actual &&
+ ! grep "^--- a/" actual
+'
+
+test_expect_success '-L with --abbrev' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c --abbrev=4 --format= -1 >actual &&
+ # 4-char abbreviated hashes on index line
+ grep "^index [0-9a-f]\{4\}\.\.[0-9a-f]\{4\}" actual
+'
+
+test_expect_success '-L with -b suppresses whitespace-only diff' '
+ git checkout ws-change &&
+ git log -L:func2:file.c --format= >without_b &&
+ git log -L:func2:file.c --format= -b >with_b &&
+ test $(grep -c "^diff --git" without_b) = 3 &&
+ test $(grep -c "^diff --git" with_b) = 2
+'
+
+test_expect_success '-L with --output-indicator-*' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c --output-indicator-new=">" \
+ --output-indicator-old="<" --output-indicator-context="|" \
+ --format= -1 >actual &&
+ grep "^>" actual &&
+ grep "^<" actual &&
+ grep "^|" actual &&
+ # No standard +/-/space content markers; exclude ---/+++ headers
+ ! grep "^+[^+]" actual &&
+ ! grep "^-[^-]" actual &&
+ ! grep "^ " actual
+'
+
+test_expect_success '-L with -R reverses diff' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c -R --format= -1 >actual &&
+ grep "^diff --git b/file.c a/file.c" actual &&
+ grep "^--- b/file.c" actual &&
+ grep "^+++ a/file.c" actual &&
+ # The modification added "F2 + 2", so reversed it is removed
+ grep "^-.*F2 + 2" actual &&
+ grep "^+.*return F2;" actual
+'
+
+test_expect_success 'setup for color-moved test' '
+ git checkout -b color-moved-test parent-oids &&
+ cat >big.c <<-\EOF &&
+ int bigfunc()
+ {
+ int a = 1;
+ int b = 2;
+ int c = 3;
+ return a + b + c;
+ }
+ EOF
+ git add big.c &&
+ git commit -m "add bigfunc" &&
+ sed "s/ / /" big.c >tmp && mv tmp big.c &&
+ git commit -a -m "reindent bigfunc"
+'
+
+test_expect_success '-L with --color-moved' '
+ git log -L:bigfunc:big.c --color-moved=zebra \
+ --color-moved-ws=ignore-all-space \
+ --color=always --format= -1 >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ # Old moved lines: bold magenta; new moved lines: bold cyan
+ grep "BOLD;MAGENTA" actual &&
+ grep "BOLD;CYAN" actual
+'
+
+test_expect_success 'setup for no-newline-at-eof tests' '
+ git checkout --orphan no-newline &&
+ git reset --hard &&
+ printf "int top()\n{\n return 1;\n}\n\nint bot()\n{\n return 2;\n}" >noeol.c &&
+ git add noeol.c &&
+ test_tick &&
+ git commit -m "add noeol.c (no trailing newline)" &&
+ sed "s/return 2/return 22/" noeol.c >tmp && mv tmp noeol.c &&
+ git commit -a -m "modify bot()" &&
+ printf "int top()\n{\n return 1;\n}\n\nint bot()\n{\n return 33;\n}\n" >noeol.c &&
+ git commit -a -m "modify bot() and add trailing newline"
+'
+
+# When the tracked function is at the end of a file with no trailing
+# newline, the "\ No newline at end of file" marker should appear.
+test_expect_success '-L no-newline-at-eof appears in tracked range' '
+ git log -L:bot:noeol.c --format= -1 HEAD~1 >actual &&
+ grep "No newline at end of file" actual
+'
+
+# When tracking a function that ends before the no-newline content,
+# the marker should not appear in the output.
+test_expect_success '-L no-newline-at-eof suppressed outside range' '
+ git log -L:top:noeol.c --format= >actual &&
+ ! grep "No newline at end of file" actual
+'
+
+# When a commit removes a no-newline last line and replaces it with
+# a newline-terminated line, the marker should still appear (on the
+# old side of the diff).
+test_expect_success '-L no-newline-at-eof marker with deleted line' '
+ git log -L:bot:noeol.c --format= -1 >actual &&
+ grep "No newline at end of file" actual
+'
+
+test_expect_success 'setup for range boundary deletion test' '
+ git checkout --orphan range-boundary &&
+ git reset --hard &&
+ cat >boundary.c <<-\EOF &&
+ void above()
+ {
+ return;
+ }
+
+ void tracked()
+ {
+ int x = 1;
+ int y = 2;
+ }
+
+ void below()
+ {
+ return;
+ }
+ EOF
+ git add boundary.c &&
+ test_tick &&
+ git commit -m "add boundary.c" &&
+ cat >boundary.c <<-\EOF &&
+ void above()
+ {
+ return;
+ }
+
+ void tracked()
+ {
+ int x = 1;
+ int y = 2;
+ }
+
+ void below_renamed()
+ {
+ return 0;
+ }
+ EOF
+ git commit -a -m "modify below() only"
+'
+
+# When only a function below the tracked range is modified, the
+# tracked function should not produce a diff.
+test_expect_success '-L suppresses deletions outside tracked range' '
+ git log -L:tracked:boundary.c --format= >actual &&
+ test $(grep -c "^diff --git" actual) = 1
+'
+
+test_expect_success '-L with -S filters to string-count changes' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c -S "F2 + 2" --format= >actual &&
+ # -S searches the whole file, not just the tracked range;
+ # combined with the -L range walk, this selects commits that
+ # both touch func2 and change the count of "F2 + 2" in the file.
+ test $(grep -c "^diff --git" actual) = 1 &&
+ grep "F2 + 2" actual
+'
+
+test_expect_success '-L with -G filters to diff-text matches' '
+ git checkout parent-oids &&
+ git log -L:func2:file.c -G "F2 [+] 2" --format= >actual &&
+ # -G greps the whole-file diff text, not just the tracked range;
+ # combined with -L, this selects commits that both touch func2
+ # and have "F2 + 2" in their diff.
+ test $(grep -c "^diff --git" actual) = 1 &&
+ grep "F2 + 2" actual
+'
+
test_done
diff --git a/t/t4211/sha1/expect.beginning-of-file b/t/t4211/sha1/expect.beginning-of-file
index 91b4054898..52c90afb3a 100644
--- a/t/t4211/sha1/expect.beginning-of-file
+++ b/t/t4211/sha1/expect.beginning-of-file
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:47:40 2013 +0100
change at very beginning
diff --git a/a.c b/a.c
+index bdb2bb1..5e709a1 100644
--- a/a.c
+++ b/a.c
@@ -1,3 +1,4 @@
@@ -20,6 +21,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -1,3 +1,3 @@
@@ -35,6 +37,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +1,3 @@
diff --git a/t/t4211/sha1/expect.end-of-file b/t/t4211/sha1/expect.end-of-file
index bd25bb2f59..c40036899a 100644
--- a/t/t4211/sha1/expect.end-of-file
+++ b/t/t4211/sha1/expect.end-of-file
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -20,3 +20,5 @@
+@@ -20,3 +20,5 @@ long f(long x)
printf("%ld\n", f(15));
return 0;
-}
@@ -23,9 +24,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -20,3 +20,3 @@
+@@ -20,3 +20,3 @@ int main ()
printf("%ld\n", f(15));
return 0;
-}
@@ -39,9 +41,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
-@@ -19,3 +19,3 @@
+@@ -19,3 +19,3 @@ int f(int x)
- printf("%d\n", f(15));
+ printf("%ld\n", f(15));
return 0;
@@ -54,6 +57,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +18,3 @@
diff --git a/t/t4211/sha1/expect.move-support-f b/t/t4211/sha1/expect.move-support-f
index c905e01bc2..ead6500d4d 100644
--- a/t/t4211/sha1/expect.move-support-f
+++ b/t/t4211/sha1/expect.move-support-f
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:49:50 2013 +0100
another simple change
diff --git a/b.c b/b.c
+index 5de3ea4..bf79c2f 100644
--- a/b.c
+++ b/b.c
@@ -4,9 +4,9 @@
@@ -26,6 +27,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -47,6 +49,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -67,6 +70,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.multiple b/t/t4211/sha1/expect.multiple
index 1eee8a7801..a41851a51d 100644
--- a/t/t4211/sha1/expect.multiple
+++ b/t/t4211/sha1/expect.multiple
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -25,9 +26,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -43,6 +45,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -71,6 +74,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -91,6 +95,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.multiple-overlapping b/t/t4211/sha1/expect.multiple-overlapping
index d930b6eec4..0ec9990eab 100644
--- a/t/t4211/sha1/expect.multiple-overlapping
+++ b/t/t4211/sha1/expect.multiple-overlapping
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,18 @@
diff --git a/t/t4211/sha1/expect.multiple-superset b/t/t4211/sha1/expect.multiple-superset
index d930b6eec4..0ec9990eab 100644
--- a/t/t4211/sha1/expect.multiple-superset
+++ b/t/t4211/sha1/expect.multiple-superset
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,18 @@
diff --git a/t/t4211/sha1/expect.no-assertion-error b/t/t4211/sha1/expect.no-assertion-error
index 994c37db1e..54c568f273 100644
--- a/t/t4211/sha1/expect.no-assertion-error
+++ b/t/t4211/sha1/expect.no-assertion-error
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:50:24 2013 +0100
move within the file
diff --git a/b.c b/b.c
+index bf79c2f..27c829c 100644
--- a/b.c
+++ b/b.c
@@ -25,0 +18,9 @@
@@ -25,9 +26,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -45,9 +47,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -63,9 +66,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
int main ()
{
- printf("%d\n", f(15));
@@ -80,6 +84,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.parallel-change-f-to-main b/t/t4211/sha1/expect.parallel-change-f-to-main
index 052def8074..65a8cc673a 100644
--- a/t/t4211/sha1/expect.parallel-change-f-to-main
+++ b/t/t4211/sha1/expect.parallel-change-f-to-main
@@ -13,6 +13,7 @@ Date: Thu Feb 28 10:49:50 2013 +0100
another simple change
diff --git a/b.c b/b.c
+index 5de3ea4..bf79c2f 100644
--- a/b.c
+++ b/b.c
@@ -4,14 +4,14 @@
@@ -39,6 +40,7 @@ Date: Fri Apr 12 16:15:57 2013 +0200
change on another line of history while rename happens
diff --git a/a.c b/a.c
+index 5de3ea4..01b5b65 100644
--- a/a.c
+++ b/a.c
@@ -4,14 +4,14 @@
@@ -65,6 +67,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -91,6 +94,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -117,6 +121,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,13 +3,14 @@
@@ -142,6 +147,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,13 @@
diff --git a/t/t4211/sha1/expect.simple-f b/t/t4211/sha1/expect.simple-f
index a1f5bc49c8..b24ae40e03 100644
--- a/t/t4211/sha1/expect.simple-f
+++ b/t/t4211/sha1/expect.simple-f
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -26,6 +27,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -46,6 +48,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.simple-f-to-main b/t/t4211/sha1/expect.simple-f-to-main
index a475768710..cd92100dfc 100644
--- a/t/t4211/sha1/expect.simple-f-to-main
+++ b/t/t4211/sha1/expect.simple-f-to-main
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -31,6 +32,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -57,6 +59,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,13 +3,14 @@
@@ -82,6 +85,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,13 @@
diff --git a/t/t4211/sha1/expect.simple-main b/t/t4211/sha1/expect.simple-main
index 39ce39bebe..ff31291d34 100644
--- a/t/t4211/sha1/expect.simple-main
+++ b/t/t4211/sha1/expect.simple-main
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -23,9 +24,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -41,9 +43,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
int main ()
{
- printf("%d\n", f(15));
@@ -58,6 +61,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.simple-main-to-end b/t/t4211/sha1/expect.simple-main-to-end
index 8480bd9cc4..4bef21e657 100644
--- a/t/t4211/sha1/expect.simple-main-to-end
+++ b/t/t4211/sha1/expect.simple-main-to-end
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -25,9 +26,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -43,9 +45,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
int main ()
{
- printf("%d\n", f(15));
@@ -60,6 +63,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.two-ranges b/t/t4211/sha1/expect.two-ranges
index c5164f3be3..aed01522e3 100644
--- a/t/t4211/sha1/expect.two-ranges
+++ b/t/t4211/sha1/expect.two-ranges
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -23,9 +24,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -41,6 +43,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -69,6 +72,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 444e415..3233403 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -89,6 +93,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.vanishes-early b/t/t4211/sha1/expect.vanishes-early
index 1f7cd06941..a413ad3659 100644
--- a/t/t4211/sha1/expect.vanishes-early
+++ b/t/t4211/sha1/expect.vanishes-early
@@ -5,11 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
--- a/a.c
+++ b/a.c
-@@ -22,1 +24,1 @@
--}
-\ No newline at end of file
+@@ -23,0 +24,1 @@ int main ()
+/* incomplete lines are bad! */
commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
@@ -19,9 +18,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
--- a/a.c
+++ b/a.c
-@@ -22,1 +22,1 @@
+@@ -22,1 +22,1 @@ int main ()
-}
+}
\ No newline at end of file
@@ -33,6 +33,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
--- /dev/null
+++ b/a.c
@@ -0,0 +20,1 @@
diff --git a/t/t4211/sha256/expect.beginning-of-file b/t/t4211/sha256/expect.beginning-of-file
index 5adfdfc1a1..e8d62328cf 100644
--- a/t/t4211/sha256/expect.beginning-of-file
+++ b/t/t4211/sha256/expect.beginning-of-file
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:47:40 2013 +0100
change at very beginning
diff --git a/a.c b/a.c
+index 3a78aaf..d325124 100644
--- a/a.c
+++ b/a.c
@@ -1,3 +1,4 @@
@@ -20,6 +21,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -1,3 +1,3 @@
@@ -35,6 +37,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +1,3 @@
diff --git a/t/t4211/sha256/expect.end-of-file b/t/t4211/sha256/expect.end-of-file
index 03ab5c1784..3b2e2384da 100644
--- a/t/t4211/sha256/expect.end-of-file
+++ b/t/t4211/sha256/expect.end-of-file
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -20,3 +20,5 @@
+@@ -20,3 +20,5 @@ long f(long x)
printf("%ld\n", f(15));
return 0;
-}
@@ -23,9 +24,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -20,3 +20,3 @@
+@@ -20,3 +20,3 @@ int main ()
printf("%ld\n", f(15));
return 0;
-}
@@ -39,9 +41,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
-@@ -19,3 +19,3 @@
+@@ -19,3 +19,3 @@ int f(int x)
- printf("%d\n", f(15));
+ printf("%ld\n", f(15));
return 0;
@@ -54,6 +57,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +18,3 @@
diff --git a/t/t4211/sha256/expect.move-support-f b/t/t4211/sha256/expect.move-support-f
index 223b4ed2a0..f49abcea3e 100644
--- a/t/t4211/sha256/expect.move-support-f
+++ b/t/t4211/sha256/expect.move-support-f
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:49:50 2013 +0100
another simple change
diff --git a/b.c b/b.c
+index 62c1fc2..69cb69c 100644
--- a/b.c
+++ b/b.c
@@ -4,9 +4,9 @@
@@ -26,6 +27,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -47,6 +49,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -67,6 +70,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.multiple b/t/t4211/sha256/expect.multiple
index dbd987b74a..0dee50ffb7 100644
--- a/t/t4211/sha256/expect.multiple
+++ b/t/t4211/sha256/expect.multiple
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -25,9 +26,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -43,6 +45,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -71,6 +74,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -91,6 +95,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.multiple-overlapping b/t/t4211/sha256/expect.multiple-overlapping
index 9015a45a25..b8c260e8ae 100644
--- a/t/t4211/sha256/expect.multiple-overlapping
+++ b/t/t4211/sha256/expect.multiple-overlapping
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,18 @@
diff --git a/t/t4211/sha256/expect.multiple-superset b/t/t4211/sha256/expect.multiple-superset
index 9015a45a25..b8c260e8ae 100644
--- a/t/t4211/sha256/expect.multiple-superset
+++ b/t/t4211/sha256/expect.multiple-superset
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
@@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,18 @@
diff --git a/t/t4211/sha256/expect.no-assertion-error b/t/t4211/sha256/expect.no-assertion-error
index 36ed12aa9c..c25f2ce19c 100644
--- a/t/t4211/sha256/expect.no-assertion-error
+++ b/t/t4211/sha256/expect.no-assertion-error
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:50:24 2013 +0100
move within the file
diff --git a/b.c b/b.c
+index 69cb69c..a0d566e 100644
--- a/b.c
+++ b/b.c
@@ -25,0 +18,9 @@
@@ -25,9 +26,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -45,9 +47,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -63,9 +66,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
int main ()
{
- printf("%d\n", f(15));
@@ -80,6 +84,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.parallel-change-f-to-main b/t/t4211/sha256/expect.parallel-change-f-to-main
index e68f8928ea..3178989253 100644
--- a/t/t4211/sha256/expect.parallel-change-f-to-main
+++ b/t/t4211/sha256/expect.parallel-change-f-to-main
@@ -13,6 +13,7 @@ Date: Thu Feb 28 10:49:50 2013 +0100
another simple change
diff --git a/b.c b/b.c
+index 62c1fc2..69cb69c 100644
--- a/b.c
+++ b/b.c
@@ -4,14 +4,14 @@
@@ -39,6 +40,7 @@ Date: Fri Apr 12 16:15:57 2013 +0200
change on another line of history while rename happens
diff --git a/a.c b/a.c
+index 62c1fc2..e1e8475 100644
--- a/a.c
+++ b/a.c
@@ -4,14 +4,14 @@
@@ -65,6 +67,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -91,6 +94,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -117,6 +121,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,13 +3,14 @@
@@ -142,6 +147,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,13 @@
diff --git a/t/t4211/sha256/expect.simple-f b/t/t4211/sha256/expect.simple-f
index 65508d7c0b..983c711fe3 100644
--- a/t/t4211/sha256/expect.simple-f
+++ b/t/t4211/sha256/expect.simple-f
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -26,6 +27,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -46,6 +48,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.simple-f-to-main b/t/t4211/sha256/expect.simple-f-to-main
index 77b721c196..e67fa017a7 100644
--- a/t/t4211/sha256/expect.simple-f-to-main
+++ b/t/t4211/sha256/expect.simple-f-to-main
@@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100
touch comment
diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -31,6 +32,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,14 +3,14 @@
@@ -57,6 +59,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,13 +3,14 @@
@@ -82,6 +85,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,13 @@
diff --git a/t/t4211/sha256/expect.simple-main b/t/t4211/sha256/expect.simple-main
index d20708c9f9..0792b27cad 100644
--- a/t/t4211/sha256/expect.simple-main
+++ b/t/t4211/sha256/expect.simple-main
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -23,9 +24,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -41,9 +43,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
int main ()
{
- printf("%d\n", f(15));
@@ -58,6 +61,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.simple-main-to-end b/t/t4211/sha256/expect.simple-main-to-end
index 617cdf3481..d3bd7c7bc6 100644
--- a/t/t4211/sha256/expect.simple-main-to-end
+++ b/t/t4211/sha256/expect.simple-main-to-end
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -25,9 +26,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -43,9 +45,10 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
int main ()
{
- printf("%d\n", f(15));
@@ -60,6 +63,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.two-ranges b/t/t4211/sha256/expect.two-ranges
index 6a94d3b9cb..7735b19723 100644
--- a/t/t4211/sha256/expect.two-ranges
+++ b/t/t4211/sha256/expect.two-ranges
@@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -23,9 +24,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
int main ()
{
printf("%ld\n", f(15));
@@ -41,6 +43,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100
touch both functions
diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
--- a/a.c
+++ b/a.c
@@ -3,9 +3,9 @@
@@ -69,6 +72,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100
change f()
diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
--- a/a.c
+++ b/a.c
@@ -3,8 +3,9 @@
@@ -89,6 +93,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.vanishes-early b/t/t4211/sha256/expect.vanishes-early
index 11ec9bdecf..bc33b963dc 100644
--- a/t/t4211/sha256/expect.vanishes-early
+++ b/t/t4211/sha256/expect.vanishes-early
@@ -5,11 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100
change back to complete line
diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
--- a/a.c
+++ b/a.c
-@@ -22,1 +24,1 @@
--}
-\ No newline at end of file
+@@ -23,0 +24,1 @@ int main ()
+/* incomplete lines are bad! */
commit 29f32ac3141c48b22803e5c4127b719917b67d0f8ca8c5248bebfa2a19f7da10
@@ -19,9 +18,10 @@ Date: Thu Feb 28 10:48:10 2013 +0100
change to an incomplete line at end
diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
--- a/a.c
+++ b/a.c
-@@ -22,1 +22,1 @@
+@@ -22,1 +22,1 @@ int main ()
-}
+}
\ No newline at end of file
@@ -33,6 +33,8 @@ Date: Thu Feb 28 10:44:48 2013 +0100
initial
diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
--- /dev/null
+++ b/a.c
@@ -0,0 +20,1 @@