X-Git-Url: https://git.octo.it/?p=git.git;a=blobdiff_plain;f=blame.c;h=7c0b62cf3f5ff78f95cf96c2582f693b5d124752;hp=9bb34e63ecf02bfde06e86627ac3b1d590dd0bc2;hb=HEAD;hpb=d69dc373cbf58d88d19dcbc6cff37e12b17f8fd2 diff --git a/blame.c b/blame.c index 9bb34e63..7c0b62cf 100644 --- a/blame.c +++ b/blame.c @@ -16,12 +16,15 @@ #include "diff.h" #include "diffcore.h" #include "revision.h" +#include "xdiff-interface.h" #define DEBUG 0 -static const char blame_usage[] = "[-c] [-l] [--] file [commit]\n" +static const char blame_usage[] = "[-c] [-l] [-t] [-S ] [--] file [commit]\n" " -c, --compability Use the same output mode as git-annotate (Default: off)\n" " -l, --long Show long commit SHA1 (Default: off)\n" + " -t, --time Show raw timestamp (Default: off)\n" + " -S, --revs-file Use revisions from revs-file instead of calling git-rev-list\n" " -h, --help This message"; static struct commit **blame_lines; @@ -57,116 +60,89 @@ static int num_get_patch = 0; static int num_commits = 0; static int patch_time = 0; -#define TEMPFILE_PATH_LEN 60 -static struct patch *get_patch(struct commit *commit, struct commit *other) -{ +struct blame_diff_state { + struct xdiff_emit_state xm; struct patch *ret; - struct util_info *info_c = (struct util_info *)commit->object.util; - struct util_info *info_o = (struct util_info *)other->object.util; - char tmp_path1[TEMPFILE_PATH_LEN], tmp_path2[TEMPFILE_PATH_LEN]; - char diff_cmd[TEMPFILE_PATH_LEN*2 + 20]; - struct timeval tv_start, tv_end; - int fd; - FILE *fin; - char buf[1024]; - - ret = xmalloc(sizeof(struct patch)); - ret->chunks = NULL; - ret->num = 0; - - get_blob(commit); - get_blob(other); +}; - gettimeofday(&tv_start, NULL); +static void process_u0_diff(void *state_, char *line, unsigned long len) +{ + struct blame_diff_state *state = state_; + struct chunk *chunk; - fd = git_mkstemp(tmp_path1, TEMPFILE_PATH_LEN, "git-blame-XXXXXX"); - if (fd < 0) - die("unable to create temp-file: %s", strerror(errno)); + if (len < 4 || line[0] != '@' || line[1] != '@') + return; - if (xwrite(fd, info_c->buf, info_c->size) != info_c->size) - die("write failed: %s", strerror(errno)); - close(fd); + if (DEBUG) + printf("chunk line: %.*s", (int)len, line); + state->ret->num++; + state->ret->chunks = xrealloc(state->ret->chunks, + sizeof(struct chunk) * state->ret->num); + chunk = &state->ret->chunks[state->ret->num - 1]; + + assert(!strncmp(line, "@@ -", 4)); + + if (parse_hunk_header(line, len, + &chunk->off1, &chunk->len1, + &chunk->off2, &chunk->len2)) { + state->ret->num--; + return; + } - fd = git_mkstemp(tmp_path2, TEMPFILE_PATH_LEN, "git-blame-XXXXXX"); - if (fd < 0) - die("unable to create temp-file: %s", strerror(errno)); + if (chunk->len1 == 0) + chunk->off1++; + if (chunk->len2 == 0) + chunk->off2++; - if (xwrite(fd, info_o->buf, info_o->size) != info_o->size) - die("write failed: %s", strerror(errno)); - close(fd); + if (chunk->off1 > 0) + chunk->off1--; + if (chunk->off2 > 0) + chunk->off2--; - sprintf(diff_cmd, "diff -U 0 %s %s", tmp_path1, tmp_path2); - fin = popen(diff_cmd, "r"); - if (!fin) - die("popen failed: %s", strerror(errno)); + assert(chunk->off1 >= 0); + assert(chunk->off2 >= 0); +} - while (fgets(buf, sizeof(buf), fin)) { - struct chunk *chunk; - char *start, *sp; +static struct patch *get_patch(struct commit *commit, struct commit *other) +{ + struct blame_diff_state state; + xpparam_t xpp; + xdemitconf_t xecfg; + mmfile_t file_c, file_o; + xdemitcb_t ecb; + struct util_info *info_c = (struct util_info *)commit->util; + struct util_info *info_o = (struct util_info *)other->util; + struct timeval tv_start, tv_end; - if (buf[0] != '@' || buf[1] != '@') - continue; + get_blob(commit); + file_c.ptr = info_c->buf; + file_c.size = info_c->size; - if (DEBUG) - printf("chunk line: %s", buf); - ret->num++; - ret->chunks = xrealloc(ret->chunks, - sizeof(struct chunk) * ret->num); - chunk = &ret->chunks[ret->num - 1]; - - assert(!strncmp(buf, "@@ -", 4)); - - start = buf + 4; - sp = index(start, ' '); - *sp = '\0'; - if (index(start, ',')) { - int ret = - sscanf(start, "%d,%d", &chunk->off1, &chunk->len1); - assert(ret == 2); - } else { - int ret = sscanf(start, "%d", &chunk->off1); - assert(ret == 1); - chunk->len1 = 1; - } - *sp = ' '; - - start = sp + 1; - sp = index(start, ' '); - *sp = '\0'; - if (index(start, ',')) { - int ret = - sscanf(start, "%d,%d", &chunk->off2, &chunk->len2); - assert(ret == 2); - } else { - int ret = sscanf(start, "%d", &chunk->off2); - assert(ret == 1); - chunk->len2 = 1; - } - *sp = ' '; + get_blob(other); + file_o.ptr = info_o->buf; + file_o.size = info_o->size; - if (chunk->len1 == 0) - chunk->off1++; - if (chunk->len2 == 0) - chunk->off2++; + gettimeofday(&tv_start, NULL); - if (chunk->off1 > 0) - chunk->off1--; - if (chunk->off2 > 0) - chunk->off2--; + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 0; + xecfg.flags = 0; + ecb.outf = xdiff_outf; + ecb.priv = &state; + memset(&state, 0, sizeof(state)); + state.xm.consume = process_u0_diff; + state.ret = xmalloc(sizeof(struct patch)); + state.ret->chunks = NULL; + state.ret->num = 0; - assert(chunk->off1 >= 0); - assert(chunk->off2 >= 0); - } - pclose(fin); - unlink(tmp_path1); - unlink(tmp_path2); + xdl_diff(&file_c, &file_o, &xpp, &xecfg, &ecb); gettimeofday(&tv_end, NULL); patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) + tv_end.tv_usec - tv_start.tv_usec; num_get_patch++; - return ret; + return state.ret; } static void free_patch(struct patch *p) @@ -175,7 +151,7 @@ static void free_patch(struct patch *p) free(p); } -static int get_blob_sha1_internal(unsigned char *sha1, const char *base, +static int get_blob_sha1_internal(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage); @@ -204,7 +180,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname, return 0; } -static int get_blob_sha1_internal(unsigned char *sha1, const char *base, +static int get_blob_sha1_internal(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { @@ -221,7 +197,7 @@ static int get_blob_sha1_internal(unsigned char *sha1, const char *base, static void get_blob(struct commit *commit) { - struct util_info *info = commit->object.util; + struct util_info *info = commit->util; char type[20]; if (info->buf) @@ -247,8 +223,8 @@ static void print_patch(struct patch *p) /* For debugging only */ static void print_map(struct commit *cmit, struct commit *other) { - struct util_info *util = cmit->object.util; - struct util_info *util2 = other->object.util; + struct util_info *util = cmit->util; + struct util_info *util2 = other->util; int i; int max = @@ -283,8 +259,8 @@ static void print_map(struct commit *cmit, struct commit *other) static void fill_line_map(struct commit *commit, struct commit *other, struct patch *p) { - struct util_info *util = commit->object.util; - struct util_info *util2 = other->object.util; + struct util_info *util = commit->util; + struct util_info *util2 = other->util; int *map = util->line_map; int *map2 = util2->line_map; int cur_chunk = 0; @@ -346,14 +322,14 @@ static void fill_line_map(struct commit *commit, struct commit *other, static int map_line(struct commit *commit, int line) { - struct util_info *info = commit->object.util; + struct util_info *info = commit->util; assert(line >= 0 && line < info->num_lines); return info->line_map[line]; } static struct util_info* get_util(struct commit *commit) { - struct util_info *util = commit->object.util; + struct util_info *util = commit->util; if (util) return util; @@ -364,13 +340,13 @@ static struct util_info* get_util(struct commit *commit) util->line_map = NULL; util->num_lines = -1; util->pathname = NULL; - commit->object.util = util; + commit->util = util; return util; } static int fill_util_info(struct commit *commit) { - struct util_info *util = commit->object.util; + struct util_info *util = commit->util; assert(util); assert(util->pathname); @@ -383,7 +359,7 @@ static int fill_util_info(struct commit *commit) static void alloc_line_map(struct commit *commit) { - struct util_info *util = commit->object.util; + struct util_info *util = commit->util; int i; if (util->line_map) @@ -407,7 +383,7 @@ static void alloc_line_map(struct commit *commit) static void init_first_commit(struct commit* commit, const char* filename) { - struct util_info* util = commit->object.util; + struct util_info* util = commit->util; int i; util->pathname = filename; @@ -416,7 +392,7 @@ static void init_first_commit(struct commit* commit, const char* filename) alloc_line_map(commit); - util = commit->object.util; + util = commit->util; for (i = 0; i < util->num_lines; i++) util->line_map[i] = i; @@ -437,7 +413,7 @@ static void process_commits(struct rev_info *rev, const char *path, assert(commit); init_first_commit(commit, path); - util = commit->object.util; + util = commit->util; num_blame_lines = util->num_lines; blame_lines = xmalloc(sizeof(struct commit *) * num_blame_lines); blame_contents = util->buf; @@ -476,7 +452,7 @@ static void process_commits(struct rev_info *rev, const char *path, continue; alloc_line_map(commit); - util = commit->object.util; + util = commit->util; for (parents = commit->parents; parents != NULL; parents = parents->next) { @@ -534,30 +510,38 @@ static void process_commits(struct rev_info *rev, const char *path, static int compare_tree_path(struct rev_info* revs, struct commit* c1, struct commit* c2) { + int ret; const char* paths[2]; - struct util_info* util = c2->object.util; + struct util_info* util = c2->util; paths[0] = util->pathname; paths[1] = NULL; - diff_tree_setup_paths(get_pathspec(revs->prefix, paths)); - return rev_compare_tree(c1->tree, c2->tree); + diff_tree_setup_paths(get_pathspec(revs->prefix, paths), + &revs->pruning); + ret = rev_compare_tree(revs, c1->tree, c2->tree); + diff_tree_release_paths(&revs->pruning); + return ret; } static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1, const char* path) { + int ret; const char* paths[2]; paths[0] = path; paths[1] = NULL; - diff_tree_setup_paths(get_pathspec(revs->prefix, paths)); - return rev_same_tree_as_empty(t1); + diff_tree_setup_paths(get_pathspec(revs->prefix, paths), + &revs->pruning); + ret = rev_same_tree_as_empty(revs, t1); + diff_tree_release_paths(&revs->pruning); + return ret; } static const char* find_rename(struct commit* commit, struct commit* parent) { - struct util_info* cutil = commit->object.util; + struct util_info* cutil = commit->util; struct diff_options diff_opts; const char *paths[1]; int i; @@ -572,7 +556,7 @@ static const char* find_rename(struct commit* commit, struct commit* parent) diff_opts.recursive = 1; diff_opts.detect_rename = DIFF_DETECT_RENAME; paths[0] = NULL; - diff_tree_setup_paths(paths); + diff_tree_setup_paths(paths, &diff_opts); if (diff_setup_done(&diff_opts) < 0) die("diff_setup_done failed"); @@ -601,7 +585,7 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit) return; if (!commit->parents) { - struct util_info* util = commit->object.util; + struct util_info* util = commit->util; if (!same_tree_as_empty_path(revs, commit->tree, util->pathname)) commit->object.flags |= TREECHANGE; @@ -628,7 +612,7 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit) case REV_TREE_NEW: { - struct util_info* util = commit->object.util; + struct util_info* util = commit->util; if (revs->remove_empty_trees && same_tree_as_empty_path(revs, p->tree, util->pathname)) { @@ -674,7 +658,7 @@ static void get_commit_info(struct commit* commit, struct commit_info* ret) static char author_buf[1024]; tmp = strstr(commit->buffer, "\nauthor ") + 8; - len = index(tmp, '\n') - tmp; + len = strchr(tmp, '\n') - tmp; ret->author = author_buf; memcpy(ret->author, tmp, len); @@ -698,13 +682,19 @@ static void get_commit_info(struct commit* commit, struct commit_info* ret) *tmp = 0; } -static const char* format_time(unsigned long time, const char* tz_str) +static const char* format_time(unsigned long time, const char* tz_str, + int show_raw_time) { static char time_buf[128]; time_t t = time; int minutes, tz; struct tm *tm; + if (show_raw_time) { + sprintf(time_buf, "%lu %s", time, tz_str); + return time_buf; + } + tz = atoi(tz_str); minutes = tz < 0 ? -tz : tz; minutes = (minutes / 100)*60 + (minutes % 100); @@ -719,26 +709,46 @@ static const char* format_time(unsigned long time, const char* tz_str) static void topo_setter(struct commit* c, void* data) { - struct util_info* util = c->object.util; + struct util_info* util = c->util; util->topo_data = data; } static void* topo_getter(struct commit* c) { - struct util_info* util = c->object.util; + struct util_info* util = c->util; return util->topo_data; } +static int read_ancestry(const char *graft_file, + unsigned char **start_sha1) +{ + FILE *fp = fopen(graft_file, "r"); + char buf[1024]; + if (!fp) + return -1; + while (fgets(buf, sizeof(buf), fp)) { + /* The format is just "Commit Parent1 Parent2 ...\n" */ + int len = strlen(buf); + struct commit_graft *graft = read_graft_line(buf, len); + register_commit_graft(graft, 0); + if (!*start_sha1) + *start_sha1 = graft->sha1; + } + fclose(fp); + return 0; +} + int main(int argc, const char **argv) { int i; struct commit *initial = NULL; - unsigned char sha1[20]; + unsigned char sha1[20], *sha1_p = NULL; const char *filename = NULL, *commit = NULL; char filename_buf[256]; int sha1_len = 8; int compability = 0; + int show_raw_time = 0; int options = 1; struct commit* start_commit; @@ -767,6 +777,18 @@ int main(int argc, const char **argv) !strcmp(argv[i], "--compability")) { compability = 1; continue; + } else if(!strcmp(argv[i], "-t") || + !strcmp(argv[i], "--time")) { + show_raw_time = 1; + continue; + } else if(!strcmp(argv[i], "-S")) { + if (i + 1 < argc && + !read_ancestry(argv[i + 1], &sha1_p)) { + compability = 1; + i++; + continue; + } + usage(blame_usage); } else if(!strcmp(argv[i], "--")) { options = 0; continue; @@ -788,7 +810,9 @@ int main(int argc, const char **argv) if(!filename) usage(blame_usage); - if(!commit) + if (commit && sha1_p) + usage(blame_usage); + else if(!commit) commit = "HEAD"; if(prefix) @@ -797,9 +821,12 @@ int main(int argc, const char **argv) strcpy(filename_buf, filename); filename = filename_buf; - if (get_sha1(commit, sha1)) - die("get_sha1 failed, commit '%s' not found", commit); - start_commit = lookup_commit_reference(sha1); + if (!sha1_p) { + if (get_sha1(commit, sha1)) + die("get_sha1 failed, commit '%s' not found", commit); + sha1_p = sha1; + } + start_commit = lookup_commit_reference(sha1_p); get_util(start_commit)->pathname = filename; if (fill_util_info(start_commit)) { printf("%s not found in %s\n", filename, commit); @@ -820,7 +847,7 @@ int main(int argc, const char **argv) args[0] = filename; args[1] = NULL; - diff_tree_setup_paths(args); + diff_tree_setup_paths(args, &rev.pruning); prepare_revision_walk(&rev); process_commits(&rev, filename, &initial); @@ -836,7 +863,7 @@ int main(int argc, const char **argv) struct util_info* u; if (!c) c = initial; - u = c->object.util; + u = c->util; if (!found_rename && strcmp(filename, u->pathname)) found_rename = 1; @@ -854,19 +881,22 @@ int main(int argc, const char **argv) if (!c) c = initial; - u = c->object.util; + u = c->util; get_commit_info(c, &ci); fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout); if(compability) { printf("\t(%10s\t%10s\t%d)", ci.author, - format_time(ci.author_time, ci.author_tz), i+1); + format_time(ci.author_time, ci.author_tz, + show_raw_time), + i+1); } else { if (found_rename) printf(" %-*.*s", longest_file, longest_file, u->pathname); printf(" (%-*.*s %10s %*d) ", longest_author, longest_author, ci.author, - format_time(ci.author_time, ci.author_tz), + format_time(ci.author_time, ci.author_tz, + show_raw_time), max_digits, i+1); } @@ -876,7 +906,7 @@ int main(int argc, const char **argv) if(blame_contents[blame_len-1] != '\n') putc('\n', stdout); } else { - char* next_buf = index(buf, '\n') + 1; + char* next_buf = strchr(buf, '\n') + 1; fwrite(buf, next_buf - buf, 1, stdout); buf = next_buf; }