Merge branch 'by/line-log' into next

* by/line-log:
  Document line history browser
  Add tests for line history browser
  Add --full-line-diff option
  Add --graph prefix before line history output
  Add parent rewriting to line history browser
  Make graph_next_line external to other part of git
  Make rewrite_parents public to other part of git
  Hook line history into cmd_log, ensuring a topo-ordered walk
  Print the line log
  map/take range to the parent of commits
  Add range clone functions
  Export three functions from diff.c
  Parse the -L options
  Refactor parse_loc
  Add the basic data structure for line level history
  parse-options: add two helper functions
  parse-options: enhance STOP_AT_NON_OPTION

Conflicts:
	builtin/log.c
This commit is contained in:
Junio C Hamano
2010-08-26 16:43:36 -07:00
16 changed files with 2789 additions and 116 deletions

View File

@@ -13,24 +13,7 @@
Annotate only the given line range. <start> and <end> can take
one of these forms:
- number
+
If <start> or <end> is a number, it specifies an
absolute line number (lines count from 1).
+
- /regex/
+
This form will use the first line matching the given
POSIX regex. 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>.
+
include::line-range-format.txt[]
-l::
Show long rev (Default: off).

View File

@@ -9,6 +9,7 @@ git-log - Show commit logs
SYNOPSIS
--------
'git log' [<options>] [<since>..<until>] [[\--] <path>...]
'git log' [<options>] -L n,m <path>
DESCRIPTION
-----------
@@ -19,6 +20,9 @@ command to control what is shown and how, and options applicable to
the 'git diff-*' commands to control how the changes
each commit introduces are shown.
With '-L' option, the command will help to trace the history of user specified
line ranges. It can trace multiple ranges coming from multiple files.
OPTIONS
-------
@@ -66,6 +70,17 @@ produced by --stat etc.
Note that only message is considered, if also a diff is shown
its size is not included.
-L <start>,<end>::
The line range. <start> and <end> can take one of these forms:
include::line-range-format.txt[]
You can also specify this option more than once before each path.
--full-line-diff::
Always print the interesting range even if the current commit
does not change any line of the range.
[\--] <path>...::
Show only commits that affect any of the specified paths. To
prevent confusion with options and branch names, paths may need

View File

@@ -0,0 +1,18 @@
- number
+
If <start> or <end> is a number, it specifies an
absolute line number (lines count from 1).
+
- /regex/
+
This form will use the first line matching the given
POSIX regex. 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>.
+

View File

@@ -518,6 +518,7 @@ LIB_H += grep.h
LIB_H += hash.h
LIB_H += help.h
LIB_H += levenshtein.h
LIB_H += line.h
LIB_H += list-objects.h
LIB_H += ll-merge.h
LIB_H += log-tree.h
@@ -608,6 +609,7 @@ LIB_OBJS += help.o
LIB_OBJS += hex.o
LIB_OBJS += ident.o
LIB_OBJS += levenshtein.o
LIB_OBJS += line.o
LIB_OBJS += list-objects.o
LIB_OBJS += ll-merge.o
LIB_OBJS += lockfile.o

View File

