commit c35a7943488e43a4fc6c48a00cf858ba8f410947 from: Stefan Sperling date: Thu Jul 12 13:04:11 2018 UTC account for line shift in blame; lots of help from tb@ commit - 45202a8f5e7659326a88147e7eecbef20545add2 commit + c35a7943488e43a4fc6c48a00cf858ba8f410947 blob - 1dcf38b4c06596714780a7ea6b58464c1ad509ec blob + 34624017c79b7a30d72aa532073224e79b3fcf78 --- got/Makefile +++ got/Makefile @@ -1,10 +1,10 @@ .PATH:${.CURDIR}/../lib PROG= got -SRCS= got.c blame.c commit_graph.c delta.c diff.c diffreg.c error.c \ - fileindex.c object.c object_idcache.c object_idset.c opentemp.c \ - path.c pack.c privsep.c reference.c repository.c sha1.c \ - worktree.c inflate.c +SRCS= got.c blame.c commit_graph.c delta.c diff.c diffoffset.c \ + diffreg.c error.c fileindex.c object.c object_idcache.c \ + object_idset.c opentemp.c path.c pack.c privsep.c reference.c \ + repository.c sha1.c worktree.c inflate.c CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib LDADD = -lutil -lz blob - e5c55ef07aa6ac499be912c4de1fc41b0874dc2b blob + 67a5c59587fdedd7aeaa91e2d271ef19d02b018c --- lib/blame.c +++ lib/blame.c @@ -34,19 +34,66 @@ #include "got_lib_delta.h" #include "got_lib_object.h" #include "got_lib_diff.h" +#include "got_lib_diffoffset.h" struct got_blame_line { int annotated; struct got_object_id id; }; +struct got_blame_diff_offsets { + struct got_diffoffset_chunks *chunks; + struct got_object_id *commit_id; + SLIST_ENTRY(got_blame_diff_offsets) entry; +}; + +SLIST_HEAD(got_blame_diff_offsets_list, got_blame_diff_offsets); + struct got_blame { FILE *f; size_t nlines; struct got_blame_line *lines; /* one per line */ + int ncommits; + struct got_blame_diff_offsets_list diff_offsets_list; }; +static void +free_diff_offsets(struct got_blame_diff_offsets *diff_offsets) +{ + if (diff_offsets->chunks) + got_diffoffset_free(diff_offsets->chunks); + free(diff_offsets->commit_id); + free(diff_offsets); +} + static const struct got_error * +alloc_diff_offsets(struct got_blame_diff_offsets **diff_offsets, + struct got_object_id *commit_id) +{ + const struct got_error *err = NULL; + + *diff_offsets = calloc(1, sizeof(**diff_offsets)); + if (*diff_offsets == NULL) + return got_error_from_errno(); + + (*diff_offsets)->commit_id = got_object_id_dup(commit_id); + if ((*diff_offsets)->commit_id == NULL) { + err = got_error_from_errno(); + free_diff_offsets(*diff_offsets); + *diff_offsets = NULL; + return err; + } + + err = got_diffoffset_alloc(&(*diff_offsets)->chunks); + if (err) { + free_diff_offsets(*diff_offsets); + return err; + } + + return NULL; +} + +static const struct got_error * annotate_line(struct got_blame *blame, int lineno, struct got_object_id *id, const struct got_error *(*cb)(void *, int, int, struct got_object_id *), void *arg) @@ -68,7 +115,68 @@ annotate_line(struct got_blame *blame, int lineno, str return err; } +static int +get_blamed_line(struct got_blame_diff_offsets_list *diff_offsets_list, + int lineno) +{ + struct got_blame_diff_offsets *diff_offsets; + + SLIST_FOREACH(diff_offsets, diff_offsets_list, entry) + lineno = got_diffoffset_get(diff_offsets->chunks, lineno); + + return lineno; +} + static const struct got_error * +blame_changes(struct got_blame *blame, struct got_diff_changes *changes, + struct got_object_id *commit_id, + const struct got_error *(*cb)(void *, int, int, struct got_object_id *), + void *arg) +{ + const struct got_error *err = NULL; + struct got_diff_change *change; + struct got_blame_diff_offsets *diff_offsets; + + SIMPLEQ_FOREACH(change, &changes->entries, entry) { + int c = change->cv.c; + int d = change->cv.d; + int new_lineno = c; + int new_length = (c < d ? d - c + 1 : (c == d ? 1 : 0)); + int ln; + + for (ln = new_lineno; ln < new_lineno + new_length; ln++) { + err = annotate_line(blame, + get_blamed_line(&blame->diff_offsets_list, ln), + commit_id, cb, arg); + if (err) + return err; + } + } + + err = alloc_diff_offsets(&diff_offsets, commit_id); + if (err) + return err; + SIMPLEQ_FOREACH(change, &changes->entries, entry) { + int a = change->cv.a; + int b = change->cv.b; + int c = change->cv.c; + int d = change->cv.d; + int old_lineno = a; + int old_length = (a < b ? b - a + 1 : (a == b ? 1 : 0)); + int new_lineno = c; + int new_length = (c < d ? d - c + 1 : (c == d ? 1 : 0)); + + err = got_diffoffset_add(diff_offsets->chunks, + old_lineno, old_length, new_lineno, new_length); + if (err) + return err; + } + SLIST_INSERT_HEAD(&blame->diff_offsets_list, diff_offsets, entry); + + return NULL; +} + +static const struct got_error * blame_commit(struct got_blame *blame, struct got_object_id *id, struct got_object_id *pid, const char *path, struct got_repository *repo, const struct got_error *(*cb)(void *, int, int, struct got_object_id *), @@ -119,22 +227,12 @@ blame_commit(struct got_blame *blame, struct got_objec if (err) goto done; - err = got_diff_blob_lines_changed(&changes, blob, pblob); + err = got_diff_blob_lines_changed(&changes, pblob, blob); if (err) goto done; if (changes) { - struct got_diff_change *change; - SIMPLEQ_FOREACH(change, &changes->entries, entry) { - int a = change->cv.a; - int b = change->cv.b; - int lineno; - for (lineno = a; lineno <= b; lineno++) { - err = annotate_line(blame, lineno, id, cb, arg); - if (err) - goto done; - } - } + err = blame_changes(blame, changes, id, cb, arg); } else if (cb) err = cb(arg, blame->nlines, -1, id); done: @@ -152,9 +250,16 @@ done: static void blame_close(struct got_blame *blame) { + struct got_blame_diff_offsets *diff_offsets; + if (blame->f) fclose(blame->f); free(blame->lines); + while (!SLIST_EMPTY(&blame->diff_offsets_list)) { + diff_offsets = SLIST_FIRST(&blame->diff_offsets_list); + SLIST_REMOVE_HEAD(&blame->diff_offsets_list, entry); + free_diff_offsets(diff_offsets); + } free(blame); } blob - /dev/null blob + 6d1af1ad443cf2c9732c10f711a9708c952f4a0e (mode 644) --- /dev/null +++ lib/diffoffset.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "got_object.h" + +#include "got_error.h" +#include "got_lib_delta.h" +#include "got_lib_inflate.h" +#include "got_lib_object.h" +#include "got_lib_diffoffset.h" + +/* + * A line offset between an old file and a new file, derived from diff chunk + * header info @@ -old_lineno,old_length +new_lineno,new_length @@ in a diff + * with zero context lines (as in diff -U0 old-file new-file). + */ +struct got_diffoffset_chunk { + int lineno; /* first line which has shifted */ + int offset; /* applies to subsequent lines until next chunk */ + SIMPLEQ_ENTRY(got_diffoffset_chunk) entry; +}; + +static struct got_diffoffset_chunk * +alloc_chunk(int lineno, int offset) +{ + struct got_diffoffset_chunk *chunk; + + chunk = calloc(1, sizeof(*chunk)); + if (chunk == NULL) + return NULL; + + chunk->lineno = lineno; + chunk->offset = offset; + + return chunk; +} + +const struct got_error * +got_diffoffset_alloc(struct got_diffoffset_chunks **chunks) +{ + const struct got_error *err = NULL; + struct got_diffoffset_chunk *first; + + first = alloc_chunk(0, 0); + if (first == NULL) + return got_error_from_errno(); + + *chunks = calloc(1, sizeof(**chunks)); + if (*chunks == NULL) { + err = got_error_from_errno(); + free(first); + return err; + } + + SIMPLEQ_INIT(*chunks); + SIMPLEQ_INSERT_HEAD(*chunks, first, entry); + + return NULL; +} + +void +got_diffoffset_free(struct got_diffoffset_chunks *chunks) +{ + struct got_diffoffset_chunk *chunk; + + while (!SIMPLEQ_EMPTY(chunks)) { + chunk = SIMPLEQ_FIRST(chunks); + SIMPLEQ_REMOVE_HEAD(chunks, entry); + free(chunk); + } + free(chunks); +} + +const struct got_error * +got_diffoffset_add(struct got_diffoffset_chunks *chunks, + int old_lineno, int old_length, int new_lineno, int new_length) +{ + struct got_diffoffset_chunk *chunk1, *chunk2; + + chunk1 = alloc_chunk(old_lineno, new_lineno - old_lineno); + if (chunk1 == NULL) + return got_error_from_errno(); + + chunk2 = alloc_chunk(old_lineno + old_length, + new_lineno - old_lineno + new_length - old_length); + if (chunk2 == NULL) { + const struct got_error *err = got_error_from_errno(); + free(chunk1); + return err; + } + + SIMPLEQ_INSERT_TAIL(chunks, chunk1, entry); + SIMPLEQ_INSERT_TAIL(chunks, chunk2, entry); + return NULL; +} + +int +got_diffoffset_get(struct got_diffoffset_chunks *chunks, int lineno) +{ + struct got_diffoffset_chunk *chunk, *prev; + + prev = SIMPLEQ_FIRST(chunks); + SIMPLEQ_FOREACH(chunk, chunks, entry) { + if (chunk->lineno > lineno) + break; + prev = chunk; + } + + return lineno + prev->offset; +} blob - /dev/null blob + 705d33a582f9bfabcaf7d4aac05826d8123084bd (mode 644) --- /dev/null +++ lib/got_lib_diffoffset.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +SIMPLEQ_HEAD(got_diffoffset_chunks, got_diffoffset_chunk); + +const struct got_error *got_diffoffset_alloc(struct got_diffoffset_chunks **); +void got_diffoffset_free(struct got_diffoffset_chunks *); +const struct got_error *got_diffoffset_add(struct got_diffoffset_chunks *, + int, int, int, int); +int got_diffoffset_get(struct got_diffoffset_chunks *, int); blob - 06bda662dc489c32771945b9cdb976211c884a6e blob + dbaa942edff0f3aeff645f149e86fb18cc6d4ab8 --- tog/Makefile +++ tog/Makefile @@ -1,10 +1,10 @@ .PATH:${.CURDIR}/../lib PROG= tog -SRCS= tog.c blame.c commit_graph.c delta.c diff.c diffreg.c error.c \ - fileindex.c object.c object_idcache.c object_idset.c \ - opentemp.c path.c pack.c privsep.c reference.c repository.c \ - sha1.c worktree.c utf8.c inflate.c +SRCS= tog.c blame.c commit_graph.c delta.c diff.c diffoffset.c \ + diffreg.c error.c fileindex.c object.c object_idcache.c \ + object_idset.c opentemp.c path.c pack.c privsep.c \ + reference.c repository.c sha1.c worktree.c utf8.c inflate.c CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib LDADD = -lpanel -lncursesw -lutil -lz -lpthread