{"thread":{"id":"24241","subject":"[PATCH v2 01/13] parse-options: stop when encounter a non-option","startedAt":"2010-06-30T15:25:14Z","lastAt":"2010-07-06T14:21:32Z","messageCount":16,"participants":["Bo Yang","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":13},"messages":[{"id":"144520","messageId":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":null,"subject":"[PATCH v2 01/13] parse-options: stop when encounter a non-option","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:14Z","receivedAt":"2010-06-30T15:25:14Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Since we need to support the syntax like:\n-L n1,m1 path1 -L n2,m2 path2.\n\nSo, make the parse-options API not stop with a\nnon-option, but report the status and go on parsing\nthe following.\n\nThanks-to: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n parse-options.c |    3 ++-\n parse-options.h |    3 ++-\n 2 files changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex 0fa79bc..cbb49d3 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -374,7 +374,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t\t\tif (parse_nodash_opt(ctx, arg, options) == 0)\n \t\t\t\tcontinue;\n \t\t\tif (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)\n-\t\t\t\tbreak;\n+\t\t\t\treturn PARSE_OPT_NON_OPTION;\n \t\t\tctx->out[ctx->cpidx++] = ctx->argv[0];\n \t\t\tcontinue;\n \t\t}\n@@ -456,6 +456,7 @@ int parse_options(int argc, const char **argv, const char *prefix,\n \tswitch (parse_options_step(&ctx, options, usagestr)) {\n \tcase PARSE_OPT_HELP:\n \t\texit(129);\n+\tcase PARSE_OPT_NON_OPTION:\n \tcase PARSE_OPT_DONE:\n \t\tbreak;\n \tdefault: /* PARSE_OPT_UNKNOWN */\ndiff --git a/parse-options.h b/parse-options.h\nindex 7435cdb..407697a 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -161,7 +161,8 @@ extern NORETURN void usage_msg_opt(const char *msg,\n enum {\n \tPARSE_OPT_HELP = -1,\n \tPARSE_OPT_DONE,\n-\tPARSE_OPT_UNKNOWN\n+\tPARSE_OPT_NON_OPTION,\n+\tPARSE_OPT_UNKNOWN,\n };\n \n /*\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144521","messageId":"1277911526-12105-2-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 02/13] parse-options: add two helper functions","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:15Z","receivedAt":"2010-06-30T15:25:15Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"1. parse_options_current: get the current option/argument the API\n   now is dealing with;\n2. parse_options_next: make the API to deal with the next\n   option/argument.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n parse-options.c |   19 +++++++++++++++++++\n parse-options.h |    4 ++++\n 2 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex cbb49d3..4266bde 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -439,6 +439,25 @@ unknown:\n \treturn PARSE_OPT_DONE;\n }\n \n+const char *parse_options_current(struct parse_opt_ctx_t *ctx)\n+{\n+\treturn ctx->argv[0];\n+}\n+\n+int parse_options_next(struct parse_opt_ctx_t *ctx, int retain)\n+{\n+\tif (ctx->argc <= 0)\n+\t\treturn -1;\n+\n+\tif (retain == 1)\n+\t\tctx->out[ctx->cpidx++] = ctx->argv[0];\n+\n+\tctx->argc--;\n+\tctx->argv++;\n+\n+\treturn 0;\n+}\n+\n int parse_options_end(struct parse_opt_ctx_t *ctx)\n {\n \tmemmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));\ndiff --git a/parse-options.h b/parse-options.h\nindex 407697a..4791baa 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -187,6 +187,10 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t\t\t      const struct option *options,\n \t\t\t      const char * const usagestr[]);\n \n+extern const char *parse_options_current(struct parse_opt_ctx_t *ctx);\n+\n+extern int parse_options_next(struct parse_opt_ctx_t *ctx, int retain);\n+\n extern int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n extern int parse_options_concat(struct option *dst, size_t, struct option *src);\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144522","messageId":"1277911526-12105-3-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 03/13] add the basic data structure for line level history","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:16Z","receivedAt":"2010-06-30T15:25:16Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"'struct diff_line_range' is the main data structure to store\nthe user interesting line range. There is one 'diff_line_range'\nfor each file, and there are multiple 'struct range' in each\n'diff_line_range'. In this way, we support multiple ranges.\n\nWithin 'struct range', there are multiple 'struct print_range'\nwhich represent a diff chunk.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n Makefile   |    2 +\n diffcore.h |    1 +\n line.c     |  455 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n line.h     |  122 ++++++++++++++++\n revision.c |    6 +\n revision.h |    9 +-\n 6 files changed, 593 insertions(+), 2 deletions(-)\n create mode 100644 line.c\n create mode 100644 line.h\n\ndiff --git a/Makefile b/Makefile\nindex 1fadd43..0183a21 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -496,6 +496,7 @@ LIB_H += grep.h\n LIB_H += hash.h\n LIB_H += help.h\n LIB_H += levenshtein.h\n+LIB_H += line.h\n LIB_H += list-objects.h\n LIB_H += ll-merge.h\n LIB_H += log-tree.h\n@@ -582,6 +583,7 @@ LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n LIB_OBJS += levenshtein.o\n+LIB_OBJS += line.o\n LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\ndiff --git a/diffcore.h b/diffcore.h\nindex 491bea0..13d8e93 100644\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -23,6 +23,7 @@\n #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */\n \n struct userdiff_driver;\n+struct diff_options;\n \n struct diff_filespec {\n \tunsigned char sha1[20];\ndiff --git a/line.c b/line.c\nnew file mode 100644\nindex 0000000..ffa97ea\n--- /dev/null\n+++ b/line.c\n@@ -0,0 +1,455 @@\n+#include \"line.h\"\n+#include \"cache.h\"\n+#include \"tag.h\"\n+#include \"blob.h\"\n+#include \"tree.h\"\n+#include \"commit.h\"\n+#include \"diff.h\"\n+#include \"decorate.h\"\n+#include \"revision.h\"\n+#include \"xdiff-interface.h\"\n+#include \"strbuf.h\"\n+#include \"log-tree.h\"\n+\n+static void cleanup(struct diff_line_range *r)\n+{\n+\twhile (r) {\n+\t\tstruct diff_line_range *next = r->next;\n+\t\tDIFF_LINE_RANGE_CLEAR(r);\n+\t\tfree(r);\n+\t\tr = next;\n+\t}\n+}\n+\n+static struct object *verify_commit(struct rev_info *revs)\n+{\n+\tstruct object *commit = NULL;\n+\tconst char *name = NULL;\n+\tint i;\n+\n+\tfor (i = 0; i < revs->pending.nr; i++) {\n+\t\tstruct object *obj = revs->pending.objects[i].item;\n+\t\tif (obj->flags & UNINTERESTING)\n+\t\t\tcontinue;\n+\t\twhile (obj->type == OBJ_TAG)\n+\t\t\tobj = deref_tag(obj, NULL, 0);\n+\t\tif (obj->type != OBJ_COMMIT)\n+\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n+\t\tif (commit)\n+\t\t\tdie(\"More than one commit to dig from: %s and %s?\",\n+\t\t\t    revs->pending.objects[i].name, name);\n+\t\tcommit = obj;\n+\t\tname = revs->pending.objects[i].name;\n+\t}\n+\n+\tif (commit == NULL)\n+\t\tdie(\"No commit specified?\");\n+\n+\treturn commit;\n+}\n+\n+static void fill_blob_sha1(struct commit *commit, struct diff_line_range *r)\n+{\n+\tunsigned mode;\n+\tunsigned char sha1[20];\n+\n+\twhile (r) {\n+\t\tif (get_tree_entry(commit->object.sha1, r->spec->path,\n+\t\t\tsha1, &mode))\n+\t\t\tgoto error;\n+\t\tfill_filespec(r->spec, sha1, mode);\n+\t\tr = r->next;\n+\t}\n+\n+\treturn;\n+error:\n+\tdie(\"There is no path %s in the commit\", r->spec->path);\n+}\n+\n+static void fill_line_ends(struct diff_filespec *spec, long *lines,\n+\tunsigned long **line_ends)\n+{\n+\tint num = 0, size = 50;\n+\tlong cur = 0;\n+\tunsigned long *ends = NULL;\n+\tchar *data = NULL;\n+\n+\tif (diff_populate_filespec(spec, 0))\n+\t\tdie(\"Cannot read blob %s\", sha1_to_hex(spec->sha1));\n+\n+\tends = xmalloc(size * sizeof(int));\n+\tends[cur++] = -1;\n+\tdata = spec->data;\n+\twhile (num < spec->size) {\n+\t\tif (data[num] == '\\n' || num == spec->size - 1) {\n+\t\t\tALLOC_GROW(ends, (cur + 1), size);\n+\t\t\tends[cur++] = num;\n+\t\t}\n+\t\tnum++;\n+\t}\n+\n+\t/* shrink the array to fit the elements */\n+\tends = xrealloc(ends, cur * sizeof(int));\n+\t*lines = cur;\n+\t*line_ends = ends;\n+}\n+\n+static const char *nth_line(struct diff_filespec *spec, int line,\n+\t\tlong lines, unsigned long *line_ends)\n+{\n+\tassert(line < lines);\n+\tassert(spec && spec->data);\n+\n+\treturn (char *)spec->data + line_ends[line] + 1;\n+}\n+\n+/*\n+ * copied from blame.c, indeed, we can even to use this to test\n+ * whether line log works. :)\n+ */\n+static const char *parse_loc(const char *spec, struct diff_filespec *file,\n+\t\t\t     long lines, unsigned long *line_ends,\n+\t\t\t     long begin, long *ret)\n+{\n+\tchar *term;\n+\tconst char *line;\n+\tlong num;\n+\tint reg_error;\n+\tregex_t regexp;\n+\tregmatch_t match[1];\n+\n+\t/* Allow \"-L <something>,+20\" to mean starting at <something>\n+\t * for 20 lines, or \"-L <something>,-5\" for 5 lines ending at\n+\t * <something>.\n+\t */\n+\tif (1 < begin && (spec[0] == '+' || spec[0] == '-')) {\n+\t\tnum = strtol(spec + 1, &term, 10);\n+\t\tif (term != spec + 1) {\n+\t\t\tif (spec[0] == '-')\n+\t\t\t\tnum = 0 - num;\n+\t\t\tif (0 < num)\n+\t\t\t\t*ret = begin + num - 2;\n+\t\t\telse if (!num)\n+\t\t\t\t*ret = begin;\n+\t\t\telse\n+\t\t\t\t*ret = begin + num;\n+\t\t\treturn term;\n+\t\t}\n+\t\treturn spec;\n+\t}\n+\tnum = strtol(spec, &term, 10);\n+\tif (term != spec) {\n+\t\t*ret = num;\n+\t\treturn term;\n+\t}\n+\tif (spec[0] != '/')\n+\t\treturn spec;\n+\n+\t/* it could be a regexp of form /.../ */\n+\tfor (term = (char *) spec + 1; *term && *term != '/'; term++) {\n+\t\tif (*term == '\\\\')\n+\t\t\tterm++;\n+\t}\n+\tif (*term != '/')\n+\t\treturn spec;\n+\n+\t/* try [spec+1 .. term-1] as regexp */\n+\t*term = 0;\n+\tbegin--; /* input is in human terms */\n+\tline = nth_line(file, begin, lines, line_ends);\n+\n+\tif (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&\n+\t    !(reg_error = regexec(&regexp, line, 1, match, 0))) {\n+\t\tconst char *cp = line + match[0].rm_so;\n+\t\tconst char *nline;\n+\n+\t\twhile (begin++ < lines) {\n+\t\t\tnline = nth_line(file, begin, lines, line_ends);\n+\t\t\tif (line <= cp && cp < nline)\n+\t\t\t\tbreak;\n+\t\t\tline = nline;\n+\t\t}\n+\t\t*ret = begin;\n+\t\tregfree(&regexp);\n+\t\t*term++ = '/';\n+\t\treturn term;\n+\t}\n+\telse {\n+\t\tchar errbuf[1024];\n+\t\tregerror(reg_error, &regexp, errbuf, 1024);\n+\t\tdie(\"-L parameter '%s': %s\", spec + 1, errbuf);\n+\t}\n+}\n+\n+static void parse_range(long lines, unsigned long *line_ends,\n+\t\tstruct range *r, struct diff_filespec *spec)\n+{\n+\tconst char *term;\n+\n+\tterm = parse_loc(r->arg, spec, lines, line_ends, 1, &r->start);\n+\tif (*term == ',') {\n+\t\tterm = parse_loc(term + 1, spec, lines, line_ends,\n+\t\t\tr->start + 1, &r->end);\n+\t\tif (*term) {\n+\t\t\tdie(\"-L parameter's argument should be <start>,<end>\");\n+\t\t}\n+\t}\n+\n+\tif (*term) {\n+\t\tdie(\"-L parameter's argument should be <start>,<end>\");\n+\t}\n+\n+\tif (r->start > r->end) {\n+\t\tlong tmp = r->start;\n+\t\tr->start = r->end;\n+\t\tr->end = tmp;\n+\t}\n+\n+\tif (r->start < 1)\n+\t\tr->start = 1;\n+\tif (r->end >= lines)\n+\t\tr->end = lines - 1;\n+}\n+\n+static void parse_lines(struct commit *commit, struct diff_line_range *r)\n+{\n+\tint i;\n+\tstruct range *old_range = NULL;\n+\tlong lines = 0;\n+\tunsigned long *ends = NULL;\n+\n+\twhile (r) {\n+\t\tstruct diff_filespec *spec = r->spec;\n+\t\tint num = r->nr;\n+\t\tassert(spec);\n+\t\tfill_blob_sha1(commit, r);\n+\t\told_range = r->ranges;\n+\t\tr->ranges = NULL;\n+\t\tr->nr = r->alloc = 0;\n+\t\tfill_line_ends(spec, &lines, &ends);\n+\t\tfor (i = 0; i < num; i++) {\n+\t\t\tparse_range(lines, ends, old_range + i, spec);\n+\t\t\tdiff_line_range_insert(r, old_range[i].arg,\n+\t\t\t\told_range[i].start, old_range[i].end);\n+\t\t}\n+\n+\t\tfree(ends);\n+\t\tends = NULL;\n+\n+\t\tr = r->next;\n+\t\tfree(old_range);\n+\t}\n+}\n+\n+/*\n+ * Insert a new line range into a diff_line_range struct, and keep the\n+ * r->ranges sorted by their starting line number.\n+ */\n+struct range *diff_line_range_insert(struct diff_line_range *r, const char *arg,\n+\t\t\t\tint start, int end)\n+{\n+\tint i = 0;\n+\tstruct range *rs = r->ranges;\n+\tint left_extend = 0, right_extend = 0;\n+\n+\tassert(r != NULL);\n+\tassert(start <= end);\n+\n+\tif (r->nr == 0 || rs[r->nr - 1].end < start - 1) {\n+\t\tDIFF_LINE_RANGE_GROW(r);\n+\t\trs = r->ranges;\n+\t\tint num = r->nr - 1;\n+\t\trs[num].arg = arg;\n+\t\trs[num].start = start;\n+\t\trs[num].end = end;\n+\t\treturn rs + num;\n+\t}\n+\n+\tfor (; i < r->nr; i++) {\n+\t\tif (rs[i].end < start - 1)\n+\t\t\tcontinue;\n+\t\tif (rs[i].end == start - 1) {\n+\t\t\trs[i].end = end;\n+\t\t\tright_extend = 1;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tassert(rs[i].end > start - 1);\n+\t\tif (rs[i].start <= start) {\n+\t\t\tif (rs[i].end < end) {\n+\t\t\t\trs[i].end = end;\n+\t\t\t\tright_extend = 1;\n+\t\t\t}\n+\t\t\tgoto out;\n+\t\t} else if (rs[i].start <= end + 1) {\n+\t\t\trs[i].start = start;\n+\t\t\tleft_extend = 1;\n+\t\t\tif (rs[i].end < end) {\n+\t\t\t\trs[i].end = end;\n+\t\t\t\tright_extend = 1;\n+\t\t\t}\n+\t\t\tgoto out;\n+\t\t} else {\n+\t\t\tint num = r->nr - i;\n+\t\t\tDIFF_LINE_RANGE_GROW(r);\n+\t\t\trs = r->ranges;\n+\t\t\tmemmove(rs + i + 1, rs + i, num * sizeof(struct range));\n+\t\t\trs[i].arg = arg;\n+\t\t\trs[i].start = start;\n+\t\t\trs[i].end = end;\n+\t\t\tgoto out;\n+\t\t}\n+\t}\n+\n+out:\n+\tassert(r->nr != i);\n+\tif (left_extend) {\n+\t\tint j = i;\n+\t\tfor (; j > -1; j--) {\n+\t\t\tif (rs[j].end >= rs[i].start - 1)\n+\t\t\t\tif (rs[j].start < rs[i].start)\n+\t\t\t\t\trs[i].start = rs[j].start;\n+\t\t}\n+\t\tmemmove(rs + j + 1, rs + i, (r->nr - i) * sizeof(struct range));\n+\t\tr->nr -= i - j - 1;\n+\t}\n+\tif (right_extend) {\n+\t\tint j = i;\n+\t\tfor (; j < r->nr; j++) {\n+\t\t\tif (rs[j].start <= rs[i].end + 1)\n+\t\t\t\tif (rs[j].end > rs[i].end)\n+\t\t\t\t\trs[i].end = rs[j].end;\n+\t\t}\n+\t\tif (j < r->nr) {\n+\t\t\tmemmove(rs + i + 1, rs + j, (r->nr - j) * sizeof(struct range));\n+\t\t}\n+\t\tr->nr -= j - i - 1;\n+\t}\n+\tassert(r->nr);\n+\n+\treturn rs + i;\n+}\n+\n+void diff_line_range_clear(struct diff_line_range *r)\n+{\n+\tint i = 0, zero = 0;\n+\n+\tfor (; i < r->nr; i++) {\n+\t\tstruct range *rg = r->ranges + i;\n+\t\tRANGE_CLEAR(rg);\n+\t}\n+\n+\tif (r->prev) {\n+\t\tzero = 0;\n+\t\tif (r->prev->count == 1) {\n+\t\t\tzero = 1;\n+\t\t}\n+\t\tfree_filespec(r->prev);\n+\t\tif (zero)\n+\t\t\tr->prev = NULL;\n+\t}\n+\tif (r->spec) {\n+\t\tzero = 0;\n+\t\tif (r->spec->count == 1) {\n+\t\t\tzero = 1;\n+\t\t}\n+\t\tfree_filespec(r->spec);\n+\t\tif (zero)\n+\t\t\tr->spec = NULL;\n+\t}\n+\n+\tr->status = '\\0';\n+\tr->alloc = r->nr = 0;\n+\n+\tif (r->ranges)\n+\t\tfree(r->ranges);\n+\tr->ranges = NULL;\n+}\n+\n+void diff_line_range_append(struct diff_line_range *r, const char *arg)\n+{\n+\tDIFF_LINE_RANGE_GROW(r);\n+\tr->ranges[r->nr - 1].arg = arg;\n+}\n+\n+struct diff_line_range *diff_line_range_merge(struct diff_line_range *out,\n+\tstruct diff_line_range *other)\n+{\n+\tstruct diff_line_range *one = out, *two = other;\n+\tstruct diff_line_range *pone;\n+\n+\twhile (one) {\n+\t\tstruct diff_line_range *prev;\n+\t\ttwo = other;\n+\t\tprev = other;\n+\t\twhile (two) {\n+\t\t\tif (!strcmp(one->spec->path, two->spec->path)) {\n+\t\t\t\tint i = 0;\n+\t\t\t\tfor (; i < two->nr; i++) {\n+\t\t\t\t\tdiff_line_range_insert(one, NULL,\n+\t\t\t\t\t\ttwo->ranges[i].start,\n+\t\t\t\t\t\ttwo->ranges[i].end);\n+\t\t\t\t}\n+\t\t\t\tif (prev == other) {\n+\t\t\t\t\tother = other->next;\n+\t\t\t\t} else {\n+\t\t\t\t\tprev->next = two->next;\n+\t\t\t\t}\n+\t\t\t\tDIFF_LINE_RANGE_CLEAR(two);\n+\t\t\t\tfree(two);\n+\t\t\t\ttwo = NULL;\n+\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tprev = two;\n+\t\t\ttwo = two->next;\n+\t\t}\n+\n+\t\tpone = one;\n+\t\tone = one->next;\n+\t}\n+\tpone->next = other;\n+\n+\treturn out;\n+}\n+\n+void add_line_range(struct rev_info *revs, struct commit *commit, struct diff_line_range *r)\n+{\n+\tstruct diff_line_range *ret = NULL;\n+\n+\tif (r != NULL) {\n+\t\tret = lookup_decoration(&revs->line_range, &commit->object);\n+\t\tif (ret != NULL) {\n+\t\t\tdiff_line_range_merge(ret, r);\n+\t\t} else {\n+\t\t\tadd_decoration(&revs->line_range, &commit->object, r);\n+\t\t}\n+\t\tcommit->object.flags |= RANGE_UPDATE;\n+\t}\n+}\n+\n+struct diff_line_range *lookup_line_range(struct rev_info *revs, struct commit *commit)\n+{\n+\tstruct diff_line_range *ret = NULL;\n+\n+\tret = lookup_decoration(&revs->line_range, &commit->object);\n+\treturn ret;\n+}\n+\n+void setup_line(struct rev_info *rev, struct diff_line_range *r)\n+{\n+\tstruct commit *commit = NULL;\n+\tstruct diff_options *opt = &rev->diffopt;\n+\n+\tcommit = (struct commit *)verify_commit(rev);\n+\tparse_lines(commit, r);\n+\n+\tadd_line_range(rev, commit, r);\n+\t/*\n+\t * Note we support -M/-C to detect file rename\n+\t */\n+\topt->nr_paths = 0;\n+\tdiff_tree_release_paths(opt);\n+}\n+\ndiff --git a/line.h b/line.h\nnew file mode 100644\nindex 0000000..a2f8545\n--- /dev/null\n+++ b/line.h\n@@ -0,0 +1,122 @@\n+#ifndef LINE_H\n+#define LINE_H\n+\n+#include \"diffcore.h\"\n+\n+struct rev_info;\n+struct commit;\n+struct diff_line_range;\n+struct diff_options;\n+\n+struct print_range {\n+\tint start, end;\n+\tint pstart, pend;\n+\tint line_added : 1;\n+};\n+\n+struct print_pair {\n+\tint alloc, nr;\n+\tstruct print_range *ranges;\n+};\n+\n+#define PRINT_RANGE_INIT(r) \\\n+\tdo { \\\n+\t\t(r)->line_added = 0; \\\n+\t} while(0);\n+\n+#define PRINT_PAIR_INIT(p) \\\n+\tdo { \\\n+\t\t(p)->alloc = (p)->nr = 0; \\\n+\t\t(p)->ranges = NULL; \\\n+\t} while(0);\n+\n+#define PRINT_PAIR_GROW(p) \\\n+\tdo { \\\n+\t\t(p)->nr ++; \\\n+\t\tALLOC_GROW((p)->ranges, (p)->nr, (p)->alloc); \\\n+\t} while (0);\n+\n+#define PRINT_PAIR_CLEAR(p) \\\n+\tdo { \\\n+\t\t(p)->alloc = (p)->nr = 0; \\\n+\t\tif ((p)->ranges) \\\n+\t\t\tfree((p)->ranges); \\\n+\t\t(p)->ranges = NULL; \\\n+\t} while(0);\n+\n+struct range {\n+\tconst char *arg;\t//The argument to specify this line range\n+\tlong start, end;\t//The start line number, inclusive\n+\tlong pstart, pend;\t//The end line number, inclusive\n+\tstruct print_pair pair;\n+\t\t\t//The changed lines inside this range\n+\tunsigned int diff:1;\n+};\n+\n+struct diff_line_range {\n+\tstruct diff_filespec *prev;\n+\tstruct diff_filespec *spec;\n+\tchar status;\n+\tint alloc;\n+\tint nr;\n+\tstruct range *ranges;\n+\tunsigned int\ttouch:1,\n+\t\t\tdiff:1;\n+\tstruct diff_line_range *next;\n+};\n+\n+#define RANGE_INIT(r) \\\n+\tdo { \\\n+\t\t(r)->arg = NULL; \\\n+\t\t(r)->start = (r)->end = 0; \\\n+\t\t(r)->pstart = (r)->pend = 0; \\\n+\t\tPRINT_PAIR_INIT(&((r)->pair)); \\\n+\t\t(r)->diff = 0; \\\n+\t} while (0);\n+\n+#define RANGE_CLEAR(r) \\\n+\tdo { \\\n+\t\t(r)->arg = NULL; \\\n+\t\t(r)->start = (r)->end = 0; \\\n+\t\t(r)->pstart = (r)->pend = 0; \\\n+\t\tPRINT_PAIR_CLEAR(&r->pair); \\\n+\t\t(r)->diff = 0; \\\n+\t} while(0);\n+\n+#define DIFF_LINE_RANGE_INIT(r) \\\n+\tdo { \\\n+\t\t(r)->prev = (r)->spec = NULL; \\\n+\t\t(r)->status = '\\0'; \\\n+\t\t(r)->alloc = (r)->nr = 0; \\\n+\t\t(r)->ranges = NULL; \\\n+\t\t(r)->next = NULL; \\\n+\t\t(r)->touch = 0; \\\n+\t\t(r)->diff = 0; \\\n+\t} while(0);\n+\n+#define DIFF_LINE_RANGE_GROW(r) \\\n+\tdo { \\\n+\t\t(r)->nr ++; \\\n+\t\tALLOC_GROW((r)->ranges, (r)->nr, (r)->alloc); \\\n+\t\tRANGE_INIT(((r)->ranges + (r)->nr - 1)); \\\n+\t} while(0);\n+\n+#define DIFF_LINE_RANGE_CLEAR(r) \\\n+\tdiff_line_range_clear((r));\n+\n+extern struct range *diff_line_range_insert(struct diff_line_range *r, const char *arg,\n+\t\t\t\tint start, int end);\n+extern void diff_line_range_append(struct diff_line_range *r, const char *arg);\n+\n+extern void diff_line_range_clear(struct diff_line_range *r);\n+\n+extern struct diff_line_range *diff_line_range_merge(struct diff_line_range *out,\n+\tstruct diff_line_range *other);\n+\n+extern void setup_line(struct rev_info *rev, struct diff_line_range *r);\n+\n+extern void add_line_range(struct rev_info *revs, struct commit *commit, struct diff_line_range *r);\n+\n+extern struct diff_line_range *lookup_line_range(struct rev_info *revs, struct commit *commit);\n+\n+#endif\ndiff --git a/revision.c b/revision.c\nindex 5403581..1de96df 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1635,6 +1635,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \tif (revs->combine_merges)\n \t\trevs->ignore_merges = 0;\n \trevs->diffopt.abbrev = revs->abbrev;\n+\n+\tif (revs->line) {\n+\t\trevs->limited = 1;\n+\t\trevs->topo_order = 1;\n+\t}\n+\n \tif (diff_setup_done(&revs->diffopt) < 0)\n \t\tdie(\"diff_setup_done failed\");\n \ndiff --git a/revision.h b/revision.h\nindex 855464f..7b01277 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -14,7 +14,8 @@\n #define CHILD_SHOWN\t(1u<<6)\n #define ADDED\t\t(1u<<7)\t/* Parents already parsed and added? */\n #define SYMMETRIC_LEFT\t(1u<<8)\n-#define ALL_REV_FLAGS\t((1u<<9)-1)\n+#define RANGE_UPDATE\t(1u<<9) /* for line level traverse */\n+#define ALL_REV_FLAGS\t((1u<<10)-1)\n \n #define DECORATE_SHORT_REFS\t1\n #define DECORATE_FULL_REFS\t2\n@@ -67,7 +68,8 @@ struct rev_info {\n \t\t\tcherry_pick:1,\n \t\t\tbisect:1,\n \t\t\tancestry_path:1,\n-\t\t\tfirst_parent_only:1;\n+\t\t\tfirst_parent_only:1,\n+\t\t\tline:1;\n \n \t/* Diff flags */\n \tunsigned int\tdiff:1,\n@@ -132,6 +134,9 @@ struct rev_info {\n \n \t/* notes-specific options: which refs to show */\n \tstruct display_notes_opt notes_opt;\n+\n+\t/* line-level intersting range */\n+\tstruct decoration line_range;\n };\n \n #define REV_TREE_SAME\t\t0\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144523","messageId":"1277911526-12105-4-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 04/13] refactor parse_loc","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:17Z","receivedAt":"2010-06-30T15:25:17Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Both 'git blame -L' and 'git log -L' parse the same style\nof line number arguments, so put the 'parse_loc' function\nto line.c and export it.\nThe caller of parse_loc should provide a callback function\nwhich is used to calculate the nth line start position.\nOther parts such as regexp search, line number parsing are\nabstracted and re-used.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n builtin/blame.c |   89 +++++-------------------------------------------------\n line.c          |   35 ++++++++++++---------\n line.h          |    5 +++\n 3 files changed, 34 insertions(+), 95 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 01e62fd..17b71cd 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -21,6 +21,7 @@\n #include \"parse-options.h\"\n #include \"utf8.h\"\n #include \"userdiff.h\"\n+#include \"line.h\"\n \n static char blame_usage[] = \"git blame [options] [rev-opts] [rev] [--] file\";\n \n@@ -541,11 +542,16 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src)\n \tdst->score = 0;\n }\n \n-static const char *nth_line(struct scoreboard *sb, int lno)\n+static const char *nth_line(struct scoreboard *sb, long lno)\n {\n \treturn sb->final_buf + sb->lineno[lno];\n }\n \n+static const char *nth_line_cb(void *data, long lno)\n+{\n+\treturn nth_line((struct scoreboard *)data, lno);\n+}\n+\n /*\n  * It is known that lines between tlno to same came from parent, and e\n  * has an overlap with that range.  it also is known that parent's\n@@ -1907,83 +1913,6 @@ static const char *add_prefix(const char *prefix, const char *path)\n }\n \n /*\n- * Parsing of (comma separated) one item in the -L option\n- */\n-static const char *parse_loc(const char *spec,\n-\t\t\t     struct scoreboard *sb, long lno,\n-\t\t\t     long begin, long *ret)\n-{\n-\tchar *term;\n-\tconst char *line;\n-\tlong num;\n-\tint reg_error;\n-\tregex_t regexp;\n-\tregmatch_t match[1];\n-\n-\t/* Allow \"-L <something>,+20\" to mean starting at <something>\n-\t * for 20 lines, or \"-L <something>,-5\" for 5 lines ending at\n-\t * <something>.\n-\t */\n-\tif (1 < begin && (spec[0] == '+' || spec[0] == '-')) {\n-\t\tnum = strtol(spec + 1, &term, 10);\n-\t\tif (term != spec + 1) {\n-\t\t\tif (spec[0] == '-')\n-\t\t\t\tnum = 0 - num;\n-\t\t\tif (0 < num)\n-\t\t\t\t*ret = begin + num - 2;\n-\t\t\telse if (!num)\n-\t\t\t\t*ret = begin;\n-\t\t\telse\n-\t\t\t\t*ret = begin + num;\n-\t\t\treturn term;\n-\t\t}\n-\t\treturn spec;\n-\t}\n-\tnum = strtol(spec, &term, 10);\n-\tif (term != spec) {\n-\t\t*ret = num;\n-\t\treturn term;\n-\t}\n-\tif (spec[0] != '/')\n-\t\treturn spec;\n-\n-\t/* it could be a regexp of form /.../ */\n-\tfor (term = (char *) spec + 1; *term && *term != '/'; term++) {\n-\t\tif (*term == '\\\\')\n-\t\t\tterm++;\n-\t}\n-\tif (*term != '/')\n-\t\treturn spec;\n-\n-\t/* try [spec+1 .. term-1] as regexp */\n-\t*term = 0;\n-\tbegin--; /* input is in human terms */\n-\tline = nth_line(sb, begin);\n-\n-\tif (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&\n-\t    !(reg_error = regexec(&regexp, line, 1, match, 0))) {\n-\t\tconst char *cp = line + match[0].rm_so;\n-\t\tconst char *nline;\n-\n-\t\twhile (begin++ < lno) {\n-\t\t\tnline = nth_line(sb, begin);\n-\t\t\tif (line <= cp && cp < nline)\n-\t\t\t\tbreak;\n-\t\t\tline = nline;\n-\t\t}\n-\t\t*ret = begin;\n-\t\tregfree(&regexp);\n-\t\t*term++ = '/';\n-\t\treturn term;\n-\t}\n-\telse {\n-\t\tchar errbuf[1024];\n-\t\tregerror(reg_error, &regexp, errbuf, 1024);\n-\t\tdie(\"-L parameter '%s': %s\", spec + 1, errbuf);\n-\t}\n-}\n-\n-/*\n  * Parsing of -L option\n  */\n static void prepare_blame_range(struct scoreboard *sb,\n@@ -1993,9 +1922,9 @@ static void prepare_blame_range(struct scoreboard *sb,\n {\n \tconst char *term;\n \n-\tterm = parse_loc(bottomtop, sb, lno, 1, bottom);\n+\tterm = parse_loc(bottomtop, nth_line_cb, sb, lno, 1, bottom);\n \tif (*term == ',') {\n-\t\tterm = parse_loc(term + 1, sb, lno, *bottom + 1, top);\n+\t\tterm = parse_loc(term + 1, nth_line_cb, sb, lno, *bottom + 1, top);\n \t\tif (*term)\n \t\t\tusage(blame_usage);\n \t}\ndiff --git a/line.c b/line.c\nindex ffa97ea..0855d29 100644\n--- a/line.c\n+++ b/line.c\n@@ -94,22 +94,26 @@ static void fill_line_ends(struct diff_filespec *spec, long *lines,\n \t*line_ends = ends;\n }\n \n-static const char *nth_line(struct diff_filespec *spec, int line,\n-\t\tlong lines, unsigned long *line_ends)\n+struct nth_line_cb {\n+\tstruct diff_filespec *spec;\n+\tlong lines;\n+\tunsigned long *line_ends;\n+};\n+\n+static const char *nth_line(void *data, long line)\n {\n-\tassert(line < lines);\n-\tassert(spec && spec->data);\n+\tstruct nth_line_cb *d = data;\n+\tassert(d && line < d->lines);\n+\tassert(d->spec && d->spec->data);\n \n-\treturn (char *)spec->data + line_ends[line] + 1;\n+\treturn (char *)d->spec->data + d->line_ends[line] + 1;\n }\n \n /*\n- * copied from blame.c, indeed, we can even to use this to test\n- * whether line log works. :)\n+ * Parsing of (comma separated) one item in the -L option\n  */\n-static const char *parse_loc(const char *spec, struct diff_filespec *file,\n-\t\t\t     long lines, unsigned long *line_ends,\n-\t\t\t     long begin, long *ret)\n+const char *parse_loc(const char *spec, nth_line_fn_t nth_line,\n+\t\tvoid *data, long lines, long begin, long *ret)\n {\n \tchar *term;\n \tconst char *line;\n@@ -156,7 +160,7 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file,\n \t/* try [spec+1 .. term-1] as regexp */\n \t*term = 0;\n \tbegin--; /* input is in human terms */\n-\tline = nth_line(file, begin, lines, line_ends);\n+\tline = nth_line(data, begin);\n \n \tif (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&\n \t    !(reg_error = regexec(&regexp, line, 1, match, 0))) {\n@@ -164,7 +168,7 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file,\n \t\tconst char *nline;\n \n \t\twhile (begin++ < lines) {\n-\t\t\tnline = nth_line(file, begin, lines, line_ends);\n+\t\t\tnline = nth_line(data, begin);\n \t\t\tif (line <= cp && cp < nline)\n \t\t\t\tbreak;\n \t\t\tline = nline;\n@@ -185,11 +189,12 @@ static void parse_range(long lines, unsigned long *line_ends,\n \t\tstruct range *r, struct diff_filespec *spec)\n {\n \tconst char *term;\n+\tstruct nth_line_cb data = {spec, lines, line_ends};\n \n-\tterm = parse_loc(r->arg, spec, lines, line_ends, 1, &r->start);\n+\tterm = parse_loc(r->arg, nth_line, &data, lines, 1, &r->start);\n \tif (*term == ',') {\n-\t\tterm = parse_loc(term + 1, spec, lines, line_ends,\n-\t\t\tr->start + 1, &r->end);\n+\t\tterm = parse_loc(term + 1, nth_line, &data, lines,\n+\t\t\t\tr->start + 1, &r->end);\n \t\tif (*term) {\n \t\t\tdie(\"-L parameter's argument should be <start>,<end>\");\n \t\t}\ndiff --git a/line.h b/line.h\nindex a2f8545..0eb03bd 100644\n--- a/line.h\n+++ b/line.h\n@@ -8,6 +8,8 @@ struct commit;\n struct diff_line_range;\n struct diff_options;\n \n+typedef const char *(*nth_line_fn_t)(void *data, long lno);\n+\n struct print_range {\n \tint start, end;\n \tint pstart, pend;\n@@ -119,4 +121,7 @@ extern void add_line_range(struct rev_info *revs, struct commit *commit, struct\n \n extern struct diff_line_range *lookup_line_range(struct rev_info *revs, struct commit *commit);\n \n+const char *parse_loc(const char *spec, nth_line_fn_t nth_line,\n+\t\tvoid *data, long lines, long begin, long *ret);\n+\n #endif\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144524","messageId":"1277911526-12105-5-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 05/13] parse the -L options","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:18Z","receivedAt":"2010-06-30T15:25:18Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"With the two new APIs of parse options added in the previous\ncommit, we parse the multiple '-L n,m <file>' syntax.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n builtin/log.c |  102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 101 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f068583..d0faf07 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -19,6 +19,7 @@\n #include \"remote.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"line.h\"\n \n /* Set a default date-time format for git log (\"log.date\" config variable) */\n static const char *default_date_mode = NULL;\n@@ -27,11 +28,24 @@ static int default_show_root = 1;\n static int decoration_style;\n static const char *fmt_patch_subject_prefix = \"PATCH\";\n static const char *fmt_pretty;\n+static const char *dashdash = \"--\";\n \n-static const char * const builtin_log_usage =\n+static char builtin_log_usage[] =\n \t\"git log [<options>] [<since>..<until>] [[--] <path>...]\\n\"\n+\t\"git log [<options>] -L n,m <path>\\n\"\n \t\"   or: git show [options] <object>...\";\n \n+static const char *log_opt_usage[] = {\n+\tbuiltin_log_usage,\n+\tNULL\n+};\n+\n+struct line_opt_callback_data {\n+\tstruct diff_line_range **range;\n+\tstruct parse_opt_ctx_t *ctx;\n+\tstruct rev_info *rev;\n+};\n+\n static int parse_decoration_style(const char *var, const char *value)\n {\n \tswitch (git_config_maybe_bool(var, value)) {\n@@ -49,12 +63,41 @@ static int parse_decoration_style(const char *var, const char *value)\n \treturn -1;\n }\n \n+static int log_line_range_callback(const struct option *option, const char *arg, int unset)\n+{\n+\tstruct line_opt_callback_data *data = option->value;\n+\tstruct diff_line_range *r = *data->range;\n+\tstruct parse_opt_ctx_t *ctx = data->ctx;\n+\tif (!arg)\n+\t\treturn -1;\n+\n+\tif (r->nr == 0 && r->next == NULL) {\n+\t\tctx->out[ctx->cpidx++] = dashdash;\n+\t}\n+\n+\tdiff_line_range_append(r, arg);\n+\tdata->rev->line = 1;\n+\treturn 0;\n+}\n+\n static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \t\t\t struct rev_info *rev, struct setup_revision_opt *opt)\n {\n \tint i;\n \tint decoration_given = 0;\n \tstruct userformat_want w;\n+\tconst char *path = NULL, *pathspec = NULL;\n+\tstatic struct diff_line_range *range = NULL;\n+\tstatic struct parse_opt_ctx_t ctx;\n+\tstatic struct line_opt_callback_data line_cb = {&range, &ctx, NULL};\n+\tstatic const struct option options[] = {\n+\t\tOPT_CALLBACK('L', NULL, &line_cb, \"n,m\", \"Process only line range n,m, counting from 1\", log_line_range_callback),\n+\t\tOPT_END()\n+\t};\n+\n+\tline_cb.rev = rev;\n+\trange = xmalloc(sizeof(*range));\n+\tDIFF_LINE_RANGE_INIT(range);\n \n \trev->abbrev = DEFAULT_ABBREV;\n \trev->commit_format = CMIT_FMT_DEFAULT;\n@@ -75,6 +118,58 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \t */\n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(builtin_log_usage);\n+\n+\tparse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |\n+\t\t\tPARSE_OPT_KEEP_ARGV0 | PARSE_OPT_STOP_AT_NON_OPTION);\n+\tfor (;;) {\n+\t\tswitch (parse_options_step(&ctx, options, log_opt_usage)) {\n+\t\tcase PARSE_OPT_HELP:\n+\t\t\texit(129);\n+\t\tcase PARSE_OPT_DONE:\n+\t\t\tgoto parse_done;\n+\t\tcase PARSE_OPT_NON_OPTION:\n+\t\t\tpath = parse_options_current(&ctx);\n+\t\t\tpathspec = prefix_path(prefix, prefix ? strlen(prefix) : 0, path);\n+\t\t\trange->spec = alloc_filespec(pathspec);\n+\t\t\tfree((void *)pathspec);\n+\t\t\tif (range->nr == 0) {\n+\t\t\t\tif(range->next) {\n+\t\t\t\t\tdie(\"Path %s need a -L <range> option\\n\"\n+\t\t\t\t\t\"If you want follow the history of the whole file \"\n+\t\t\t\t\t\"whether to using 'git log' without -L or using \"\n+\t\t\t\t\t\"'git log -L 1,$ <path>'\", range->spec->path);\n+\t\t\t\t} else {\n+\t\t\t\t\tparse_options_next(&ctx, 1);\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tstruct diff_line_range *r = xmalloc(sizeof(*r));\n+\t\t\tDIFF_LINE_RANGE_INIT(r);\n+\t\t\tr->next = range;\n+\t\t\trange = r;\n+\t\t\tparse_options_next(&ctx, 1);\n+\t\t\tcontinue;\n+\t\tcase PARSE_OPT_UNKNOWN:\n+\t\t\tparse_options_next(&ctx, 1);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tparse_revision_opt(rev, &ctx, options, log_opt_usage);\n+\t}\n+parse_done:\n+\targc = parse_options_end(&ctx);\n+\n+\t/* die if '-L <range>' with no pathspec follow */\n+\tif (range->nr > 0 && range->spec == NULL) {\n+\t\tdie(\"Each -L should follow a pathspec\");\n+\t}\n+\t/* clear up the last range */\n+\tif (range->nr == 0) {\n+\t\tstruct diff_line_range *r = range->next;\n+\t\tDIFF_LINE_RANGE_CLEAR(range);\n+\t\trange = r;\n+\t}\n+\n \targc = setup_revisions(argc, argv, rev, opt);\n \n \tmemset(&w, 0, sizeof(w));\n@@ -125,6 +220,11 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \t\trev->show_decorations = 1;\n \t\tload_ref_decorations(decoration_style);\n \t}\n+\n+\t/* Test whether line level history is asked for */\n+\tif (range && range->nr > 0) {\n+\t\tsetup_line(rev, range);\n+\t}\n }\n \n /*\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144525","messageId":"1277911526-12105-6-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 06/13] export three functions from diff.c","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:19Z","receivedAt":"2010-06-30T15:25:19Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Use fill_metainfo to fill the line level diff meta data,\nemit_line to print out a line and quote_two to quote\npaths.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n diff.c |    6 +++---\n diff.h |   17 +++++++++++++++++\n 2 files changed, 20 insertions(+), 3 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 2332fa6..a370a1c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -144,7 +144,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \treturn git_color_default_config(var, value, cb);\n }\n \n-static char *quote_two(const char *one, const char *two)\n+char *quote_two(const char *one, const char *two)\n {\n \tint need_one = quote_c_style(one, NULL, NULL, 1);\n \tint need_two = quote_c_style(two, NULL, NULL, 1);\n@@ -325,7 +325,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res\n \t\tfputc('\\n', file);\n }\n \n-static void emit_line(struct diff_options *o, const char *set, const char *reset,\n+void emit_line(struct diff_options *o, const char *set, const char *reset,\n \t\t      const char *line, int len)\n {\n \temit_line_0(o, set, reset, line[0], line+1, len-1);\n@@ -2564,7 +2564,7 @@ static int similarity_index(struct diff_filepair *p)\n \treturn p->score * 100 / MAX_SCORE;\n }\n \n-static void fill_metainfo(struct strbuf *msg,\n+void fill_metainfo(struct strbuf *msg,\n \t\t\t  const char *name,\n \t\t\t  const char *other,\n \t\t\t  struct diff_filespec *one,\ndiff --git a/diff.h b/diff.h\nindex 063d10a..9676ab9 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -12,6 +12,7 @@ struct diff_queue_struct;\n struct strbuf;\n struct diff_filespec;\n struct userdiff_driver;\n+struct diff_filepair;\n \n typedef void (*change_fn_t)(struct diff_options *options,\n \t\t unsigned old_mode, unsigned new_mode,\n@@ -301,4 +302,20 @@ extern size_t fill_textconv(struct userdiff_driver *driver,\n \n extern struct userdiff_driver *get_textconv(struct diff_filespec *one);\n \n+/* some output functions line.c need */\n+extern void fill_metainfo(struct strbuf *msg,\n+\t\t\t  const char *name,\n+\t\t\t  const char *other,\n+\t\t\t  struct diff_filespec *one,\n+\t\t\t  struct diff_filespec *two,\n+\t\t\t  struct diff_options *o,\n+\t\t\t  struct diff_filepair *p,\n+\t\t\t  int *must_show_header,\n+\t\t\t  int use_color);\n+\n+extern void emit_line(struct diff_options *o, const char *set, const char *reset,\n+\t\t      const char *line, int len);\n+\n+extern char *quote_two(const char *one, const char *two);\n+\n #endif /* DIFF_H */\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144526","messageId":"1277911526-12105-7-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 07/13] add range clone functions","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:20Z","receivedAt":"2010-06-30T15:25:20Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Both single range clone and deeply clone are supported.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n line.c |   35 +++++++++++++++++++++++++++++++++++\n line.h |    4 ++++\n 2 files changed, 39 insertions(+), 0 deletions(-)\n\ndiff --git a/line.c b/line.c\nindex 0855d29..904d0c2 100644\n--- a/line.c\n+++ b/line.c\n@@ -377,6 +377,41 @@ void diff_line_range_append(struct diff_line_range *r, const char *arg)\n \tr->ranges[r->nr - 1].arg = arg;\n }\n \n+struct diff_line_range *diff_line_range_clone(struct diff_line_range *r)\n+{\n+\tstruct diff_line_range *ret = xmalloc(sizeof(*ret));\n+\n+\tDIFF_LINE_RANGE_INIT(ret);\n+\tret->ranges = xcalloc(r->nr, sizeof(struct range));\n+\tmemcpy(ret->ranges, r->ranges, sizeof(struct range) * r->nr);\n+\n+\tret->alloc = ret->nr = r->nr;\n+\n+\tret->spec = r->spec;\n+\tassert(ret->spec);\n+\tret->spec->count++;\n+\n+\treturn ret;\n+}\n+\n+struct diff_line_range *diff_line_range_clone_deeply(struct diff_line_range *r)\n+{\n+\tstruct diff_line_range *ret = NULL;\n+\tstruct diff_line_range *tmp = NULL, *prev = NULL;\n+\n+\tassert(r);\n+\tret = tmp = prev = diff_line_range_clone(r);\n+\tr = r->next;\n+\twhile (r) {\n+\t\ttmp = diff_line_range_clone(r);\n+\t\tprev->next = tmp;\n+\t\tprev = tmp;\n+\t\tr = r->next;\n+\t}\n+\n+\treturn ret;\n+}\n+\n struct diff_line_range *diff_line_range_merge(struct diff_line_range *out,\n \tstruct diff_line_range *other)\n {\ndiff --git a/line.h b/line.h\nindex 0eb03bd..885e985 100644\n--- a/line.h\n+++ b/line.h\n@@ -112,6 +112,10 @@ extern void diff_line_range_append(struct diff_line_range *r, const char *arg);\n \n extern void diff_line_range_clear(struct diff_line_range *r);\n \n+extern struct diff_line_range *diff_line_range_clone(struct diff_line_range *r);\n+\n+extern struct diff_line_range *diff_line_range_clone_deeply(struct diff_line_range *r);\n+\n extern struct diff_line_range *diff_line_range_merge(struct diff_line_range *out,\n \tstruct diff_line_range *other);\n \n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144527","messageId":"1277911526-12105-8-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 08/13] map/take range to parent","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:21Z","receivedAt":"2010-06-30T15:25:21Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"The interesting line range will be passed to all of its parents.\nFor non-merge commit, we just map_range the ranges. Generally, the\nalgorithm do:\n\n1. Run a diffcore_std to find out the pairs;\n2. Run a xdi_diff_hunks on each interesting file pair;\n3. The map_range_cb callback will be invoked for each diff hunk,\n   and in the function map_lines we will calculate the pre-image\n   range from the post-image range.\n\nFor merge commit, another take_range pass will be done except the\nabove normal map_range work. It is used to subtract each same\npart lines of the current range out. After this pass, if there is\nany line range left, this means the merge must be a non-trivial\nmerge. This is how the non-trivial merge detect work.\n\nThe algorithm that map lines from post-image to pre-image is in\nthe function map_lines. Generally, we use simple line number\ncalculation method to do the map.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n line.c     |  452 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n revision.h |    5 +-\n 2 files changed, 456 insertions(+), 1 deletions(-)\n\ndiff --git a/line.c b/line.c\nindex 904d0c2..734a3cb 100644\n--- a/line.c\n+++ b/line.c\n@@ -493,3 +493,455 @@ void setup_line(struct rev_info *rev, struct diff_line_range *r)\n \tdiff_tree_release_paths(opt);\n }\n \n+struct take_range_cb_data {\n+\tstruct diff_line_range *interesting;\n+\tstruct diff_line_range *range;\n+\tlong plno, tlno;\n+\tint diff;\n+};\n+\n+#define SCALE_FACTOR 4\n+void map_lines(long p_start, long p_end, long t_start, long t_end,\n+\t\tlong start, long end, long *o_start, long *o_end)\n+{\n+\t/*\n+\t * xdiff always assign 'same' with the last line number of two\n+\t * same ranges. When some lines are added from scratch,\n+\t * p_start = same + 1;\n+\t * p_end = same;\n+\t * so, we can understand the following condition.\n+\t */\n+\tif (p_start > p_end) {\n+\t\t*o_start = *o_end = 0;\n+\t\treturn;\n+\t}\n+\t/* A deletion */\n+\tif (t_start > t_end) {\n+\t\t*o_start = p_start;\n+\t\t*o_end = p_end;\n+\t\treturn;\n+\t}\n+\n+\tif (start == t_start && end == t_end) {\n+\t\t*o_start = p_start;\n+\t\t*o_end = p_end;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * A new strategy for lines mapping:\n+\t *\n+\t * When the pre-image is no more than 1/4 of the post-image, it\n+\t * take no sense to say what the real pre-image is, wan can just\n+\t * treat all the post-image lines as added from scratch.\n+\t */\n+\tif (SCALE_FACTOR * (p_end - p_start + 1) < (t_end - t_start + 1)) {\n+\t\t*o_start = *o_end = 0;\n+\t\treturn;\n+\t}\n+\n+\t*o_start = p_start + start - t_start;\n+\t*o_end = p_end - (t_end - end);\n+\n+\tif (*o_start > *o_end) {\n+\t\tint temp = *o_start;\n+\t\t*o_start = *o_end;\n+\t\t*o_end = temp;\n+\t}\n+\n+\tif (*o_start < p_start)\n+\t\t*o_start = p_start;\n+\tif (*o_end > p_end)\n+\t\t*o_end = p_end;\n+}\n+\n+static void map_range(struct take_range_cb_data *data, int same,\n+\t\tlong p_start, long p_end, long t_start, long t_end)\n+{\n+\tstruct range *ranges = data->interesting->ranges;\n+\tlong takens, takene, start, end;\n+\tint i = 0, out = 0, added = 0;\n+\tlong op_start = p_start, op_end = p_end, ot_start = t_start, ot_end = t_end;\n+\n+\tfor (; i < data->interesting->nr; i++) {\n+\t\tadded = 0;\n+\t\tif (t_start > ranges[i].end)\n+\t\t\tcontinue;\n+\t\tif (t_end < ranges[i].start)\n+\t\t\tbreak;\n+\n+\t\tif (t_start > ranges[i].start) {\n+\t\t\tstart = t_start;\n+\t\t\ttakens = p_start;\n+\t\t\tif (t_end >= ranges[i].end) {\n+\t\t\t\tend = ranges[i].end;\n+\t\t\t\ttakene = p_start + end - t_start;\n+\t\t\t} else {\n+\t\t\t\tend = t_end;\n+\t\t\t\ttakene = p_end;\n+\t\t\t\tout = 1;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tstart = ranges[i].start;\n+\t\t\ttakens = p_start + start - t_start;\n+\t\t\tif (t_end >= ranges[i].end) {\n+\t\t\t\tend = ranges[i].end;\n+\t\t\t\ttakene = p_start + end - t_start;\n+\t\t\t} else {\n+\t\t\t\tend = t_end;\n+\t\t\t\ttakene = p_end;\n+\t\t\t\tout = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!same) {\n+\t\t\tstruct print_pair *pair = &ranges[i].pair;\n+\t\t\tPRINT_PAIR_GROW(pair);\n+\t\t\tstruct print_range *rr = pair->ranges + pair->nr - 1;\n+\t\t\tPRINT_RANGE_INIT(rr);\n+\t\t\trr->start = start;\n+\t\t\trr->end = end;\n+\t\t\tmap_lines(op_start, op_end, ot_start, ot_end, start, end,\n+\t\t\t\t\t&takens, &takene);\n+\t\t\tif (takens == 0 && takene == 0) {\n+\t\t\t\tadded = 1;\n+\t\t\t\trr->line_added = 1;\n+\t\t\t}\n+\t\t\trr->pstart = takens;\n+\t\t\trr->pend = takene;\n+\t\t\tdata->diff = 1;\n+\t\t\tdata->interesting->diff = 1;\n+\t\t\tranges[i].diff = 1;\n+\t\t}\n+\t\tif (added) {\n+\t\t\t/* Code movement/copy detect here */\n+\t\t} else {\n+\t\t\tstruct range *added_range = diff_line_range_insert(data->range,\n+\t\t\t\t\tNULL, takens, takene);\n+\t\t\tassert(added_range);\n+\t\t\tranges[i].pstart = added_range->start;\n+\t\t\tranges[i].pend = added_range->end;\n+\t\t}\n+\n+\t\tt_start = end + 1;\n+\t\tp_start = takene + 1;\n+\n+\t\tif (out)\n+\t\t\tbreak;\n+\t}\n+}\n+\n+static void take_range(struct take_range_cb_data *data,\n+\t\tlong p_start, long p_end, long t_start, long t_end)\n+{\n+\tstruct range *ranges = data->interesting->ranges;\n+\tlong takens, takene, start, end;\n+\tint i = 0, out = 0, added = 0;\n+\n+\tfor (; i < data->interesting->nr; i++) {\n+\t\tadded = 0;\n+\t\tif (t_start > ranges[i].end)\n+\t\t\tcontinue;\n+\t\tif (t_end < ranges[i].start)\n+\t\t\tbreak;\n+\n+\t\tif (t_start > ranges[i].start) {\n+\t\t\tlong tmp = ranges[i].end;\n+\t\t\tranges[i].end = t_start - 1;\n+\t\t\tstart = t_start;\n+\t\t\ttakens = p_start;\n+\t\t\tif (t_end >= tmp) {\n+\t\t\t\tend = tmp;\n+\t\t\t\ttakene = p_start + end - t_start;\n+\t\t\t\tp_start = takene + 1;\n+\t\t\t\tt_start = end + 1;\n+\t\t\t} else {\n+\t\t\t\tend = t_end;\n+\t\t\t\ttakene = p_end;\n+\t\t\t\tdiff_line_range_insert(data->interesting, NULL,\n+\t\t\t\t\tt_end + 1, tmp);\n+\t\t\t\tout = 1;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tstart = ranges[i].start;\n+\t\t\ttakens = p_start + start - t_start;\n+\t\t\tif (t_end >= ranges[i].end) {\n+\t\t\t\tint num = data->interesting->nr - 1;\n+\t\t\t\tend = ranges[i].end;\n+\t\t\t\ttakene = p_start + end - t_start;\n+\t\t\t\tt_start = end + 1;\n+\t\t\t\tp_start = takene + 1;\n+\t\t\t\tmemmove(ranges + i, ranges + i + 1, (num - i) * sizeof(*ranges));\n+\t\t\t\tdata->interesting->nr = num;\n+\t\t\t\ti--;\n+\t\t\t} else {\n+\t\t\t\tend = t_end;\n+\t\t\t\ttakene = p_end;\n+\t\t\t\tranges[i].start = t_end + 1;\n+\t\t\t\tout = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tdiff_line_range_insert(data->range, NULL, takens, takene);\n+\n+\t\tif (out)\n+\t\t\tbreak;\n+\t}\n+}\n+\n+static void take_range_cb(void *data, long same, long p_next, long t_next)\n+{\n+\tstruct take_range_cb_data *d = data;\n+\tlong p_start = d->plno + 1, t_start = d->tlno + 1;\n+\tlong p_end = p_start + same - t_start, t_end = same;\n+\n+\t/* If one file is added from scratch, this may confuse take_range */\n+\tif (t_end >= t_start)\n+\t\ttake_range(d, p_start, p_end, t_start, t_end);\n+\td->plno = p_next;\n+\td->tlno = t_next;\n+}\n+\n+static void map_range_cb(void *data, long same, long p_next, long t_next)\n+{\n+\tstruct take_range_cb_data *d = data;\n+\n+\tlong p_start = d->plno + 1;\n+\tlong t_start = d->tlno + 1;\n+\tlong p_end = same - t_start + p_start;\n+\tlong t_end = same;\n+\n+\t/* Firstly, take the unchanged lines from child */\n+\tif (t_end >= t_start)\n+\t\tmap_range(d, 1, p_start, p_end, t_start, t_end);\n+\n+\t/* find out which lines to print */\n+\tt_start = same + 1;\n+\tp_start = d->plno + t_start - d->tlno;\n+\tmap_range(d, 0, p_start, p_next, t_start, t_next);\n+\n+\td->plno = p_next;\n+\td->tlno = t_next;\n+}\n+\n+static void assign_range_to_parent(struct rev_info *rev, struct commit *c,\n+\t\tstruct commit *p, struct diff_line_range *r,\n+\t\tstruct diff_options *opt, int map)\n+{\n+\tstruct diff_line_range *rr = xmalloc(sizeof(*rr));\n+\tstruct diff_line_range *cr = rr, *prev_r = rr;\n+\tstruct tree_desc desc1, desc2;\n+\tvoid *tree1 = NULL, *tree2 = NULL;\n+\tunsigned long size1, size2;\n+\tstruct diff_queue_struct *queue;\n+\tstruct take_range_cb_data cb = {NULL, cr, 0, 0};\n+\txpparam_t xpp;\n+\txdemitconf_t xecfg;\n+\tint i, diff = 0;\n+\txdiff_emit_hunk_consume_fn fn = map ? map_range_cb : take_range_cb;\n+\n+\tDIFF_LINE_RANGE_INIT(cr);\n+\tmemset(&xpp, 0, sizeof(xpp));\n+\tmemset(&xecfg, 0, sizeof(xecfg));\n+\txpp.flags = XDF_NEED_MINIMAL;\n+\txecfg.ctxlen = xecfg.interhunkctxlen = 0;\n+\n+\t/*\n+\t * Compose up two trees, for root commit, we make up a empty tree.\n+\t */\n+\tassert(c);\n+\ttree2 = read_object_with_reference(c->tree->object.sha1, \"tree\", &size2, NULL);\n+\tif (tree2 == NULL)\n+\t\tdie(\"Unable to read tree (%s)\", sha1_to_hex(c->tree->object.sha1));\n+\tinit_tree_desc(&desc2, tree2, size2);\n+\tif (p) {\n+\t\ttree1 = read_object_with_reference(p->tree->object.sha1, \"tree\", &size1, NULL);\n+\t\tif (tree1 == NULL)\n+\t\t\tdie(\"Unable to read tree (%s)\", sha1_to_hex(p->tree->object.sha1));\n+\t\tinit_tree_desc(&desc1, tree1, size1);\n+\t} else {\n+\t\tinit_tree_desc(&desc1, \"\", 0);\n+\t}\n+\n+\tDIFF_QUEUE_CLEAR(&diff_queued_diff);\n+\tdiff_tree(&desc1, &desc2, \"\", opt);\n+\tdiffcore_std(opt);\n+\n+\tqueue = &diff_queued_diff;\n+\tfor (i = 0; i < queue->nr; i++) {\n+\t\tstruct diff_filepair *pair = queue->queue[i];\n+\t\tstruct diff_line_range *rg = r;\n+\t\tmmfile_t file_p, file_t;\n+\t\twhile (rg) {\n+\t\t\tassert(rg->spec->path && pair->two->path);\n+\t\t\tif (!strcmp(rg->spec->path, pair->two->path))\n+\t\t\t\tbreak;\n+\t\t\trg = rg->next;\n+\t\t}\n+\n+\t\tif (rg == NULL)\n+\t\t\tcontinue;\n+\t\trg->touch = 1;\n+\t\tif (rg->nr == 0)\n+\t\t\tcontinue;\n+\n+\t\trg->status = pair->status;\n+\t\tassert(pair->two->sha1_valid);\n+\t\tdiff_populate_filespec(pair->two, 0);\n+\t\tfile_t.ptr = pair->two->data;\n+\t\tfile_t.size = pair->two->size;\n+\n+\t\tif (rg->prev)\n+\t\t\tfree_filespec(rg->prev);\n+\t\trg->prev = pair->one;\n+\t\trg->prev->count++;\n+\t\tif (pair->one->sha1_valid) {\n+\t\t\tdiff_populate_filespec(pair->one, 0);\n+\t\t\tfile_p.ptr = pair->one->data;\n+\t\t\tfile_p.size = pair->one->size;\n+\t\t} else {\n+\t\t\tfile_p.ptr = \"\";\n+\t\t\tfile_p.size = 0;\n+\t\t}\n+\n+\t\tif (cr->nr != 0) {\n+\t\t\tstruct diff_line_range *tmp = xmalloc(sizeof(*tmp));\n+\t\t\tcr->next = tmp;\n+\t\t\tprev_r = cr;\n+\t\t\tcr = tmp;\n+\t\t} else if (cr->spec)\n+\t\t\tDIFF_LINE_RANGE_CLEAR(cr);\n+\n+\t\tDIFF_LINE_RANGE_INIT(cr);\n+\t\tif (pair->one->sha1_valid) {\n+\t\t\tcr->spec = pair->one;\n+\t\t\tcr->spec->count++;\n+\t\t}\n+\n+\t\tcb.interesting = rg;\n+\t\tcb.range = cr;\n+\t\tcb.diff = 0;\n+\t\tcb.plno = cb.tlno = 0;\n+\t\txdi_diff_hunks(&file_p, &file_t, fn, &cb, &xpp, &xecfg);\n+\t\tif (cb.diff)\n+\t\t\tdiff = 1;\n+\t\t/*\n+\t\t * The remain part is the same part.\n+\t\t * Instead of calculating the true line number of the two files,\n+\t\t * use the biggest integer.\n+\t\t */\n+\t\tif (map)\n+\t\t\tmap_range(&cb, 1, cb.plno + 1, 0x7FFFFFFF, cb.tlno + 1, 0x7FFFFFFF);\n+\t\telse\n+\t\t\ttake_range(&cb, cb.plno + 1, 0x7FFFFFFF, cb.tlno + 1, 0x7FFFFFFF);\n+\t}\n+\topt->output_format = DIFF_FORMAT_NO_OUTPUT;\n+\tdiff_flush(opt);\n+\n+\t/* Collect the untouch ranges, this comes from the files not changed\n+\t * between two commit.\n+\t */\n+\tstruct diff_line_range *rg = r;\n+\twhile (rg) {\n+\t\t/* clear the touch one to make it usable in next round */\n+\t\tif (rg->touch) {\n+\t\t\trg->touch = 0;\n+\t\t} else {\n+\t\t\tstruct diff_line_range *untouch = diff_line_range_clone(rg);\n+\t\t\tif (prev_r == rr && rr->nr == 0) {\n+\t\t\t\trr = prev_r = untouch;\n+\t\t\t} else {\n+\t\t\t\tprev_r->next = untouch;\n+\t\t\t\tprev_r = untouch;\n+\t\t\t}\n+\t\t}\n+\t\trg = rg->next;\n+\t}\n+\n+\tif (cr->nr == 0) {\n+\t\tDIFF_LINE_RANGE_CLEAR(cr);\n+\t\tfree(cr);\n+\t\tif (prev_r == cr) {\n+\t\t\trr = NULL;\n+\t\t} else {\n+\t\t\tprev_r->next = NULL;\n+\t\t}\n+\t}\n+\n+\tif (rr) {\n+\t\tassert(p);\n+\t\tadd_line_range(rev, p, rr);\n+\t}\n+\n+\t/* and the ranges of current commit c is updated */\n+\tc->object.flags &= ~RANGE_UPDATE;\n+\tif (diff)\n+\t\tc->object.flags |= NEED_PRINT;\n+\n+\tif (tree1)\n+\t\tfree(tree1);\n+\tif (tree2)\n+\t\tfree(tree2);\n+}\n+\n+static void diff_update_parent_range(struct rev_info *rev, struct commit *commit)\n+{\n+\tstruct diff_line_range *r = lookup_line_range(rev, commit);\n+\tstruct commit_list *parents = commit->parents;\n+\tstruct commit *c = NULL;\n+\tif (parents) {\n+\t\tassert(parents->next == NULL);\n+\t\tc = parents->item;\n+\t}\n+\n+\tassign_range_to_parent(rev, commit, c, r, &rev->diffopt, 1);\n+}\n+\n+static void assign_parents_range(struct rev_info *rev, struct commit *commit)\n+{\n+\tstruct commit_list *parents = commit->parents;\n+\tstruct diff_line_range *r = lookup_line_range(rev, commit);\n+\tstruct diff_line_range *copy = NULL, *range = NULL;\n+\tint nontrivial = 0;\n+\n+\t/*\n+\t * If we are in linear history, update range and flush the patch if\n+\t * necessary\n+\t */\n+\tif (parents == NULL || parents->next == NULL) {\n+\t\treturn diff_update_parent_range(rev, commit);\n+\t}\n+\n+\t/*\n+\t * Loop on the parents and assign the ranges to different\n+\t * parents, if there is any range left, this commit must\n+\t * be an evil merge.\n+\t */\n+\tcopy = diff_line_range_clone_deeply(r);\n+\tparents = commit->parents;\n+\twhile (parents) {\n+\t\tstruct commit *p = parents->item;\n+\t\tassign_range_to_parent(rev, commit, p, r, &rev->diffopt, 1);\n+\t\tassign_range_to_parent(rev, commit, p, copy, &rev->diffopt, 0);\n+\t\tparents = parents->next;\n+\t}\n+\n+\t/*\n+\t * yes, this must be an evil merge.\n+\t */\n+\trange = copy;\n+\twhile (range) {\n+\t\tif (range->nr) {\n+\t\t\tcommit->object.flags |= NEED_PRINT | EVIL_MERGE;\n+\t\t\tnontrivial = 1;\n+\t\t}\n+\t\trange = range->next;\n+\t}\n+\n+\tif (nontrivial) {\n+\t\tadd_decoration(&rev->nontrivial_merge, &commit->object, copy);\n+\t} else {\n+\t\tcleanup(copy);\n+\t}\n+}\n+\ndiff --git a/revision.h b/revision.h\nindex 7b01277..32bde08 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -15,7 +15,9 @@\n #define ADDED\t\t(1u<<7)\t/* Parents already parsed and added? */\n #define SYMMETRIC_LEFT\t(1u<<8)\n #define RANGE_UPDATE\t(1u<<9) /* for line level traverse */\n-#define ALL_REV_FLAGS\t((1u<<10)-1)\n+#define NEED_PRINT\t(1u<<10)\n+#define EVIL_MERGE\t(1u<<11)\n+#define ALL_REV_FLAGS\t((1u<<12)-1)\n \n #define DECORATE_SHORT_REFS\t1\n #define DECORATE_FULL_REFS\t2\n@@ -137,6 +139,7 @@ struct rev_info {\n \n \t/* line-level intersting range */\n \tstruct decoration line_range;\n+\tstruct decoration nontrivial_merge;\n };\n \n #define REV_TREE_SAME\t\t0\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144528","messageId":"1277911526-12105-9-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 09/13] print the line log","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:22Z","receivedAt":"2010-06-30T15:25:22Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"'struct line_chunk' is used to make sure each file is scaned\nonly once when printing the lines. We trace the line number and\nthe start position of intermediate line in this struct.\nTwo helper functions from diff.c are used to generate the\nconsistent format of diff meta info.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n line.c |  245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 245 insertions(+), 0 deletions(-)\n\ndiff --git a/line.c b/line.c\nindex 734a3cb..973eff5 100644\n--- a/line.c\n+++ b/line.c\n@@ -945,3 +945,248 @@ static void assign_parents_range(struct rev_info *rev, struct commit *commit)\n \t}\n }\n \n+struct line_chunk {\n+\tint lone, ltwo;\n+\tconst char *one, *two;\n+\tconst char *one_end, *two_end;\n+\tstruct diff_line_range *range;\n+};\n+\n+static void flush_lines(struct diff_options *opt, const char **ptr, const char *end,\n+\t\tint slno, int elno, int *lno, const char *color, const char heading)\n+{\n+\tconst char *p = *ptr;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *reset;\n+\n+\tif (strcmp(color, \"\"))\n+\t\treset = \"\";\n+\telse\n+\t\treset = diff_get_color_opt(opt, DIFF_RESET);\n+\n+\tstrbuf_addf(&buf, \"%s%c\", color, heading);\n+\twhile (*ptr < end && *lno < slno) {\n+\t\tif (**ptr == '\\n') {\n+\t\t\t(*lno)++;\n+\t\t\tif (*lno == slno) {\n+\t\t\t\t(*ptr)++;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\t(*ptr)++;\n+\t}\n+\tassert(*ptr <= end);\n+\tp = *ptr;\n+\n+\t/*\n+\t * todo: when --graph landed on master, this should be changed\n+\t * a little.\n+\t */\n+\twhile (*ptr < end && *lno <= elno) {\n+\t\tif (**ptr == '\\n') {\n+\t\t\tfprintf(opt->file, \"%s\", buf.buf);\n+\t\t\tif (*ptr - p) {\n+\t\t\t\tfwrite(p, *ptr - p, 1, opt->file);\n+\t\t\t}\n+\t\t\tfprintf(opt->file, \"%s\\n\", reset);\n+\t\t\tp = *ptr + 1;\n+\t\t\t(*lno)++;\n+\t\t}\n+\t\t(*ptr)++;\n+\t}\n+\tif (*lno <= elno) {\n+\t\tfprintf(opt->file, \"%s\", buf.buf);\n+\t\tif (*ptr - p) {\n+\t\t\tfwrite(p, *ptr - p, 1, opt->file);\n+\t\t}\n+\t\tfprintf(opt->file, \"%s\\n\", reset);\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n+static void diff_flush_range(struct diff_options *opt, struct line_chunk *chunk,\n+\t\tstruct range *range)\n+{\n+\tstruct print_pair *pair = &range->pair;\n+\tconst char *old = diff_get_color_opt(opt, DIFF_FILE_OLD);\n+\tconst char *new = diff_get_color_opt(opt, DIFF_FILE_NEW);\n+\tint i, cur = range->start;\n+\n+\tfor (i = 0; i < pair->nr; i++) {\n+\t\tstruct print_range *pr = pair->ranges + i;\n+\t\tif (cur < pr->start)\n+\t\t\tflush_lines(opt, &chunk->two, chunk->two_end,\n+\t\t\t\tcur, pr->start - 1, &chunk->ltwo, \"\", ' ');\n+\n+\t\tif (!pr->line_added)\n+\t\t\tflush_lines(opt, &chunk->one, chunk->one_end,\n+\t\t\t\tpr->pstart, pr->pend, &chunk->lone, old, '-');\n+\t\tflush_lines(opt, &chunk->two, chunk->two_end,\n+\t\t\tpr->start, pr->end, &chunk->ltwo, new, '+');\n+\n+\t\tcur = pr->end + 1;\n+\t}\n+\n+\tif (cur <= range->end) {\n+\t\tflush_lines(opt, &chunk->two, chunk->two_end,\n+\t\t\tcur, range->end, &chunk->ltwo, \"\", ' ');\n+\t}\n+}\n+\n+static void diff_flush_chunks(struct diff_options *opt, struct line_chunk *chunk)\n+{\n+\tstruct diff_line_range *range = chunk->range;\n+\tconst char *set = diff_get_color_opt(opt, DIFF_FRAGINFO);\n+\tconst char *reset = diff_get_color_opt(opt, DIFF_RESET);\n+\tint i;\n+\n+\tfor (i = 0; i < range->nr; i++) {\n+\t\tstruct range *r = range->ranges + i;\n+\t\tlong lenp = r->pend - r->pstart + 1, pstart = r->pstart;\n+\t\tlong len = r->end - r->start + 1;\n+\t\tif (pstart == 0)\n+\t\t\tlenp = 0;\n+\n+\t\tfprintf(opt->file, \"%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n+\t\t\tset, pstart, lenp, r->start, len, reset);\n+\n+\t\tdiff_flush_range(opt, chunk, r);\n+\t}\n+}\n+\n+static void diff_flush_filepair(struct rev_info *rev, struct diff_line_range *range)\n+{\n+\tstruct diff_options *opt = &rev->diffopt;\n+\tstruct diff_filespec *one = range->prev, *two = range->spec;\n+\tstruct diff_filepair p = {one, two, range->status, 0};\n+\tstruct strbuf header = STRBUF_INIT, meta = STRBUF_INIT;\n+\tconst char *a_prefix, *b_prefix;\n+\tconst char *name_a, *name_b, *a_one, *b_two;\n+\tconst char *lbl[2];\n+\tconst char *set = diff_get_color_opt(opt, DIFF_METAINFO);\n+\tconst char *reset = diff_get_color_opt(opt, DIFF_RESET);\n+\tstruct line_chunk chunk;\n+\tint must_show_header;\n+\n+\t/*\n+\t * the ranges that touch no different file, in this case\n+\t * the line number will not change, and of course we have\n+\t * no sensible rang->pair since there is no diff run.\n+\t */\n+\tif (one == NULL) {\n+\t\treturn;\n+\t}\n+\n+\tif (range->status == DIFF_STATUS_DELETED)\n+\t\tdie(\"We are following an nonexistent file, interesting!\");\n+\n+\tname_a  = one->path;\n+\tname_b = two->path;\n+\tfill_metainfo(&meta, name_a, name_b, one, two, opt, &p, &must_show_header,\n+\t\t\tDIFF_OPT_TST(opt, COLOR_DIFF));\n+\n+\tdiff_set_mnemonic_prefix(opt, \"a/\", \"b/\");\n+\tif (DIFF_OPT_TST(opt, REVERSE_DIFF)) {\n+\t\ta_prefix = opt->b_prefix;\n+\t\tb_prefix = opt->a_prefix;\n+\t} else {\n+\t\ta_prefix = opt->a_prefix;\n+\t\tb_prefix = opt->b_prefix;\n+\t}\n+\n+\tname_a = DIFF_FILE_VALID(one) ? name_a : name_b;\n+\tname_b = DIFF_FILE_VALID(two) ? name_b : name_a;\n+\n+\ta_one = quote_two(a_prefix, name_a + (*name_a == '/'));\n+\tb_two = quote_two(b_prefix, name_b + (*name_b == '/'));\n+\tlbl[0] = DIFF_FILE_VALID(one) ? a_one : \"/dev/null\";\n+\tlbl[1] = DIFF_FILE_VALID(two) ? b_two : \"/dev/null\";\n+\tstrbuf_addf(&header, \"%sdiff --git %s %s%s\\n\", set, a_one, b_two, reset);\n+\tif (lbl[0][0] == '/') {\n+\t\tstrbuf_addf(&header, \"%snew file mode %06o%s\\n\", set, two->mode, reset);\n+\t} else if (lbl[1][0] == '/') {\n+\t\tstrbuf_addf(&header, \"%sdeleted file mode %06o%s\\n\", set, one->mode, reset);\n+\t} else if (one->mode != two->mode) {\n+\t\t\tstrbuf_addf(&header, \"%sold mode %06o%s\\n\", set, one->mode, reset);\n+\t\t\tstrbuf_addf(&header, \"%snew mode %06o%s\\n\", set, two->mode, reset);\n+\t}\n+\n+\tfprintf(opt->file, \"%s%s\", header.buf, meta.buf);\n+\tstrbuf_release(&meta);\n+\tstrbuf_release(&header);\n+\tfprintf(opt->file, \"%s--- %s%s\\n\", set, lbl[0], reset);\n+\tfprintf(opt->file, \"%s+++ %s%s\\n\", set, lbl[1], reset);\n+\tfree((void *)a_one);\n+\tfree((void *)b_two);\n+\n+\tchunk.one = one->data;\n+\tchunk.one_end = one->data + one->size;\n+\tchunk.lone = 1;\n+\tchunk.two = two->data;\n+\tchunk.two_end = two->data + two->size;\n+\tchunk.ltwo = 1;\n+\tchunk.range = range;\n+\tdiff_flush_chunks(&rev->diffopt, &chunk);\n+}\n+\n+#define EVIL_MERGE_STR \"nontrivial merge found\"\n+static void flush_nontrivial_merge(struct rev_info *rev, struct diff_line_range *range)\n+{\n+\tstruct diff_options *opt = &rev->diffopt;\n+\tconst char *reset = diff_get_color_opt(opt, DIFF_RESET);\n+\tconst char *frag = diff_get_color_opt(opt, DIFF_FRAGINFO);\n+\tconst char *meta = diff_get_color_opt(opt, DIFF_METAINFO);\n+\tconst char *new = diff_get_color_opt(opt, DIFF_FILE_NEW);\n+\n+\tfprintf(opt->file, \"%s%s%s\\n\", meta, EVIL_MERGE_STR, reset);\n+\n+\twhile (range) {\n+\t\tif (range->nr) {\n+\t\t\tfprintf(opt->file, \"%s%s%s\\n\\n\", meta, range->spec->path, reset);\n+\t\t\tint lno = 1;\n+\t\t\tconst char *ptr = range->spec->data;\n+\t\t\tconst char *end = range->spec->data + range->spec->size;\n+\t\t\tint i = 0;\n+\t\t\tfor (; i < range->nr; i++) {\n+\t\t\t\tstruct range *r = range->ranges + i;\n+\t\t\t\tfprintf(opt->file, \"%s@@ %ld,%ld @@%s\\n\", frag, r->start,\n+\t\t\t\t\tr->end - r->start + 1, reset);\n+\t\t\t\tflush_lines(opt, &ptr, end, r->start, r->end,\n+\t\t\t\t\t&lno, new, ' ');\n+\t\t\t}\n+\t\t\tfprintf(opt->file, \"\\n\");\n+\t\t}\n+\t\trange = range->next;\n+\t}\n+}\n+\n+static void line_log_flush(struct rev_info *rev, struct commit *c)\n+{\n+\tstruct diff_line_range *range = lookup_line_range(rev, c);\n+\tstruct diff_line_range *nontrivial = lookup_decoration(&rev->nontrivial_merge, &c->object);\n+\tstruct log_info log;\n+\n+\tif (range == NULL)\n+\t\treturn;\n+\n+\tlog.commit = c;\n+\tlog.parent = NULL;\n+\trev->loginfo = &log;\n+\tshow_log(rev);\n+\trev->loginfo = NULL;\n+\t/*\n+\t * Add a new line after each commit message, of course we should\n+\t * add --graph alignment later when the patches comes to master.\n+\t */\n+\tfprintf(rev->diffopt.file, \"\\n\");\n+\n+\tif (c->object.flags & EVIL_MERGE)\n+\t\treturn flush_nontrivial_merge(rev, nontrivial);\n+\n+\twhile (range) {\n+\t\tif (range->diff)\n+\t\t\tdiff_flush_filepair(rev, range);\n+\t\trange = range->next;\n+\t}\n+}\n+\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144529","messageId":"1277911526-12105-10-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 10/13] map/print ranges along traversing the history topologically","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:23Z","receivedAt":"2010-06-30T15:25:23Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Since ranges may change in different branches, we should\nmake sure we do not pass range to parent until all the\nranges get 'combined' at the commit which is a split commit.\nSo, topological traversing is necessary.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n builtin/log.c |    5 ++++-\n line.c        |   52 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n line.h        |    2 ++\n 3 files changed, 58 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex d0faf07..f14d54a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -612,7 +612,10 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \tmemset(&opt, 0, sizeof(opt));\n \topt.def = \"HEAD\";\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\tif (rev.line)\n+\t\treturn cmd_line_log_walk(&rev);\n+\telse\n+\t\treturn cmd_log_walk(&rev);\n }\n \n /* format-patch */\ndiff --git a/line.c b/line.c\nindex 973eff5..faaa25a 100644\n--- a/line.c\n+++ b/line.c\n@@ -1190,3 +1190,55 @@ static void line_log_flush(struct rev_info *rev, struct commit *c)\n \t}\n }\n \n+int cmd_line_log_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct commit_list *list = NULL;\n+\tstruct diff_line_range *r = NULL;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(\"revision walk prepare failed\");\n+\n+\tlist = rev->commits;\n+\tif (list) {\n+\t\tlist->item->object.flags |= RANGE_UPDATE;\n+\t\tlist = list->next;\n+\t}\n+\t/* Clear the flags */\n+\twhile (list) {\n+\t\tlist->item->object.flags &= 0x0;\n+\t\tlist = list->next;\n+\t}\n+\n+\tlist = rev->commits;\n+\twhile (list) {\n+\t\tcommit = list->item;\n+\n+\t\tif (commit->object.flags & RANGE_UPDATE) {\n+\t\t\tassign_parents_range(rev, commit);\n+\t\t}\n+\n+\t\tif (commit->object.flags & NEED_PRINT) {\n+\t\t\tline_log_flush(rev, commit);\n+\t\t}\n+\n+\t\tr = lookup_line_range(rev, commit);\n+\t\tif (r) {\n+\t\t\tcleanup(r);\n+\t\t\tr = NULL;\n+\t\t\tadd_line_range(rev, commit, r);\n+\t\t}\n+\n+\t\tr = lookup_decoration(&rev->nontrivial_merge, &commit->object);\n+\t\tif (r) {\n+\t\t\tcleanup(r);\n+\t\t\tr = NULL;\n+\t\t\tadd_decoration(&rev->nontrivial_merge, &commit->object, r);\n+\t\t}\n+\n+\t\tlist = list->next;\n+\t}\n+\n+\treturn 0;\n+}\n+\ndiff --git a/line.h b/line.h\nindex 885e985..b293894 100644\n--- a/line.h\n+++ b/line.h\n@@ -128,4 +128,6 @@ extern struct diff_line_range *lookup_line_range(struct rev_info *revs, struct c\n const char *parse_loc(const char *spec, nth_line_fn_t nth_line,\n \t\tvoid *data, long lines, long begin, long *ret);\n \n+extern int cmd_line_log_walk(struct rev_info *rev);\n+\n #endif\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144530","messageId":"1277911526-12105-11-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 11/13] add --always-print option","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:24Z","receivedAt":"2010-06-30T15:25:24Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Always print the interesting ranges even if the current\ncommit does not change any line of it.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n builtin/log.c |    3 +++\n line.c        |   11 +++++++++--\n revision.h    |    3 ++-\n 3 files changed, 14 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f14d54a..a856f3c 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -85,6 +85,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n {\n \tint i;\n \tint decoration_given = 0;\n+\tstatic int always_print = 0;\n \tstruct userformat_want w;\n \tconst char *path = NULL, *pathspec = NULL;\n \tstatic struct diff_line_range *range = NULL;\n@@ -92,6 +93,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tstatic struct line_opt_callback_data line_cb = {&range, &ctx, NULL};\n \tstatic const struct option options[] = {\n \t\tOPT_CALLBACK('L', NULL, &line_cb, \"n,m\", \"Process only line range n,m, counting from 1\", log_line_range_callback),\n+\t\tOPT_BOOLEAN(0, \"always-print\", &always_print, \"Always print the interesting range even if the current commit does not change any line of it\"),\n \t\tOPT_END()\n \t};\n \n@@ -224,6 +226,7 @@ parse_done:\n \t/* Test whether line level history is asked for */\n \tif (range && range->nr > 0) {\n \t\tsetup_line(rev, range);\n+\t\trev->always_print = always_print;\n \t}\n }\n \ndiff --git a/line.c b/line.c\nindex faaa25a..eb7375d 100644\n--- a/line.c\n+++ b/line.c\n@@ -1074,6 +1074,13 @@ static void diff_flush_filepair(struct rev_info *rev, struct diff_line_range *ra\n \t * no sensible rang->pair since there is no diff run.\n \t */\n \tif (one == NULL) {\n+\t\tif (rev->always_print) {\n+\t\t\tchunk.two = two->data;\n+\t\t\tchunk.two_end = two->data + two->size;\n+\t\t\tchunk.ltwo = 1;\n+\t\t\tchunk.range = range;\n+\t\t\tdiff_flush_chunks(&rev->diffopt, &chunk);\n+\t\t}\n \t\treturn;\n \t}\n \n@@ -1184,7 +1191,7 @@ static void line_log_flush(struct rev_info *rev, struct commit *c)\n \t\treturn flush_nontrivial_merge(rev, nontrivial);\n \n \twhile (range) {\n-\t\tif (range->diff)\n+\t\tif (range->diff || (range->nr && rev->always_print))\n \t\t\tdiff_flush_filepair(rev, range);\n \t\trange = range->next;\n \t}\n@@ -1218,7 +1225,7 @@ int cmd_line_log_walk(struct rev_info *rev)\n \t\t\tassign_parents_range(rev, commit);\n \t\t}\n \n-\t\tif (commit->object.flags & NEED_PRINT) {\n+\t\tif (commit->object.flags & NEED_PRINT || rev->always_print) {\n \t\t\tline_log_flush(rev, commit);\n \t\t}\n \ndiff --git a/revision.h b/revision.h\nindex 32bde08..3d25fd0 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -71,7 +71,8 @@ struct rev_info {\n \t\t\tbisect:1,\n \t\t\tancestry_path:1,\n \t\t\tfirst_parent_only:1,\n-\t\t\tline:1;\n+\t\t\tline:1,\n+\t\t\talways_print:1;\n \n \t/* Diff flags */\n \tunsigned int\tdiff:1,\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144531","messageId":"1277911526-12105-12-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 12/13] add two test cases","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:25Z","receivedAt":"2010-06-30T15:25:25Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"t4301: for simple linear history only\nt4302: for history contains merge\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n t/t4301-log-line-single-history.sh |  342 ++++++++++++++++++++++++++++++++++++\n t/t4302-log-line-merge-history.sh  |  118 +++++++++++++\n 2 files changed, 460 insertions(+), 0 deletions(-)\n create mode 100755 t/t4301-log-line-single-history.sh\n create mode 100755 t/t4302-log-line-merge-history.sh\n\ndiff --git a/t/t4301-log-line-single-history.sh b/t/t4301-log-line-single-history.sh\nnew file mode 100755\nindex 0000000..9981496\n--- /dev/null\n+++ b/t/t4301-log-line-single-history.sh\n@@ -0,0 +1,342 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Bo Yang\n+#\n+\n+test_description='Test git log -L with single line of history\n+\n+'\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/diff-lib.sh\n+\n+echo >path0 'void func(){\n+\tint a = 0;\n+\tint b = 1;\n+\tint c;\n+\tc = a + b;\n+}\n+'\n+\n+echo >path1 'void output(){\n+\tprintf(\"hello world\");\n+}\n+'\n+\n+test_expect_success \\\n+    'add path0/path1 and commit.' \\\n+    'git add path0 path1 &&\n+     git commit -m \"Base commit\"'\n+\n+echo >path0 'void func(){\n+\tint a = 10;\n+\tint b = 11;\n+\tint c;\n+\tc = a + b;\n+}\n+'\n+\n+echo >path1 'void output(){\n+\tconst char *str = \"hello world!\";\n+\tprintf(\"%s\", str);\n+}\n+'\n+\n+test_expect_success \\\n+    'Change the 2,3 lines of path0 and path1.' \\\n+    'git add path0 path1 &&\n+     git commit -m \"Change 2,3 lines of path0 and path1\"'\n+\n+echo >path0 'void func(){\n+\tint a = 10;\n+\tint b = 11;\n+\tint c;\n+\tc = 10 * (a + b);\n+}\n+'\n+\n+test_expect_success \\\n+\t'Change the 5th line of path0.' \\\n+\t'git add path0 &&\n+\t git commit -m \"Change the 5th line of path0\"'\n+\n+echo >path0 'void func(){\n+\tint a = 10;\n+\tint b = 11;\n+\tprintf(\"%d\", a - b);\n+}\n+'\n+\n+test_expect_success \\\n+\t'Final change of path0.' \\\n+\t'git add path0 &&\n+\t git commit -m \"Final change of path0\"'\n+\n+test_expect_success \\\n+    'Show the line level log of path0' \\\n+    'git log --pretty=format:%s%n%b -L /func/,/^}/ path0 > current-path0'\n+\n+test_expect_success \\\n+    'Show the line level log of path1' \\\n+    'git log --pretty=format:%s%n%b -L /output/,/^}/ path1 > current-path1'\n+\n+test_expect_success \\\n+\t'Show the line level log of two files' \\\n+    'git log --pretty=format:%s%n%b -L /func/,/^}/ path0 -L /output/,/^}/ path1 > current-pathall'\n+\n+test_expect_success \\\n+\t'Test the line number argument' \\\n+\t'git log --pretty=format:%s%n%b -L 1,2 path0 > current-linenum'\n+\n+test_expect_success \\\n+\t'Test the --always-print option' \\\n+\t'git log --pretty=format:%s%n%b --always-print -L 1,2 path0 > current-always'\n+\n+cat >expected-path0 <<\\EOF\n+Final change of path0\n+\n+diff --git a/path0 b/path0\n+index 44db133..1518c15 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,6 +1,5 @@\n+ void func(){\n+ \tint a = 10;\n+ \tint b = 11;\n+-\tint c;\n+-\tc = 10 * (a + b);\n++\tprintf(\"%d\", a - b);\n+ }\n+\n+Change the 5th line of path0\n+\n+diff --git a/path0 b/path0\n+index 9ef1692..44db133 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,6 +1,6 @@\n+ void func(){\n+ \tint a = 10;\n+ \tint b = 11;\n+ \tint c;\n+-\tc = a + b;\n++\tc = 10 * (a + b);\n+ }\n+\n+Change 2,3 lines of path0 and path1\n+\n+diff --git a/path0 b/path0\n+index aabffdf..9ef1692 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,6 +1,6 @@\n+ void func(){\n+-\tint a = 0;\n+-\tint b = 1;\n++\tint a = 10;\n++\tint b = 11;\n+ \tint c;\n+ \tc = a + b;\n+ }\n+\n+Base commit\n+\n+diff --git a/path0 b/path0\n+new file mode 100644\n+index 0000000..aabffdf\n+--- /dev/null\n++++ b/path0\n+@@ -0,0 +1,6 @@\n++void func(){\n++\tint a = 0;\n++\tint b = 1;\n++\tint c;\n++\tc = a + b;\n++}\n+EOF\n+\n+cat >expected-path1 <<\\EOF\n+Change 2,3 lines of path0 and path1\n+\n+diff --git a/path1 b/path1\n+index 997d841..1d711b5 100644\n+--- a/path1\n++++ b/path1\n+@@ -1,3 +1,4 @@\n+ void output(){\n+-\tprintf(\"hello world\");\n++\tconst char *str = \"hello world!\";\n++\tprintf(\"%s\", str);\n+ }\n+\n+Base commit\n+\n+diff --git a/path1 b/path1\n+new file mode 100644\n+index 0000000..997d841\n+--- /dev/null\n++++ b/path1\n+@@ -0,0 +1,3 @@\n++void output(){\n++\tprintf(\"hello world\");\n++}\n+EOF\n+\n+cat >expected-pathall <<\\EOF\n+Final change of path0\n+\n+diff --git a/path0 b/path0\n+index 44db133..1518c15 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,6 +1,5 @@\n+ void func(){\n+ \tint a = 10;\n+ \tint b = 11;\n+-\tint c;\n+-\tc = 10 * (a + b);\n++\tprintf(\"%d\", a - b);\n+ }\n+\n+Change the 5th line of path0\n+\n+diff --git a/path0 b/path0\n+index 9ef1692..44db133 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,6 +1,6 @@\n+ void func(){\n+ \tint a = 10;\n+ \tint b = 11;\n+ \tint c;\n+-\tc = a + b;\n++\tc = 10 * (a + b);\n+ }\n+\n+Change 2,3 lines of path0 and path1\n+\n+diff --git a/path0 b/path0\n+index aabffdf..9ef1692 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,6 +1,6 @@\n+ void func(){\n+-\tint a = 0;\n+-\tint b = 1;\n++\tint a = 10;\n++\tint b = 11;\n+ \tint c;\n+ \tc = a + b;\n+ }\n+diff --git a/path1 b/path1\n+index 997d841..1d711b5 100644\n+--- a/path1\n++++ b/path1\n+@@ -1,3 +1,4 @@\n+ void output(){\n+-\tprintf(\"hello world\");\n++\tconst char *str = \"hello world!\";\n++\tprintf(\"%s\", str);\n+ }\n+\n+Base commit\n+\n+diff --git a/path0 b/path0\n+new file mode 100644\n+index 0000000..aabffdf\n+--- /dev/null\n++++ b/path0\n+@@ -0,0 +1,6 @@\n++void func(){\n++\tint a = 0;\n++\tint b = 1;\n++\tint c;\n++\tc = a + b;\n++}\n+diff --git a/path1 b/path1\n+new file mode 100644\n+index 0000000..997d841\n+--- /dev/null\n++++ b/path1\n+@@ -0,0 +1,3 @@\n++void output(){\n++\tprintf(\"hello world\");\n++}\n+EOF\n+\n+cat >expected-linenum <<\\EOF\n+Change 2,3 lines of path0 and path1\n+\n+diff --git a/path0 b/path0\n+index aabffdf..9ef1692 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,2 +1,2 @@\n+ void func(){\n+-\tint a = 0;\n++\tint a = 10;\n+\n+Base commit\n+\n+diff --git a/path0 b/path0\n+new file mode 100644\n+index 0000000..aabffdf\n+--- /dev/null\n++++ b/path0\n+@@ -0,0 +1,2 @@\n++void func(){\n++\tint a = 0;\n+EOF\n+\n+cat >expected-always <<\\EOF\n+Final change of path0\n+\n+diff --git a/path0 b/path0\n+index 44db133..1518c15 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,2 +1,2 @@\n+ void func(){\n+ \tint a = 10;\n+\n+Change the 5th line of path0\n+\n+diff --git a/path0 b/path0\n+index 9ef1692..44db133 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,2 +1,2 @@\n+ void func(){\n+ \tint a = 10;\n+\n+Change 2,3 lines of path0 and path1\n+\n+diff --git a/path0 b/path0\n+index aabffdf..9ef1692 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,2 +1,2 @@\n+ void func(){\n+-\tint a = 0;\n++\tint a = 10;\n+\n+Base commit\n+\n+diff --git a/path0 b/path0\n+new file mode 100644\n+index 0000000..aabffdf\n+--- /dev/null\n++++ b/path0\n+@@ -0,0 +1,2 @@\n++void func(){\n++\tint a = 0;\n+EOF\n+\n+test_expect_success \\\n+    'validate the output.' \\\n+    'test_cmp current-path0 expected-path0 &&\n+\t test_cmp current-path1 expected-path1 &&\n+\t test_cmp current-pathall expected-pathall &&\n+\t test_cmp current-linenum expected-linenum &&\n+\t test_cmp current-always expected-always'\n+\n+test_done\ndiff --git a/t/t4302-log-line-merge-history.sh b/t/t4302-log-line-merge-history.sh\nnew file mode 100755\nindex 0000000..70ad0bf\n--- /dev/null\n+++ b/t/t4302-log-line-merge-history.sh\n@@ -0,0 +1,118 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Bo Yang\n+#\n+\n+test_description='Test git log -L with merge commit\n+\n+'\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/diff-lib.sh\n+\n+echo >path0 'void func(){\n+\tprintf(\"hello\");\n+}\n+'\n+\n+test_expect_success \\\n+    'Add path0 and commit.' \\\n+    'git add path0 &&\n+     git commit -m \"Base commit\"'\n+\n+echo >path0 'void func(){\n+\tprintf(\"hello earth\");\n+}\n+'\n+\n+test_expect_success \\\n+    'Change path0 in master.' \\\n+    'git add path0 &&\n+     git commit -m \"Change path0 in master\"'\n+\n+test_expect_success \\\n+\t'Make a new branch from the base commit' \\\n+\t'git checkout -b feature master^'\n+\n+echo >path0 'void func(){\n+\tprint(\"hello moon\");\n+}\n+'\n+\n+test_expect_success \\\n+    'Change path0 in feature.' \\\n+    'git add path0 &&\n+     git commit -m \"Change path0 in feature\"'\n+\n+test_expect_success \\\n+\t'Merge the master to feature' \\\n+\t'! git merge master'\n+\n+echo >path0 'void func(){\n+\tprintf(\"hello earth and moon\");\n+}\n+'\n+\n+test_expect_success \\\n+\t'Resolve the conflict' \\\n+\t'git add path0 &&\n+\t git commit -m \"Merge two branches\"'\n+\n+test_expect_success \\\n+    'Show the line level log of path0' \\\n+    'git log --pretty=format:%s%n%b -L /func/,/^}/ path0 > current-path0'\n+\n+test_expect_success \\\n+    'Show the line level log of path0' \\\n+    'git log --pretty=format:%s%n%b -L /func/,/^}/ path0 > current'\n+\n+cat >expected <<\\EOF\n+Merge two branches\n+\n+nontrivial merge found\n+path0\n+\n+@@ 2,1 @@\n+ \tprintf(\"hello earth and moon\");\n+\n+\n+Change path0 in master\n+\n+diff --git a/path0 b/path0\n+index f628dea..bef7fa3 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,3 +1,3 @@\n+ void func(){\n+-\tprintf(\"hello\");\n++\tprintf(\"hello earth\");\n+ }\n+\n+Change path0 in feature\n+\n+diff --git a/path0 b/path0\n+index f628dea..a940ef6 100644\n+--- a/path0\n++++ b/path0\n+@@ -1,3 +1,3 @@\n+ void func(){\n+-\tprintf(\"hello\");\n++\tprint(\"hello moon\");\n+ }\n+\n+Base commit\n+\n+diff --git a/path0 b/path0\n+new file mode 100644\n+index 0000000..f628dea\n+--- /dev/null\n++++ b/path0\n+@@ -0,0 +1,3 @@\n++void func(){\n++\tprintf(\"hello\");\n++}\n+EOF\n+test_expect_success \\\n+    'validate the output.' \\\n+    'test_cmp current expected'\n+\n+test_done\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144532","messageId":"1277911526-12105-13-git-send-email-struggleyb.nku@gmail.com","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH v2 13/13] some document update","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-06-30T15:25:26Z","receivedAt":"2010-06-30T15:25:26Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n Documentation/git-log.txt |   30 ++++++++++++++++++++++++++++++\n 1 files changed, 30 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-log.txt b/Documentation/git-log.txt\nindex 0e6ff31..14a9703 100644\n--- a/Documentation/git-log.txt\n+++ b/Documentation/git-log.txt\n@@ -9,6 +9,7 @@ git-log - Show commit logs\n SYNOPSIS\n --------\n 'git log' [<options>] [<since>..<until>] [[\\--] <path>...]\n+'git log' [<options>] -L n,m <path>\n \n DESCRIPTION\n -----------\n@@ -19,6 +20,9 @@ command to control what is shown and how, and options applicable to\n the 'git diff-*' commands to control how the changes\n each commit introduces are shown.\n \n+With '-L' option, the command will help to trace the history of user specified\n+line ranges. It can trace multiple ranges coming from multiple files.\n+\n \n OPTIONS\n -------\n@@ -72,6 +76,32 @@ include::diff-options.txt[]\n \tto be prefixed with \"\\-- \" to separate them from options or\n \trefnames.\n \n+-L <start>,<end>::\n+\tThe line range.  <start> and <end> can take one of these forms:\n+\n+- number\n++\n+If <start> or <end> is a number, it specifies an\n+absolute line number (lines count from 1).\n++\n+\n+- /regex/\n++\n+This form will use the first line matching the given\n+POSIX regex.  If <end> is a regex, it will search\n+starting at the line given by <start>.\n++\n+\n+- +offset or -offset\n++\n+This is only valid for <end> and will specify a number\n+of lines before or after the line given by <start>.\n++\n+\n+--always-print::\n+\tAlways print the interesting range even if the current commit\n+\tdoes not change any line of the range.\n+\n \n include::rev-list-options.txt[]\n \n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"144865","messageId":"7vhbkd72w1.fsf@alter.siamese.dyndns.org","threadId":"24241","inReplyTo":"1277911526-12105-1-git-send-email-struggleyb.nku@gmail.com","subject":"Re: [PATCH v2 01/13] parse-options: stop when encounter a non-option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-06T02:36:14Z","receivedAt":"2010-07-06T02:36:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Bo Yang <struggleyb.nku@gmail.com> writes:\n\n> Since we need to support the syntax like:\n> -L n1,m1 path1 -L n2,m2 path2.\n>\n> So, make the parse-options API not stop with a\n\nThe above sentence does not parse well.\n\n> non-option, but report the status and go on parsing\n> the following.\n>\n> Thanks-to: Jonathan Nieder <jrnieder@gmail.com>\n> Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>\n> ---\n>  parse-options.c |    3 ++-\n>  parse-options.h |    3 ++-\n>  2 files changed, 4 insertions(+), 2 deletions(-)\n>\n> diff --git a/parse-options.c b/parse-options.c\n> index 0fa79bc..cbb49d3 100644\n> --- a/parse-options.c\n> +++ b/parse-options.c\n> @@ -374,7 +374,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n>  \t\t\tif (parse_nodash_opt(ctx, arg, options) == 0)\n>  \t\t\t\tcontinue;\n>  \t\t\tif (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)\n> -\t\t\t\tbreak;\n> +\t\t\t\treturn PARSE_OPT_NON_OPTION;\n>  \t\t\tctx->out[ctx->cpidx++] = ctx->argv[0];\n>  \t\t\tcontinue;\n>  \t\t}\n> @@ -456,6 +456,7 @@ int parse_options(int argc, const char **argv, const char *prefix,\n>  \tswitch (parse_options_step(&ctx, options, usagestr)) {\n>  \tcase PARSE_OPT_HELP:\n>  \t\texit(129);\n> +\tcase PARSE_OPT_NON_OPTION:\n>  \tcase PARSE_OPT_DONE:\n>  \t\tbreak;\n>  \tdefault: /* PARSE_OPT_UNKNOWN */\n> diff --git a/parse-options.h b/parse-options.h\n> index 7435cdb..407697a 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n> @@ -161,7 +161,8 @@ extern NORETURN void usage_msg_opt(const char *msg,\n>  enum {\n>  \tPARSE_OPT_HELP = -1,\n>  \tPARSE_OPT_DONE,\n> -\tPARSE_OPT_UNKNOWN\n> +\tPARSE_OPT_NON_OPTION,\n> +\tPARSE_OPT_UNKNOWN,\n>  };\n>  \n>  /*\n> -- \n> 1.7.0.2.273.gc2413.dirty\n"},{"id":"144866","messageId":"7vbpal72vu.fsf@alter.siamese.dyndns.org","threadId":"24241","inReplyTo":"1277911526-12105-3-git-send-email-struggleyb.nku@gmail.com","subject":"Re: [PATCH v2 03/13] add the basic data structure for line level history","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-06T02:36:21Z","receivedAt":"2010-07-06T02:36:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This patch does not apply.   What did you base this version on top of?\n"},{"id":"144907","messageId":"AANLkTilYsybKdfHjYv7DdK2zGqdVdzImo-Pn2aDAsjxC@mail.gmail.com","threadId":"24241","inReplyTo":"7vbpal72vu.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 03/13] add the basic data structure for line level history","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-07-06T14:21:32Z","receivedAt":"2010-07-06T14:21:32Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"On Tue, Jul 6, 2010 at 10:36 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> This patch does not apply.   What did you base this version on top of?\n\nHmm, I have just fetch the newest version of current git and make a\nrebase, and I found that there is no conflict of any patch. Could you\nplease take a look that whether you are applying the the newest patch\nof this thread. Its subject is: [PATCH v2 03/13] add the basic data\nstructure for line level.\n\nAnd my log to see:\n\nd0e8b59b04c19419458fd61a9342efba8f849a82 some document update\n70188b228b459fc92b740aadbd5dac72c010cb01 add two test cases\nbcb75c23a5d46dad69439d60002624f6071ba3dc add --always-print option\n99c4697dcc5414ee3b864a7cc2c07cb25be229d9 map/print ranges along\ntraversing the history topologically\n6ccac2635a4e62c2b8129d9e2306f7e8177d9a16 print the line log\nc9b90713a844939c2dd3695b5006f83818c6309f map/take range to parent\ncdd2b5bfcf0be9021d50b0657d2692cb2538ae0c add range clone functions\n0e3bd1b8c52c5d9ed1d597e9248331ac6b484fea export three functions from diff.c\nb06f615af36a6c9e4a06406124d3fae8b2cd9a54 parse the -L options\n3a3f8ae45ebe56e3be9b2fb1c0e238dd9c284b02 refactor parse_loc\n067314e4ad65d8595ad35a6a4e954e0a3da514f1 add the basic data structure\nfor line level history\nc967805c73daf3fb88d242a5b82768f16a1f904e parse-options: add two helper functions\ne6247e51dc11cc8a133403c55d9dfe8e3ede8723 parse-options: stop when\nencounter a non-option\n22cff1daaaf3db08b6fed9c7f4c1a289f1dd5d83 Change to debug mode.\n5b5275f6e9ca6284ca123f729d5a78f409e448fb Merge branch 'maint'\n\nThe master is at 5b5275f6e9ca6284ca123f729d5a78f409e448fb. :-)\n\n--\nRegards!\nBo\n----------------------------\nMy blog: http://blog.morebits.org\nWhy Git: http://www.whygitisbetterthanx.com/\n"}]}