diff options
Diffstat (limited to 'range-diff.c')
| -rw-r--r-- | range-diff.c | 202 |
1 files changed, 140 insertions, 62 deletions
diff --git a/range-diff.c b/range-diff.c index 48b0e1b4ce..b9950f10c8 100644 --- a/range-diff.c +++ b/range-diff.c @@ -2,7 +2,7 @@ #include "range-diff.h" #include "string-list.h" #include "run-command.h" -#include "argv-array.h" +#include "strvec.h" #include "hashmap.h" #include "xdiff-interface.h" #include "linear-assignment.h" @@ -10,6 +10,7 @@ #include "commit.h" #include "pretty.h" #include "userdiff.h" +#include "apply.h" struct patch_util { /* For the search for an exact match */ @@ -24,47 +25,70 @@ struct patch_util { struct object_id oid; }; +static size_t find_end_of_line(char *buffer, unsigned long size) +{ + char *eol = memchr(buffer, '\n', size); + + if (!eol) + return size; + + *eol = '\0'; + return eol + 1 - buffer; +} + /* * Reads the patches into a string list, with the `util` field being populated * as struct object_id (will need to be free()d). */ -static int read_patches(const char *range, struct string_list *list) +static int read_patches(const char *range, struct string_list *list, + const struct strvec *other_arg) { struct child_process cp = CHILD_PROCESS_INIT; - FILE *in; - struct strbuf buf = STRBUF_INIT, line = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT; struct patch_util *util = NULL; int in_header = 1; - - argv_array_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges", - "--reverse", "--date-order", "--decorate=no", - /* - * Choose indicators that are not used anywhere - * else in diffs, but still look reasonable - * (e.g. will not be confusing when debugging) - */ - "--output-indicator-new=>", - "--output-indicator-old=<", - "--output-indicator-context=#", - "--no-abbrev-commit", range, - NULL); + char *line, *current_filename = NULL; + int offset, len; + size_t size; + + strvec_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges", + "--reverse", "--date-order", "--decorate=no", + "--no-prefix", + /* + * Choose indicators that are not used anywhere + * else in diffs, but still look reasonable + * (e.g. will not be confusing when debugging) + */ + "--output-indicator-new=>", + "--output-indicator-old=<", + "--output-indicator-context=#", + "--no-abbrev-commit", + "--pretty=medium", + "--notes", + NULL); + if (other_arg) + strvec_pushv(&cp.args, other_arg->v); + strvec_push(&cp.args, range); cp.out = -1; cp.no_stdin = 1; cp.git_cmd = 1; if (start_command(&cp)) return error_errno(_("could not start `log`")); - in = fdopen(cp.out, "r"); - if (!in) { + if (strbuf_read(&contents, cp.out, 0) < 0) { error_errno(_("could not read `log` output")); finish_command(&cp); return -1; } - while (strbuf_getline(&line, in) != EOF) { + line = contents.buf; + size = contents.len; + for (offset = 0; size > 0; offset += len, size -= len, line += len) { const char *p; - if (skip_prefix(line.buf, "commit ", &p)) { + len = find_end_of_line(line, size); + line[len - 1] = '\0'; + if (skip_prefix(line, "commit ", &p)) { if (util) { string_list_append(list, buf.buf)->util = util; strbuf_reset(&buf); @@ -75,8 +99,7 @@ static int read_patches(const char *range, struct string_list *list) free(util); string_list_clear(list, 1); strbuf_release(&buf); - strbuf_release(&line); - fclose(in); + strbuf_release(&contents); finish_command(&cp); return -1; } @@ -85,61 +108,115 @@ static int read_patches(const char *range, struct string_list *list) continue; } - if (starts_with(line.buf, "diff --git")) { + if (!util) { + error(_("could not parse first line of `log` output: " + "did not start with 'commit ': '%s'"), + line); + string_list_clear(list, 1); + strbuf_release(&buf); + strbuf_release(&contents); + finish_command(&cp); + return -1; + } + + if (starts_with(line, "diff --git")) { + struct patch patch = { 0 }; + struct strbuf root = STRBUF_INIT; + int linenr = 0; + int orig_len; + in_header = 0; strbuf_addch(&buf, '\n'); if (!util->diff_offset) util->diff_offset = buf.len; - strbuf_addch(&buf, ' '); - strbuf_addbuf(&buf, &line); + line[len - 1] = '\n'; + orig_len = len; + len = parse_git_diff_header(&root, &linenr, 0, line, + len, size, &patch); + if (len < 0) + die(_("could not parse git header '%.*s'"), + orig_len, line); + strbuf_addstr(&buf, " ## "); + if (patch.is_new > 0) + strbuf_addf(&buf, "%s (new)", patch.new_name); + else if (patch.is_delete > 0) + strbuf_addf(&buf, "%s (deleted)", patch.old_name); + else if (patch.is_rename) + strbuf_addf(&buf, "%s => %s", patch.old_name, patch.new_name); + else + strbuf_addstr(&buf, patch.new_name); + + free(current_filename); + if (patch.is_delete > 0) + current_filename = xstrdup(patch.old_name); + else + current_filename = xstrdup(patch.new_name); + + if (patch.new_mode && patch.old_mode && + patch.old_mode != patch.new_mode) + strbuf_addf(&buf, " (mode change %06o => %06o)", + patch.old_mode, patch.new_mode); + + strbuf_addstr(&buf, " ##"); } else if (in_header) { - if (starts_with(line.buf, "Author: ")) { - strbuf_addbuf(&buf, &line); + if (starts_with(line, "Author: ")) { + strbuf_addstr(&buf, " ## Metadata ##\n"); + strbuf_addstr(&buf, line); strbuf_addstr(&buf, "\n\n"); - } else if (starts_with(line.buf, " ")) { - strbuf_rtrim(&line); - strbuf_addbuf(&buf, &line); + strbuf_addstr(&buf, " ## Commit message ##\n"); + } else if (starts_with(line, "Notes") && + line[strlen(line) - 1] == ':') { + strbuf_addstr(&buf, "\n\n"); + /* strip the trailing colon */ + strbuf_addf(&buf, " ## %.*s ##\n", + (int)(strlen(line) - 1), line); + } else if (starts_with(line, " ")) { + p = line + len - 2; + while (isspace(*p) && p >= line) + p--; + strbuf_add(&buf, line, p - line + 1); strbuf_addch(&buf, '\n'); } continue; - } else if (starts_with(line.buf, "@@ ")) + } else if (skip_prefix(line, "@@ ", &p)) { + p = strstr(p, "@@"); strbuf_addstr(&buf, "@@"); - else if (!line.buf[0] || starts_with(line.buf, "index ")) + if (current_filename && p[2]) + strbuf_addf(&buf, " %s:", current_filename); + if (p) + strbuf_addstr(&buf, p + 2); + } else if (!line[0]) /* * A completely blank (not ' \n', which is context) * line is not valid in a diff. We skip it * silently, because this neatly handles the blank * separator line between commits in git-log * output. - * - * We also want to ignore the diff's `index` lines - * because they contain exact blob hashes in which - * we are not interested. */ continue; - else if (line.buf[0] == '>') { + else if (line[0] == '>') { strbuf_addch(&buf, '+'); - strbuf_add(&buf, line.buf + 1, line.len - 1); - } else if (line.buf[0] == '<') { + strbuf_addstr(&buf, line + 1); + } else if (line[0] == '<') { strbuf_addch(&buf, '-'); - strbuf_add(&buf, line.buf + 1, line.len - 1); - } else if (line.buf[0] == '#') { + strbuf_addstr(&buf, line + 1); + } else if (line[0] == '#') { strbuf_addch(&buf, ' '); - strbuf_add(&buf, line.buf + 1, line.len - 1); + strbuf_addstr(&buf, line + 1); } else { strbuf_addch(&buf, ' '); - strbuf_addbuf(&buf, &line); + strbuf_addstr(&buf, line); } strbuf_addch(&buf, '\n'); util->diffsize++; } - fclose(in); - strbuf_release(&line); + strbuf_release(&contents); if (util) string_list_append(list, buf.buf)->util = util; strbuf_release(&buf); + free(current_filename); if (finish_command(&cp)) return -1; @@ -148,18 +225,16 @@ static int read_patches(const char *range, struct string_list *list) } static int patch_util_cmp(const void *dummy, const struct patch_util *a, - const struct patch_util *b, const char *keydata) + const struct patch_util *b, const char *keydata) { return strcmp(a->diff, keydata ? keydata : b->diff); } static void find_exact_matches(struct string_list *a, struct string_list *b) { - struct hashmap map; + struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL); int i; - hashmap_init(&map, (hashmap_cmp_fn)patch_util_cmp, NULL, 0); - /* First, add the patches of a to a hash map */ for (i = 0; i < a->nr; i++) { struct patch_util *util = a->items[i].util; @@ -167,8 +242,8 @@ static void find_exact_matches(struct string_list *a, struct string_list *b) util->i = i; util->patch = a->items[i].string; util->diff = util->patch + util->diff_offset; - hashmap_entry_init(util, strhash(util->diff)); - hashmap_add(&map, util); + hashmap_entry_init(&util->e, strhash(util->diff)); + hashmap_add(&map, &util->e); } /* Now try to find exact matches in b */ @@ -178,8 +253,8 @@ static void find_exact_matches(struct string_list *a, struct string_list *b) util->i = i; util->patch = b->items[i].string; util->diff = util->patch + util->diff_offset; - hashmap_entry_init(util, strhash(util->diff)); - other = hashmap_remove(&map, util, NULL); + hashmap_entry_init(&util->e, strhash(util->diff)); + other = hashmap_remove_entry(&map, util, e, NULL); if (other) { if (other->matching >= 0) BUG("already assigned!"); @@ -189,7 +264,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b) } } - hashmap_free(&map, 0); + hashmap_clear(&map); } static void diffsize_consume(void *data, char *line, unsigned long len) @@ -354,8 +429,9 @@ static void output_pair_header(struct diff_options *diffopt, fwrite(buf->buf, buf->len, 1, diffopt->file); } -static struct userdiff_driver no_func_name = { - .funcname = { "$^", 0 } +static struct userdiff_driver section_headers = { + .funcname = { "^ ## (.*) ##$\n" + "^.?@@ (.*)$", REG_EXTENDED } }; static struct diff_filespec *get_filespec(const char *name, const char *p) @@ -367,13 +443,13 @@ static struct diff_filespec *get_filespec(const char *name, const char *p) spec->size = strlen(p); spec->should_munmap = 0; spec->is_stdin = 1; - spec->driver = &no_func_name; + spec->driver = §ion_headers; return spec; } static void patch_diff(const char *a, const char *b, - struct diff_options *diffopt) + struct diff_options *diffopt) { diff_queue(&diff_queued_diff, get_filespec("a", a), get_filespec("b", b)); @@ -444,16 +520,17 @@ static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data) int show_range_diff(const char *range1, const char *range2, int creation_factor, int dual_color, - struct diff_options *diffopt) + const struct diff_options *diffopt, + const struct strvec *other_arg) { int res = 0; struct string_list branch1 = STRING_LIST_INIT_DUP; struct string_list branch2 = STRING_LIST_INIT_DUP; - if (read_patches(range1, &branch1)) + if (read_patches(range1, &branch1, other_arg)) res = error(_("could not parse log for '%s'"), range1); - if (!res && read_patches(range2, &branch2)) + if (!res && read_patches(range2, &branch2, other_arg)) res = error(_("could not parse log for '%s'"), range2); if (!res) { @@ -469,6 +546,7 @@ int show_range_diff(const char *range1, const char *range2, opts.output_format = DIFF_FORMAT_PATCH; opts.flags.suppress_diff_headers = 1; opts.flags.dual_color_diffed_diffs = dual_color; + opts.flags.suppress_hunk_header_line_count = 1; opts.output_prefix = output_prefix_cb; strbuf_addstr(&indent, " "); opts.output_prefix_data = &indent; |
