diff --git a/blame.c b/blame.c index 1ee0d323ca..75cffd8a3f 100644 --- a/blame.c +++ b/blame.c @@ -703,11 +703,30 @@ static void* topo_getter(struct commit* c) 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]; @@ -741,6 +760,14 @@ int main(int argc, const char **argv) !strcmp(argv[i], "--compability")) { compability = 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; @@ -762,7 +789,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) @@ -771,9 +800,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); diff --git a/commit.c b/commit.c index d4976fb89f..d534c9ba58 100644 --- a/commit.c +++ b/commit.c @@ -101,11 +101,7 @@ static unsigned long parse_commit_date(const char *buf) return date; } -static struct commit_graft { - unsigned char sha1[20]; - int nr_parent; - unsigned char parent[0][20]; /* more */ -} **commit_graft; +static struct commit_graft **commit_graft; static int commit_graft_alloc, commit_graft_nr; static int commit_graft_pos(const unsigned char *sha1) @@ -127,70 +123,98 @@ static int commit_graft_pos(const unsigned char *sha1) return -lo - 1; } -static void prepare_commit_graft(void) +int register_commit_graft(struct commit_graft *graft, int ignore_dups) +{ + int pos = commit_graft_pos(graft->sha1); + + if (0 <= pos) { + if (ignore_dups) + free(graft); + else { + free(commit_graft[pos]); + commit_graft[pos] = graft; + } + return 1; + } + pos = -pos - 1; + if (commit_graft_alloc <= ++commit_graft_nr) { + commit_graft_alloc = alloc_nr(commit_graft_alloc); + commit_graft = xrealloc(commit_graft, + sizeof(*commit_graft) * + commit_graft_alloc); + } + if (pos < commit_graft_nr) + memmove(commit_graft + pos + 1, + commit_graft + pos, + (commit_graft_nr - pos - 1) * + sizeof(*commit_graft)); + commit_graft[pos] = graft; + return 0; +} + +struct commit_graft *read_graft_line(char *buf, int len) +{ + /* The format is just "Commit Parent1 Parent2 ...\n" */ + int i; + struct commit_graft *graft = NULL; + + if (buf[len-1] == '\n') + buf[--len] = 0; + if (buf[0] == '#') + return 0; + if ((len + 1) % 41) { + bad_graft_data: + error("bad graft data: %s", buf); + free(graft); + return NULL; + } + i = (len + 1) / 41 - 1; + graft = xmalloc(sizeof(*graft) + 20 * i); + graft->nr_parent = i; + if (get_sha1_hex(buf, graft->sha1)) + goto bad_graft_data; + for (i = 40; i < len; i += 41) { + if (buf[i] != ' ') + goto bad_graft_data; + if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) + goto bad_graft_data; + } + return graft; +} + +int read_graft_file(const char *graft_file) { - char *graft_file = get_graft_file(); FILE *fp = fopen(graft_file, "r"); char buf[1024]; - if (!fp) { - commit_graft = (struct commit_graft **) "hack"; - return; - } + if (!fp) + return -1; while (fgets(buf, sizeof(buf), fp)) { /* The format is just "Commit Parent1 Parent2 ...\n" */ int len = strlen(buf); - int i; - struct commit_graft *graft = NULL; - - if (buf[len-1] == '\n') - buf[--len] = 0; - if (buf[0] == '#') - continue; - if ((len + 1) % 41) { - bad_graft_data: - error("bad graft data: %s", buf); - free(graft); - continue; - } - i = (len + 1) / 41 - 1; - graft = xmalloc(sizeof(*graft) + 20 * i); - graft->nr_parent = i; - if (get_sha1_hex(buf, graft->sha1)) - goto bad_graft_data; - for (i = 40; i < len; i += 41) { - if (buf[i] != ' ') - goto bad_graft_data; - if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) - goto bad_graft_data; - } - i = commit_graft_pos(graft->sha1); - if (0 <= i) { + struct commit_graft *graft = read_graft_line(buf, len); + if (register_commit_graft(graft, 1)) error("duplicate graft data: %s", buf); - free(graft); - continue; - } - i = -i - 1; - if (commit_graft_alloc <= ++commit_graft_nr) { - commit_graft_alloc = alloc_nr(commit_graft_alloc); - commit_graft = xrealloc(commit_graft, - sizeof(*commit_graft) * - commit_graft_alloc); - } - if (i < commit_graft_nr) - memmove(commit_graft + i + 1, - commit_graft + i, - (commit_graft_nr - i - 1) * - sizeof(*commit_graft)); - commit_graft[i] = graft; } fclose(fp); + return 0; +} + +static void prepare_commit_graft(void) +{ + static int commit_graft_prepared; + char *graft_file; + + if (commit_graft_prepared) + return; + graft_file = get_graft_file(); + read_graft_file(graft_file); + commit_graft_prepared = 1; } static struct commit_graft *lookup_commit_graft(const unsigned char *sha1) { int pos; - if (!commit_graft) - prepare_commit_graft(); + prepare_commit_graft(); pos = commit_graft_pos(sha1); if (pos < 0) return NULL; diff --git a/commit.h b/commit.h index 98682b232a..918c9ab5e4 100644 --- a/commit.h +++ b/commit.h @@ -90,4 +90,15 @@ void sort_in_topological_order(struct commit_list ** list, int lifo); void sort_in_topological_order_fn(struct commit_list ** list, int lifo, topo_sort_set_fn_t setter, topo_sort_get_fn_t getter); + +struct commit_graft { + unsigned char sha1[20]; + int nr_parent; + unsigned char parent[FLEX_ARRAY][20]; /* more */ +}; + +struct commit_graft *read_graft_line(char *buf, int len); +int register_commit_graft(struct commit_graft *, int); +int read_graft_file(const char *graft_file); + #endif /* COMMIT_H */