@@ -21,6 +21,7 @@
#include "parse-options.h"
#include "utf8.h"
#include "userdiff.h"
#include "line.h"
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
@@ -541,11 +542,16 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
dst->score = 0;
}
static const char *nth_line(struct scoreboard *sb, int lno)
static const char *nth_line(struct scoreboard *sb, long lno)
{
return sb->final_buf + sb->lineno[lno];
}
static const char *nth_line_cb(void *data, long lno)
{
return nth_line((struct scoreboard *)data, lno);
}
/*
* It is known that lines between tlno to same came from parent, and e
* has an overlap with that range. it also is known that parent's
@@ -1898,83 +1904,6 @@ static const char *add_prefix(const char *prefix, const char *path)
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
}
/*
* Parsing of (comma separated) one item in the -L option
*/
static const char *parse_loc(const char *spec,
struct scoreboard *sb, long lno,
long begin, long *ret)
{
char *term;
const char *line;
long num;
int reg_error;
regex_t regexp;
regmatch_t match[1];
/* Allow "-L <something>,+20" to mean starting at <something>
* for 20 lines, or "-L <something>,-5" for 5 lines ending at
* <something>.
*/
if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
num = strtol(spec + 1, &term, 10);
if (term != spec + 1) {
if (spec[0] == '-')
num = 0 - num;
if (0 < num)
*ret = begin + num - 2;
else if (!num)
*ret = begin;
else
*ret = begin + num;
return term;
}
return spec;
}
num = strtol(spec, &term, 10);
if (term != spec) {
*ret = num;
return term;
}
if (spec[0] != '/')
return spec;
/* it could be a regexp of form /.../ */
for (term = (char *) spec + 1; *term && *term != '/'; term++) {
if (*term == '\\')
term++;
}
if (*term != '/')
return spec;
/* try [spec+1 .. term-1] as regexp */
*term = 0;
begin--; /* input is in human terms */
line = nth_line(sb, begin);
if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
!(reg_error = regexec(&regexp, line, 1, match, 0))) {
const char *cp = line + match[0].rm_so;
const char *nline;
while (begin++ < lno) {
nline = nth_line(sb, begin);
if (line <= cp && cp < nline)
break;
line = nline;
}
*ret = begin;
regfree(&regexp);
*term++ = '/';
return term;
}
else {
char errbuf[1024];
regerror(reg_error, &regexp, errbuf, 1024);
die("-L parameter '%s': %s", spec + 1, errbuf);
}
}
/*
* Parsing of -L option
*/
@@ -1985,9 +1914,9 @@ static void prepare_blame_range(struct scoreboard *sb,
{
const char *term;
term = parse_loc(bottomtop, sb, lno, 1, bottom);
term = parse_loc(bottomtop, nth_line_cb, sb, lno, 1, bottom);
if (*term == ',') {
term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
term = parse_loc(term + 1, nth_line_cb, sb, lno, *bottom + 1, top);
if (*term)
usage(blame_usage);
}

View File

@@ -19,6 +19,7 @@
#include "remote.h"
#include "string-list.h"
#include "parse-options.h"
#include "line.h"
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -27,11 +28,24 @@ static int default_show_root = 1;
static int decoration_style;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
static const char *dashdash = "--";
static const char * const builtin_log_usage =
static char builtin_log_usage[] =
"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
"git log [<options>] -L n,m <path>\n"
" or: git show [options] <object>...";
static const char *log_opt_usage[] = {
builtin_log_usage,
NULL
};
struct line_opt_callback_data {
struct diff_line_range **range;
struct parse_opt_ctx_t *ctx;
struct rev_info *rev;
};
static int parse_decoration_style(const char *var, const char *value)
{
switch (git_config_maybe_bool(var, value)) {
@@ -49,12 +63,48 @@ static int parse_decoration_style(const char *var, const char *value)
return -1;
}
static int log_line_range_callback(const struct option *option, const char *arg, int unset)
{
struct line_opt_callback_data *data = option->value;
struct diff_line_range *r = *data->range;
struct parse_opt_ctx_t *ctx = data->ctx;
if (!arg)
return -1;
if (r->nr == 0 && r->next == NULL)
ctx->out[ctx->cpidx++] = dashdash;
diff_line_range_append(r, arg);
data->rev->line_level_traverse = 1;
return 0;
}
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
int i;
int decoration_given = 0;
static int full_line_diff;
struct userformat_want w;
const char *path = NULL, *fullpath = NULL;
static struct diff_line_range *range;
struct diff_line_range *r = NULL;
static struct parse_opt_ctx_t ctx;
static struct line_opt_callback_data line_cb = {&range, &ctx, NULL};
static const struct option options[] = {
OPT_CALLBACK('L', NULL, &line_cb, "n,m",
"Process only line range n,m, counting from 1",
log_line_range_callback),
OPT_BOOLEAN(0, "full-line-diff", &full_line_diff,
"Always print the interesting range even if the \
current commit does not change any line of it"),
OPT_END()
};
line_cb.rev = rev;
range = xmalloc(sizeof(*range));
DIFF_LINE_RANGE_INIT(range);
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
@@ -75,6 +125,56 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
*/
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_log_usage);
parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_STOP_AT_NON_OPTION);
for (;;) {
switch (parse_options_step(&ctx, options, log_opt_usage)) {
case PARSE_OPT_HELP:
exit(129);
case PARSE_OPT_DONE:
goto parse_done;
case PARSE_OPT_NON_OPTION:
path = parse_options_current(&ctx);
fullpath = prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
range->spec = alloc_filespec(fullpath);
free((void *)fullpath);
if (range->nr == 0) {
if (range->next) {
die("Path %s need a -L <range> option\n"
"If you want follow the history of the whole file "
"use 'git log -L 1,$ <path>'", range->spec->path);
} else {
parse_options_next(&ctx, 1);
continue;
}
}
r = xmalloc(sizeof(*r));
DIFF_LINE_RANGE_INIT(r);
r->next = range;
range = r;
parse_options_next(&ctx, 1);
continue;
case PARSE_OPT_UNKNOWN:
parse_options_next(&ctx, 1);
continue;
}
parse_revision_opt(rev, &ctx, options, log_opt_usage);
}
parse_done:
argc = parse_options_end(&ctx);
/* die if '-L <range>' with no pathspec follow */
if (range->nr > 0 && range->spec == NULL)
die("Each -L should follow a path");
/* clear up the last range */
if (range->nr == 0) {
struct diff_line_range *r = range->next;
DIFF_LINE_RANGE_CLEAR(range);
range = r;
}
argc = setup_revisions(argc, argv, rev, opt);
memset(&w, 0, sizeof(w));
@@ -125,6 +225,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
}
/* Test whether line level history is asked for */
if (range && range->nr > 0) {
setup_line(rev, range);
rev->full_line_diff = full_line_diff;
}
setup_pager();
}
@@ -510,7 +616,10 @@ int cmd_log(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
cmd_log_init(argc, argv, prefix, &rev, &opt);
return cmd_log_walk(&rev);
if (rev.line_level_traverse)
return cmd_line_log_walk(&rev);
else
return cmd_log_walk(&rev);
}
/* format-patch */

