static int reverse;
static int blank_boundary;
static int incremental;
-static int cmd_is_annotate;
static int xdl_opts = XDF_NEED_MINIMAL;
static struct string_list mailmap;
return porigin;
}
-/*
- * Parsing of patch chunks...
- */
-struct chunk {
- /* line number in postimage; up to but not including this
- * line is the same as preimage
- */
- int same;
-
- /* preimage line number after this chunk */
- int p_next;
-
- /* postimage line number after this chunk */
- int t_next;
-};
-
-struct patch {
- struct chunk *chunks;
- int num;
-};
-
-struct blame_diff_state {
- struct xdiff_emit_state xm;
- struct patch *ret;
- unsigned hunk_post_context;
- unsigned hunk_in_pre_context : 1;
-};
-
-static void process_u_diff(void *state_, char *line, unsigned long len)
-{
- struct blame_diff_state *state = state_;
- struct chunk *chunk;
- int off1, off2, len1, len2, num;
-
- num = state->ret->num;
- if (len < 4 || line[0] != '@' || line[1] != '@') {
- if (state->hunk_in_pre_context && line[0] == ' ')
- state->ret->chunks[num - 1].same++;
- else {
- state->hunk_in_pre_context = 0;
- if (line[0] == ' ')
- state->hunk_post_context++;
- else
- state->hunk_post_context = 0;
- }
- return;
- }
-
- if (num && state->hunk_post_context) {
- chunk = &state->ret->chunks[num - 1];
- chunk->p_next -= state->hunk_post_context;
- chunk->t_next -= state->hunk_post_context;
- }
- state->ret->num = ++num;
- state->ret->chunks = xrealloc(state->ret->chunks,
- sizeof(struct chunk) * num);
- chunk = &state->ret->chunks[num - 1];
- if (parse_hunk_header(line, len, &off1, &len1, &off2, &len2)) {
- state->ret->num--;
- return;
- }
-
- /* Line numbers in patch output are one based. */
- off1--;
- off2--;
-
- chunk->same = len2 ? off2 : (off2 + 1);
-
- chunk->p_next = off1 + (len1 ? len1 : 1);
- chunk->t_next = chunk->same + len2;
- state->hunk_in_pre_context = 1;
- state->hunk_post_context = 0;
-}
-
-static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
- int context)
-{
- struct blame_diff_state state;
- xpparam_t xpp;
- xdemitconf_t xecfg;
- xdemitcb_t ecb;
-
- xpp.flags = xdl_opts;
- memset(&xecfg, 0, sizeof(xecfg));
- xecfg.ctxlen = context;
- ecb.outf = xdiff_outf;
- ecb.priv = &state;
- memset(&state, 0, sizeof(state));
- state.xm.consume = process_u_diff;
- state.ret = xmalloc(sizeof(struct patch));
- state.ret->chunks = NULL;
- state.ret->num = 0;
-
- xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb);
-
- if (state.ret->num) {
- struct chunk *chunk;
- chunk = &state.ret->chunks[state.ret->num - 1];
- chunk->p_next -= state.hunk_post_context;
- chunk->t_next -= state.hunk_post_context;
- }
- return state.ret;
-}
-
-/*
- * Run diff between two origins and grab the patch output, so that
- * we can pass blame for lines origin is currently suspected for
- * to its parent.
- */
-static struct patch *get_patch(struct origin *parent, struct origin *origin)
-{
- mmfile_t file_p, file_o;
- struct patch *patch;
-
- fill_origin_blob(parent, &file_p);
- fill_origin_blob(origin, &file_o);
- if (!file_p.ptr || !file_o.ptr)
- return NULL;
- patch = compare_buffer(&file_p, &file_o, 0);
- num_get_patch++;
- return patch;
-}
-
-static void free_patch(struct patch *p)
-{
- free(p->chunks);
- free(p);
-}
-
/*
* Link in a new blame entry to the scoreboard. Entries that cover the
* same line range have been removed from the scoreboard previously.
}
}
+struct blame_chunk_cb_data {
+ struct scoreboard *sb;
+ struct origin *target;
+ struct origin *parent;
+ long plno;
+ long tlno;
+};
+
+static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
+{
+ struct blame_chunk_cb_data *d = data;
+ blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
+ d->plno = p_next;
+ d->tlno = t_next;
+}
+
/*
* We are looking at the origin 'target' and aiming to pass blame
* for the lines it is suspected to its parent. Run diff to find
struct origin *target,
struct origin *parent)
{
- int i, last_in_target, plno, tlno;
- struct patch *patch;
+ int last_in_target;
+ mmfile_t file_p, file_o;
+ struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
last_in_target = find_last_in_target(sb, target);
if (last_in_target < 0)
return 1; /* nothing remains for this target */
- patch = get_patch(parent, target);
- plno = tlno = 0;
- for (i = 0; i < patch->num; i++) {
- struct chunk *chunk = &patch->chunks[i];
+ fill_origin_blob(parent, &file_p);
+ fill_origin_blob(target, &file_o);
+ num_get_patch++;
- blame_chunk(sb, tlno, plno, chunk->same, target, parent);
- plno = chunk->p_next;
- tlno = chunk->t_next;
- }
+ memset(&xpp, 0, sizeof(xpp));
+ xpp.flags = xdl_opts;
+ memset(&xecfg, 0, sizeof(xecfg));
+ xecfg.ctxlen = 0;
+ xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
/* The rest (i.e. anything after tlno) are the same as the parent */
- blame_chunk(sb, tlno, plno, last_in_target, target, parent);
+ blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
- free_patch(patch);
return 0;
}
}
}
+struct handle_split_cb_data {
+ struct scoreboard *sb;
+ struct blame_entry *ent;
+ struct origin *parent;
+ struct blame_entry *split;
+ long plno;
+ long tlno;
+};
+
+static void handle_split_cb(void *data, long same, long p_next, long t_next)
+{
+ struct handle_split_cb_data *d = data;
+ handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
+ d->plno = p_next;
+ d->tlno = t_next;
+}
+
/*
* Find the lines from parent that are the same as ent so that
* we can pass blames to it. file_p has the blob contents for
const char *cp;
int cnt;
mmfile_t file_o;
- struct patch *patch;
- int i, plno, tlno;
+ struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
/*
* Prepare mmfile that contains only the lines in ent.
}
file_o.size = cp - file_o.ptr;
- patch = compare_buffer(file_p, &file_o, 1);
-
/*
* file_o is a part of final image we are annotating.
* file_p partially may match that image.
*/
+ memset(&xpp, 0, sizeof(xpp));
+ xpp.flags = xdl_opts;
+ memset(&xecfg, 0, sizeof(xecfg));
+ xecfg.ctxlen = 1;
memset(split, 0, sizeof(struct blame_entry [3]));
- plno = tlno = 0;
- for (i = 0; i < patch->num; i++) {
- struct chunk *chunk = &patch->chunks[i];
-
- handle_split(sb, ent, tlno, plno, chunk->same, parent, split);
- plno = chunk->p_next;
- tlno = chunk->t_next;
- }
+ xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
/* remainder, if any, all match the preimage */
- handle_split(sb, ent, tlno, plno, ent->num_lines, parent, split);
- free_patch(patch);
+ handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
}
/*
if (!DIFF_FILE_VALID(p->one))
continue; /* does not exist in parent */
+ if (S_ISGITLINK(p->one->mode))
+ continue; /* ignore git links */
if (porigin && !strcmp(p->one->path, porigin->path))
/* find_move already dealt with this path */
continue;
int detailed)
{
int len;
- char *tmp, *endp;
+ char *tmp, *endp, *reencoded, *message;
static char author_buf[1024];
static char committer_buf[1024];
static char summary_buf[1024];
die("Cannot read commit %s",
sha1_to_hex(commit->object.sha1));
}
+ reencoded = reencode_commit_message(commit, NULL);
+ message = reencoded ? reencoded : commit->buffer;
ret->author = author_buf;
- get_ac_line(commit->buffer, "\nauthor ",
+ get_ac_line(message, "\nauthor ",
sizeof(author_buf), author_buf, &ret->author_mail,
&ret->author_time, &ret->author_tz);
- if (!detailed)
+ if (!detailed) {
+ free(reencoded);
return;
+ }
ret->committer = committer_buf;
- get_ac_line(commit->buffer, "\ncommitter ",
+ get_ac_line(message, "\ncommitter ",
sizeof(committer_buf), committer_buf, &ret->committer_mail,
&ret->committer_time, &ret->committer_tz);
ret->summary = summary_buf;
- tmp = strstr(commit->buffer, "\n\n");
+ tmp = strstr(message, "\n\n");
if (!tmp) {
error_out:
sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
+ free(reencoded);
return;
}
tmp += 2;
goto error_out;
memcpy(summary_buf, tmp, len);
summary_buf[len] = 0;
+ free(reencoded);
}
/*
if (suspect->commit->object.flags & UNINTERESTING) {
if (blank_boundary)
memset(hex, ' ', length);
- else if (!cmd_is_annotate) {
+ else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
length--;
putchar('^');
}
/*
* Add phony grafts for use with -S; this is primarily to
- * support git-cvsserver that wants to give a linear history
+ * support git's cvsserver that wants to give a linear history
* to its clients.
*/
static int read_ancestry(const char *graft_file)
struct commit *commit;
struct origin *origin;
unsigned char head_sha1[20];
- struct strbuf buf;
+ struct strbuf buf = STRBUF_INIT;
const char *ident;
time_t now;
int size, len;
origin = make_origin(commit, path);
- strbuf_init(&buf, 0);
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
};
struct parse_opt_ctx_t ctx;
-
- cmd_is_annotate = !strcmp(argv[0], "annotate");
+ int cmd_is_annotate = !strcmp(argv[0], "annotate");
git_config(git_blame_config, NULL);
init_revisions(&revs, NULL);
parse_done:
argc = parse_options_end(&ctx);
+ if (cmd_is_annotate)
+ output_option |= OUTPUT_ANNOTATE_COMPAT;
+
if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
PICKAXE_BLAME_COPY_HARDER);