diff --git a/apply.c b/apply.c index 1742ab2..fd7133c 100644 --- a/apply.c +++ b/apply.c @@ -22,6 +22,7 @@ static int prefix_length = -1; static int allow_binary_replacement = 0; static int check_index = 0; static int write_index = 0; +static int annotate = 0; static int diffstat = 0; static int numstat = 0; static int summary = 0; @@ -1425,6 +1426,260 @@ static void stat_patch_list(struct patch printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels); } +/* Patch-based annotate: + * + * A view of the revision is recorded using the foll + * + * Using the generic patch parser, each fragment of each patch is iterated and + * the effect of added and deleted lines are applied in the following way: + * + * 1. Deleted lines are + * 2. Added lines are 'blamed' on the patch in question. + */ + +/* Stores info about the origin of one or more line which are in the revision + * being annotated. The position of the blame in the annotated revision is + * implied by the position of the blame structs position according to each + * other. */ +struct blame { + struct blame *next; + unsigned long lines; + char *src; + struct patch *patch; +}; + +/* Used for storing the current 'state' of a revision. That is, information + * about which lines has been deleted from the revision being annotated, and + * thus should be ignored and which lines are interesting when recording blame. + */ +struct revline { + struct blame *blame; + unsigned int ignore:1; +}; + +struct revline *revlines; +unsigned long revlines_max; +unsigned long revlines_size; + +// #define DEBUG + +static void show_annotate(void) +{ + struct revline *pos, *end = &revlines[revlines_size]; + unsigned long line = 1; + + for (pos = revlines; pos < end; pos++) { + struct blame *blame = pos->blame; + + if (!blame) continue; + + while (blame) { + struct blame *tmp = blame; + unsigned long lines; + + for (lines = 0; lines < blame->lines; lines++, line++) { + printf("%5ld %5ld %s %s\n", line, blame->lines, + blame->patch->new_name, blame->patch->new_sha1_prefix); + } + + blame = blame->next; + free(tmp); + } + } + + free(revlines); +} + +unsigned long all_adds, all_dels, all_blames; + +static void insert_blame(struct patch *patch, unsigned long offset, + unsigned long lines) +{ + struct revline *line = &revlines[offset]; + struct revline *end = &revlines[offset + lines]; + struct blame *list = NULL, *list_end = NULL; + + for (lines = 0; line < end; line++) { + struct blame *blame; + + if (!line->ignore) + lines++; + + /* Prefer to collect as many blame lines as possible. However, + * if we encounter a line with blame attached store the blame + * lines so the blame already recorded can be post fixed to it. + * Gots to keep the order right! */ + if (lines && line->blame) { + blame = xmalloc(sizeof(*blame)); + blame->patch = patch; + blame->lines = lines; + blame->next = NULL; + + if (list) { + list_end->next = blame; + } else { + list = blame; + } + + list_end = blame; + + all_blames += lines; + lines = 0; + } + + if (line->blame) { + if (list) { + list_end->next = line->blame; + } else { + list = line->blame; + } + + for (blame = line->blame; blame->next; blame = blame->next) + ; + + list_end = blame; + line->blame = NULL; + } + } + + if (lines) { + struct blame *blame = xmalloc(sizeof(*blame)); + + blame->patch = patch; + blame->lines = lines; + blame->next = NULL; + + all_blames += lines; + + if (list) { + list_end->next = blame; + } else { + list = blame; + } + + list_end = blame; + } + + if (end->blame) + list_end->next = end->blame; + + end->blame = list; +} + +static void update_revlines(struct patch *patch, unsigned long oldpos, + unsigned long adds, unsigned long dels) +{ + if (adds) { + insert_blame(patch, oldpos, adds); + + if (adds > dels) { + unsigned long diff = adds - dels; + + memmove(&revlines[oldpos], + &revlines[oldpos + diff], + (revlines_size - (oldpos + diff)) * sizeof(*revlines)); + } + + all_adds += adds; + } + + if (dels) { + unsigned long offset; + unsigned long diff = dels; + + if (dels > adds) { + diff -= adds; + + memmove(&revlines[oldpos + diff], + &revlines[oldpos], + (revlines_size - (oldpos + diff)) * sizeof(*revlines)); + } + + /* Clear all inserted lines */ + for (offset = 0; offset < diff; offset++) { + revlines[oldpos + offset].ignore = 1; + revlines[oldpos + offset].blame = NULL; + } + + all_dels += dels; + } +} + +static void annotate_fragment(struct fragment *frag, struct patch *patch) +{ + unsigned long oldpos = frag->oldpos; + unsigned long adds = 0, dels = 0; + unsigned long srcpos; + + if (oldpos + frag->oldlines > revlines_max) + revlines_max = oldpos + frag->oldlines + 1; + + if (frag->newpos + frag->newlines > revlines_max) + revlines_max = frag->newpos + frag->newlines + 1; + + if (revlines_max > revlines_size) { + revlines = xrealloc(revlines, revlines_max * sizeof(*revlines)); + memset(&revlines[revlines_size], 0, (revlines_max - revlines_size) * sizeof(*revlines)); + revlines_size = revlines_max; + } + + for (srcpos = 0; srcpos < frag->size; srcpos++) { + char *end = memchr(frag->patch + srcpos, '\n', frag->size - srcpos); + + switch (frag->patch[srcpos]) { + case '@': /* BEGIN */ + break; + + case '+': /* DELETE */ + adds++; + break; + + case '-': /* ADD */ + dels++; + break; + + default: + if (adds || dels) { + update_revlines(patch, oldpos, adds, dels); + + oldpos += dels - adds; + + printf("adds=%5ld, dels=%5ld, all-adds=%5ld, all-dels=%5ld, all-blames=%5ld\n", adds, dels, all_adds, all_dels, all_blames); + + adds = dels = 0; + } + + oldpos++; + } + + if (!end) break; + + srcpos += end - (frag->patch + srcpos); + } + + if (adds || dels) { + update_revlines(patch, oldpos, adds, dels); + printf("adds=%5ld, dels=%5ld, all-adds=%5ld, all-dels=%5ld, all-blames=%5ld\n", adds, dels, all_adds, all_dels, all_blames); + } +} + +static void annotate_patch(struct patch *patch) +{ + struct fragment *frag; + + for (frag = patch->fragments; frag; frag = frag->next) + annotate_fragment(frag, patch); +} + +static void annotate_patch_list(struct patch *patch) +{ + for (; patch ; patch = patch->next) + annotate_patch(patch); + + show_annotate(); +} + + static void numstat_patch_list(struct patch *patch) { for ( ; patch; patch = patch->next) { @@ -1778,6 +2033,9 @@ static int apply_patch(int fd) if (diffstat) stat_patch_list(list); + if (annotate) + annotate_patch_list(list); + if (numstat) numstat_patch_list(list); @@ -1845,6 +2103,11 @@ int main(int argc, char **argv) apply = 1; continue; } + if (!strcmp(arg, "--annotate")) { + apply = 0; + annotate = 1; + continue; + } if (!strcmp(arg, "--index-info")) { apply = 0; show_index_info = 1;