6
diff.c
View File

@@ -151,7 +151,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
return git_color_default_config(var, value, cb);
}
static char *quote_two(const char *one, const char *two)
char *quote_two(const char *one, const char *two)
{
int need_one = quote_c_style(one, NULL, NULL, 1);
int need_two = quote_c_style(two, NULL, NULL, 1);
@@ -332,7 +332,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
fputc('\n', file);
}
static void emit_line(struct diff_options *o, const char *set, const char *reset,
void emit_line(struct diff_options *o, const char *set, const char *reset,
const char *line, int len)
{
emit_line_0(o, set, reset, line[0], line+1, len-1);
@@ -2571,7 +2571,7 @@ static int similarity_index(struct diff_filepair *p)
return p->score * 100 / MAX_SCORE;
}
static void fill_metainfo(struct strbuf *msg,
void fill_metainfo(struct strbuf *msg,
const char *name,
const char *other,
struct diff_filespec *one,

17
diff.h
View File

@@ -12,6 +12,7 @@ struct diff_queue_struct;
struct strbuf;
struct diff_filespec;
struct userdiff_driver;
struct diff_filepair;
typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
@@ -312,4 +313,20 @@ extern size_t fill_textconv(struct userdiff_driver *driver,
extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
/* some output functions line.c need */
extern void fill_metainfo(struct strbuf *msg,
const char *name,
const char *other,
struct diff_filespec *one,
struct diff_filespec *two,
struct diff_options *o,
struct diff_filepair *p,
int *must_show_header,
int use_color);
extern void emit_line(struct diff_options *o, const char *set, const char *reset,
const char *line, int len);
extern char *quote_two(const char *one, const char *two);
#endif /* DIFF_H */

1549
line.c Normal file

File diff suppressed because it is too large Load Diff

141
line.h Normal file
View File

@@ -0,0 +1,141 @@
#ifndef LINE_H
#define LINE_H
#include "diffcore.h"
struct rev_info;
struct commit;
struct diff_line_range;
struct diff_options;
typedef const char *(*nth_line_fn_t)(void *data, long lno);
struct print_range {
int start, end; /* Line range of post-image */
int pstart, pend; /* Line range of pre-image */
int line_added : 1; /* whether this range is added */
};
struct print_pair {
int alloc, nr;
struct print_range *ranges;
};
#define PRINT_RANGE_INIT(r) \
do { \
(r)->start = (r)->end = 0; \
(r)->pstart = (r)->pend = 0; \
(r)->line_added = 0; \
} while (0)
#define PRINT_PAIR_INIT(p) \
do { \
(p)->alloc = (p)->nr = 0; \
(p)->ranges = NULL; \
} while (0)
#define PRINT_PAIR_GROW(p) \
do { \
(p)->nr++; \
ALLOC_GROW((p)->ranges, (p)->nr, (p)->alloc); \
} while (0)
#define PRINT_PAIR_CLEAR(p) \
do { \
(p)->alloc = (p)->nr = 0; \
if ((p)->ranges) \
free((p)->ranges); \
(p)->ranges = NULL; \
} while (0)
struct line_range {
const char *arg; /* The argument to specify this line range */
long start, end; /* The interesting line range of current commit */
long pstart, pend; /* The corresponding range of parent commit */
struct print_pair pair;
/* The changed lines inside this range */
unsigned int diff:1;
};
struct diff_line_range {
struct diff_filespec *prev;
struct diff_filespec *spec;
char status;
int alloc;
int nr;
struct line_range *ranges;
unsigned int touch:1,
diff:1;
struct diff_line_range *next;
};
#define RANGE_INIT(r) \
do { \
(r)->arg = NULL; \
(r)->start = (r)->end = 0; \
(r)->pstart = (r)->pend = 0; \
PRINT_PAIR_INIT(&((r)->pair)); \
(r)->diff = 0; \
} while (0)
#define RANGE_CLEAR(r) \
do { \
(r)->arg = NULL; \
(r)->start = (r)->end = 0; \
(r)->pstart = (r)->pend = 0; \
PRINT_PAIR_CLEAR(&r->pair); \
(r)->diff = 0; \
} while (0)
#define DIFF_LINE_RANGE_INIT(r) \
do { \
(r)->prev = (r)->spec = NULL; \
(r)->status = '\0'; \
(r)->alloc = (r)->nr = 0; \
(r)->ranges = NULL; \
(r)->next = NULL; \
(r)->touch = 0; \
(r)->diff = 0; \
} while (0)
#define DIFF_LINE_RANGE_GROW(r) \
do { \
(r)->nr++; \
ALLOC_GROW((r)->ranges, (r)->nr, (r)->alloc); \
RANGE_INIT(((r)->ranges + (r)->nr - 1)); \
} while (0)
#define DIFF_LINE_RANGE_CLEAR(r) \
diff_line_range_clear((r));
extern struct line_range *diff_line_range_insert(struct diff_line_range *r,
const char *arg, int start, int end);
extern void diff_line_range_append(struct diff_line_range *r, const char *arg);
extern void diff_line_range_clear(struct diff_line_range *r);
extern struct diff_line_range *diff_line_range_merge(
struct diff_line_range *out,
struct diff_line_range *other);
extern struct diff_line_range *diff_line_range_clone(struct diff_line_range *r);
extern struct diff_line_range *diff_line_range_clone_deeply(struct diff_line_range *r);
extern void setup_line(struct rev_info *rev, struct diff_line_range *r);
extern void add_line_range(struct rev_info *revs, struct commit *commit,
struct diff_line_range *r);
extern struct diff_line_range *lookup_line_range(struct rev_info *revs,
struct commit *commit);
const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
void *data, long lines, long begin, long *ret);
extern int cmd_line_log_walk(struct rev_info *rev);
extern void limit_list_line(struct rev_info *rev);
#endif

View File

@@ -374,7 +374,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
if (parse_nodash_opt(ctx, arg, options) == 0)
continue;
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break;
return PARSE_OPT_NON_OPTION;
ctx->out[ctx->cpidx++] = ctx->argv[0];
continue;
}
@@ -439,6 +439,25 @@ unknown:
return PARSE_OPT_DONE;
}
const char *parse_options_current(struct parse_opt_ctx_t *ctx)
{
return ctx->argv[0];
}
int parse_options_next(struct parse_opt_ctx_t *ctx, int keep)
{
if (ctx->argc <= 0)
return -1;
if (keep)
ctx->out[ctx->cpidx++] = ctx->argv[0];
ctx->argc--;
ctx->argv++;
return 0;
}
int parse_options_end(struct parse_opt_ctx_t *ctx)
{
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
@@ -456,6 +475,7 @@ int parse_options(int argc, const char **argv, const char *prefix,
switch (parse_options_step(&ctx, options, usagestr)) {
case PARSE_OPT_HELP:
exit(129);
case PARSE_OPT_NON_OPTION:
case PARSE_OPT_DONE:
break;
default: /* PARSE_OPT_UNKNOWN */

View File

@@ -161,7 +161,8 @@ extern NORETURN void usage_msg_opt(const char *msg,
enum {
PARSE_OPT_HELP = -1,
PARSE_OPT_DONE,
PARSE_OPT_UNKNOWN
PARSE_OPT_NON_OPTION,
PARSE_OPT_UNKNOWN,
};
/*
@@ -186,6 +187,10 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,
const struct option *options,
const char * const usagestr[]);
extern const char *parse_options_current(struct parse_opt_ctx_t *ctx);
extern int parse_options_next(struct parse_opt_ctx_t *ctx, int keep);
extern int parse_options_end(struct parse_opt_ctx_t *ctx);
extern int parse_options_concat(struct option *dst, size_t, struct option *src);

View File

@@ -13,6 +13,7 @@
#include "decorate.h"
#include "log-tree.h"
#include "string-list.h"
#include "line.h"
volatile show_early_output_fn_t show_early_output;
@@ -1662,6 +1663,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (revs->combine_merges)
revs->ignore_merges = 0;
revs->diffopt.abbrev = revs->abbrev;
if (revs->line_level_traverse) {
revs->limited = 1;
revs->topo_order = 1;
}
if (diff_setup_done(&revs->diffopt) < 0)
die("diff_setup_done failed");
@@ -1905,6 +1912,11 @@ int prepare_revision_walk(struct rev_info *revs)
return -1;
if (revs->topo_order)
sort_in_topological_order(&revs->commits, revs->lifo);
if (revs->full_line_diff)
revs->dense = 0;
if (revs->rewrite_parents && revs->line_level_traverse
&& !revs->full_line_diff)
limit_list_line(revs);
if (revs->simplify_merges)
simplify_merges(revs);
if (revs->children.name)
@@ -1912,12 +1924,6 @@ int prepare_revision_walk(struct rev_info *revs)
return 0;
}
enum rewrite_result {
rewrite_one_ok,
rewrite_one_noparents,
rewrite_one_error
};
static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
{
struct commit_list *cache = NULL;
@@ -1939,12 +1945,13 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
}
}
static int rewrite_parents(struct rev_info *revs, struct commit *commit)
int rewrite_parents(struct rev_info *revs, struct commit *commit,
rewrite_parent_fn_t rewrite_parent)
{
struct commit_list **pp = &commit->parents;
while (*pp) {
struct commit_list *parent = *pp;
switch (rewrite_one(revs, &parent->item)) {
switch (rewrite_parent(revs, &parent->item)) {
case rewrite_one_ok:
break;
case rewrite_one_noparents:
@@ -2012,7 +2019,7 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
if (action == commit_show &&
!revs->show_all &&
revs->prune && revs->dense && want_ancestry(revs)) {
if (rewrite_parents(revs, commit) < 0)
if (rewrite_parents(revs, commit, rewrite_one) < 0)
return commit_error;
}
return action;

View File

@@ -14,7 +14,11 @@
#define CHILD_SHOWN (1u<<6)
#define ADDED (1u<<7) /* Parents already parsed and added? */
#define SYMMETRIC_LEFT (1u<<8)
#define ALL_REV_FLAGS ((1u<<9)-1)
#define RANGE_UPDATE (1u<<9) /* for line level traverse */
#define NEED_PRINT (1u<<10)
#define NONTRIVIAL_MERGE (1u<<11)
#define EVIL_MERGE (1u<<12)
#define ALL_REV_FLAGS ((1u<<13)-1)
#define DECORATE_SHORT_REFS 1
#define DECORATE_FULL_REFS 2
@@ -68,7 +72,9 @@ struct rev_info {
cherry_pick:1,
bisect:1,
ancestry_path:1,
first_parent_only:1;
first_parent_only:1,
line_level_traverse:1,
full_line_diff:1;
/* Diff flags */
unsigned int diff:1,
@@ -137,6 +143,9 @@ struct rev_info {
/* commit counts */
int count_left;
int count_right;
/* line level range that we are chasing */
struct decoration line_range;
struct decoration nontrivial_merge;
};
#define REV_TREE_SAME 0
@@ -193,4 +202,14 @@ enum commit_action {
extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
enum rewrite_result {
rewrite_one_ok,
rewrite_one_noparents,
rewrite_one_error
};
typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct commit **pp);
extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
rewrite_parent_fn_t rewrite_parent);
#endif

View File

@@ -0,0 +1,685 @@
#!/bin/sh
#
# Copyright (c) 2010 Bo Yang
#
test_description='Test git log -L with single line of history'
. ./test-lib.sh
. "$TEST_DIRECTORY"/diff-lib.sh
cat >path0 <<\EOF
void func()
{
int a = 0;
int b = 1;
int c;
c = a + b;
}
EOF
cat >path1 <<\EOF
void output()
{
printf("hello world");
}
EOF
test_expect_success 'add path0/path1 and commit.' '
git add path0 path1 &&
git commit -m "Base commit"
'
cat >path0 <<\EOF
void func()
{
int a = 10;
int b = 11;
int c;
c = a + b;
}
EOF
cat >path1 <<\EOF
void output()
{
const char *str = "hello world!";
printf("%s", str);
}
EOF
test_expect_success 'Change the 2,3 lines of path0 and path1.' '
git add path0 path1 &&
git commit -m "Change 2,3 lines of path0 and path1"
'
cat >path0 <<\EOF
void func()
{
int a = 10;
int b = 11;
int c;
c = 10 * (a + b);
}
EOF
test_expect_success 'Change the 5th line of path0.' '
git add path0 &&
git commit -m "Change the 5th line of path0"
'
cat >path0 <<\EOF
void func()
{
int a = 10;
int b = 11;
printf("%d", a - b);
}
EOF
test_expect_success 'Final change of path0.' '
git add path0 &&
git commit -m "Final change of path0"
'
cat >expected-path0 <<\EOF
Final change of path0
diff --git a/path0 b/path0
index ccdf243..ccf8bcf 100644
--- a/path0
+++ b/path0
@@ -1,7 +1,6 @@
void func()
{
int a = 10;
int b = 11;
- int c;
- c = 10 * (a + b);
+ printf("%d", a - b);
}
Change the 5th line of path0
diff --git a/path0 b/path0
index b0eb888..ccdf243 100644
--- a/path0
+++ b/path0
@@ -1,7 +1,7 @@
void func()
{
int a = 10;
int b = 11;
int c;
- c = a + b;
+ c = 10 * (a + b);
}
Change 2,3 lines of path0 and path1
diff --git a/path0 b/path0
index fb33939..b0eb888 100644
--- a/path0
+++ b/path0
@@ -1,7 +1,7 @@
void func()
{
- int a = 0;
- int b = 1;
+ int a = 10;
+ int b = 11;
int c;
c = a + b;
}
Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +1,7 @@
+void func()
+{
+ int a = 0;
+ int b = 1;
+ int c;
+ c = a + b;
+}
EOF
cat >expected-path1 <<\EOF
Change 2,3 lines of path0 and path1
diff --git a/path1 b/path1
index 52be2a5..cc54b12 100644
--- a/path1
+++ b/path1
@@ -1,4 +1,5 @@
void output()
{
- printf("hello world");
+ const char *str = "hello world!";
+ printf("%s", str);
}
Base commit
diff --git a/path1 b/path1
new file mode 100644
index 0000000..52be2a5
--- /dev/null
+++ b/path1
@@ -0,0 +1,4 @@
+void output()
+{
+ printf("hello world");
+}
EOF
cat >expected-pathall <<\EOF
Final change of path0
diff --git a/path0 b/path0
index ccdf243..ccf8bcf 100644
--- a/path0
+++ b/path0
@@ -1,7 +1,6 @@
void func()
{
int a = 10;
int b = 11;
- int c;
- c = 10 * (a + b);
+ printf("%d", a - b);
}
Change the 5th line of path0
diff --git a/path0 b/path0
index b0eb888..ccdf243 100644
--- a/path0
+++ b/path0
@@ -1,7 +1,7 @@
void func()
{
int a = 10;
int b = 11;
int c;
- c = a + b;
+ c = 10 * (a + b);
}
Change 2,3 lines of path0 and path1
diff --git a/path0 b/path0
index fb33939..b0eb888 100644
--- a/path0
+++ b/path0
@@ -1,7 +1,7 @@
void func()
{
- int a = 0;
- int b = 1;
+ int a = 10;
+ int b = 11;
int c;
c = a + b;
}
diff --git a/path1 b/path1
index 52be2a5..cc54b12 100644
--- a/path1
+++ b/path1
@@ -1,4 +1,5 @@
void output()
{
- printf("hello world");
+ const char *str = "hello world!";
+ printf("%s", str);
}
Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +1,7 @@
+void func()
+{
+ int a = 0;
+ int b = 1;
+ int c;
+ c = a + b;
+}
diff --git a/path1 b/path1
new file mode 100644
index 0000000..52be2a5
--- /dev/null
+++ b/path1
@@ -0,0 +1,4 @@
+void output()
+{
+ printf("hello world");
+}
EOF
cat >expected-linenum <<\EOF
Change 2,3 lines of path0 and path1
diff --git a/path0 b/path0
index fb33939..b0eb888 100644
--- a/path0
+++ b/path0
@@ -2,3 +2,3 @@
{
- int a = 0;
- int b = 1;
+ int a = 10;
+ int b = 11;
Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +2,3 @@
+{
+ int a = 0;
+ int b = 1;
EOF
cat >expected-always <<\EOF
Final change of path0
diff --git a/path0 b/path0
index ccdf243..ccf8bcf 100644
--- a/path0
+++ b/path0
@@ -2,3 +2,3 @@
{
int a = 10;
int b = 11;
Change the 5th line of path0
diff --git a/path0 b/path0
index b0eb888..ccdf243 100644
--- a/path0
+++ b/path0
@@ -2,3 +2,3 @@
{
int a = 10;
int b = 11;
Change 2,3 lines of path0 and path1
diff --git a/path0 b/path0
index fb33939..b0eb888 100644
--- a/path0
+++ b/path0
@@ -2,3 +2,3 @@
{
- int a = 0;
- int b = 1;
+ int a = 10;
+ int b = 11;
Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +2,3 @@
+{
+ int a = 0;
+ int b = 1;
EOF
test_expect_success 'Show the line level log of path0' '
git log --pretty=format:%s%n%b -L /func/,/^}/ path0 > current-path0
'
test_expect_success 'validate the path0 output.' '
test_cmp current-path0 expected-path0
'
test_expect_success 'Show the line level log of path1' '
git log --pretty=format:%s%n%b -L /output/,/^}/ path1 > current-path1
'
test_expect_success 'validate the path1 output.' '
test_cmp current-path1 expected-path1
'
test_expect_success 'Show the line level log of two files' '
git log --pretty=format:%s%n%b -L /func/,/^}/ path0 -L /output/,/^}/ path1 > current-pathall
'
test_expect_success 'validate the all path output.' '
test_cmp current-pathall expected-pathall
'
test_expect_success 'Test the line number argument' '
git log --pretty=format:%s%n%b -L 2,4 path0 > current-linenum
'
test_expect_success 'validate the line number output.' '
test_cmp current-linenum expected-linenum
'
test_expect_success 'Test the --full-line-diff option' '
git log --pretty=format:%s%n%b --full-line-diff -L 2,4 path0 > current-always
'
test_expect_success 'validate the --full-line-diff output.' '
test_cmp current-always expected-always
'
# Rerun all log with graph
test_expect_success 'Show the line level log of path0 with --graph' '
git log --pretty=format:%s%n%b --graph -L /func/,/^}/ path0 > current-path0-graph
'
test_expect_success 'Show the line level log of path1 with --graph' '
git log --pretty=format:%s%n%b --graph -L /output/,/^}/ path1 > current-path1-graph
'
test_expect_success 'Show the line level log of two files with --graph' '
git log --pretty=format:%s%n%b --graph -L /func/,/^}/ path0 --graph -L /output/,/^}/ path1 > current-pathall-graph
'
test_expect_success 'Test the line number argument with --graph' '
git log --pretty=format:%s%n%b --graph -L 2,4 path0 > current-linenum-graph
'
test_expect_success 'Test the --full-line-diff option with --graph option' '
git log --pretty=format:%s%n%b --full-line-diff --graph -L 2,4 path0 > current-always-graph
'
cat > expected-path0-graph <<\EOF
* Final change of path0
|
| diff --git a/path0 b/path0
| index ccdf243..ccf8bcf 100644
| --- a/path0
| +++ b/path0
| @@ -1,7 +1,6 @@
| void func()
| {
| int a = 10;
| int b = 11;
| - int c;
| - c = 10 * (a + b);
| + printf("%d", a - b);
| }
|
* Change the 5th line of path0
|
| diff --git a/path0 b/path0
| index b0eb888..ccdf243 100644
| --- a/path0
| +++ b/path0
| @@ -1,7 +1,7 @@
| void func()
| {
| int a = 10;
| int b = 11;
| int c;
| - c = a + b;
| + c = 10 * (a + b);
| }
|
* Change 2,3 lines of path0 and path1
|
| diff --git a/path0 b/path0
| index fb33939..b0eb888 100644
| --- a/path0
| +++ b/path0
| @@ -1,7 +1,7 @@
| void func()
| {
| - int a = 0;
| - int b = 1;
| + int a = 10;
| + int b = 11;
| int c;
| c = a + b;
| }
|
* Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +1,7 @@
+void func()
+{
+ int a = 0;
+ int b = 1;
+ int c;
+ c = a + b;
+}
EOF
cat > expected-path1-graph <<\EOF
* Change 2,3 lines of path0 and path1
|
| diff --git a/path1 b/path1
| index 52be2a5..cc54b12 100644
| --- a/path1
| +++ b/path1
| @@ -1,4 +1,5 @@
| void output()
| {
| - printf("hello world");
| + const char *str = "hello world!";
| + printf("%s", str);
| }
|
* Base commit
diff --git a/path1 b/path1
new file mode 100644
index 0000000..52be2a5
--- /dev/null
+++ b/path1
@@ -0,0 +1,4 @@
+void output()
+{
+ printf("hello world");
+}
EOF
cat > expected-pathall-graph <<\EOF
* Final change of path0
|
| diff --git a/path0 b/path0
| index ccdf243..ccf8bcf 100644
| --- a/path0
| +++ b/path0
| @@ -1,7 +1,6 @@
| void func()
| {
| int a = 10;
| int b = 11;
| - int c;
| - c = 10 * (a + b);
| + printf("%d", a - b);
| }
|
* Change the 5th line of path0
|
| diff --git a/path0 b/path0
| index b0eb888..ccdf243 100644
| --- a/path0
| +++ b/path0
| @@ -1,7 +1,7 @@
| void func()
| {
| int a = 10;
| int b = 11;
| int c;
| - c = a + b;
| + c = 10 * (a + b);
| }
|
* Change 2,3 lines of path0 and path1
|
| diff --git a/path0 b/path0
| index fb33939..b0eb888 100644
| --- a/path0
| +++ b/path0
| @@ -1,7 +1,7 @@
| void func()
| {
| - int a = 0;
| - int b = 1;
| + int a = 10;
| + int b = 11;
| int c;
| c = a + b;
| }
| diff --git a/path1 b/path1
| index 52be2a5..cc54b12 100644
| --- a/path1
| +++ b/path1
| @@ -1,4 +1,5 @@
| void output()
| {
| - printf("hello world");
| + const char *str = "hello world!";
| + printf("%s", str);
| }
|
* Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +1,7 @@
+void func()
+{
+ int a = 0;
+ int b = 1;
+ int c;
+ c = a + b;
+}
diff --git a/path1 b/path1
new file mode 100644
index 0000000..52be2a5
--- /dev/null
+++ b/path1
@@ -0,0 +1,4 @@
+void output()
+{
+ printf("hello world");
+}
EOF
cat > expected-linenum-graph <<\EOF
* Change 2,3 lines of path0 and path1
|
| diff --git a/path0 b/path0
| index fb33939..b0eb888 100644
| --- a/path0
| +++ b/path0
| @@ -2,3 +2,3 @@
| {
| - int a = 0;
| - int b = 1;
| + int a = 10;
| + int b = 11;
|
* Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +2,3 @@
+{
+ int a = 0;
+ int b = 1;
EOF
cat > expected-always-graph <<\EOF
* Final change of path0
|
| diff --git a/path0 b/path0
| index ccdf243..ccf8bcf 100644
| --- a/path0
| +++ b/path0
| @@ -2,3 +2,3 @@
| {
| int a = 10;
| int b = 11;
|
* Change the 5th line of path0
|
| diff --git a/path0 b/path0
| index b0eb888..ccdf243 100644
| --- a/path0
| +++ b/path0
| @@ -2,3 +2,3 @@
| {
| int a = 10;
| int b = 11;
|
* Change 2,3 lines of path0 and path1
|
| diff --git a/path0 b/path0
| index fb33939..b0eb888 100644
| --- a/path0
| +++ b/path0
| @@ -2,3 +2,3 @@
| {
| - int a = 0;
| - int b = 1;
| + int a = 10;
| + int b = 11;
|
* Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..fb33939
--- /dev/null
+++ b/path0
@@ -0,0 +2,3 @@
+{
+ int a = 0;
+ int b = 1;
EOF
test_expect_success 'validate the path0 output.' '
test_cmp current-path0-graph expected-path0-graph
'
test_expect_success 'validate the path1 output.' '
test_cmp current-path1-graph expected-path1-graph
'
test_expect_success 'validate the all path output.' '
test_cmp current-pathall-graph expected-pathall-graph
'
test_expect_success 'validate graph output' '
test_cmp current-linenum-graph expected-linenum-graph
'
test_expect_success 'validate --full-line-diff output' '
test_cmp current-always-graph expected-always-graph
'
test_done

174
t/t4302-log-line-merge-history.sh Executable file
View File

@@ -0,0 +1,174 @@
#!/bin/sh
#
# Copyright (c) 2010 Bo Yang
#
test_description='Test git log -L with merge commit'
. ./test-lib.sh
. "$TEST_DIRECTORY"/diff-lib.sh
cat >path0 <<\EOF
void func()
{
printf("hello");
}
EOF
test_expect_success 'Add path0 and commit.' '
git add path0 &&
git commit -m "Base commit"
'
cat >path0 <<\EOF
void func()
{
printf("hello earth");
}
EOF
test_expect_success 'Change path0 in master.' '
git add path0 &&
git commit -m "Change path0 in master"
'
test_expect_success 'Make a new branch from the base commit' '
git checkout -b feature master^
'
cat >path0 <<\EOF
void func()
{
print("hello moon");
}
EOF
test_expect_success 'Change path0 in feature.' '
git add path0 &&
git commit -m "Change path0 in feature"
'
test_expect_success 'Merge the master to feature' '
! git merge master
'
cat >path0 <<\EOF
void func()
{
printf("hello earth and moon");
}
EOF
test_expect_success 'Resolve the conflict' '
git add path0 &&
git commit -m "Merge two branches"
'
test_expect_success 'Show the line level log of path0' '
git log --pretty=format:%s%n%b -L /func/,/^}/ path0 > current
'
cat >expected <<\EOF
Merge two branches
nontrivial merge found
path0
@@ 3,1 @@
printf("hello earth and moon");
Change path0 in master
diff --git a/path0 b/path0
index 56aeee5..11e66c5 100644
--- a/path0
+++ b/path0
@@ -1,4 +1,4 @@
void func()
{
- printf("hello");
+ printf("hello earth");
}
Change path0 in feature
diff --git a/path0 b/path0
index 56aeee5..258fced 100644
--- a/path0
+++ b/path0
@@ -1,4 +1,4 @@
void func()
{
- printf("hello");
+ print("hello moon");
}
Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..56aeee5
--- /dev/null
+++ b/path0
@@ -0,0 +1,4 @@
+void func()
+{
+ printf("hello");
+}
EOF
cat > expected-graph <<\EOF
* Merge two branches
|\
| |
| | nontrivial merge found
| | path0
| | @@ 3,1 @@
| | printf("hello earth and moon");
| |
| |
| * Change path0 in master
| |
| | diff --git a/path0 b/path0
| | index 56aeee5..11e66c5 100644
| | --- a/path0
| | +++ b/path0
| | @@ -3,1 +3,1 @@
| | - printf("hello");
| | + printf("hello earth");
| |
* | Change path0 in feature
|/
|
| diff --git a/path0 b/path0
| index 56aeee5..258fced 100644
| --- a/path0
| +++ b/path0
| @@ -3,1 +3,1 @@
| - printf("hello");
| + print("hello moon");
|
* Base commit
diff --git a/path0 b/path0
new file mode 100644
index 0000000..56aeee5
--- /dev/null
+++ b/path0
@@ -0,0 +3,1 @@
+ printf("hello");
EOF
test_expect_success 'Show the line log of the 2 line of path0 with graph' '
git log --pretty=format:%s%n%b --graph -L 3,+1 path0 > current-graph
'
test_expect_success 'validate the output.' '
test_cmp current expected
'
test_expect_success 'validate the graph output.' '
test_cmp current-graph expected-graph
'
test_done