commit - 63581804340e880bf611c6a4a59eda26c503799f
commit + 84451b3ef755f3226d0d79af367632e5f3a830e7
blob - b53ca469a18871cc2f6af334dab25028599c6488
blob + c787aadf05e2afab61bd34976f7349912252e6da
--- include/got_blame.h
+++ include/got_blame.h
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+/*
+ * Write an annotated version of a file at a given in-repository path,
+ * as found in the commit specified by ID, to the specified output file.
+ */
const struct got_error *got_blame(const char *, struct got_object_id *,
struct got_repository *, FILE *);
+
+/*
+ * Like got_blame() but instead of generating an output file invoke
+ * a callback whenever an annotation has been computed for a line.
+ *
+ * The callback receives the provided void * argument, the total number
+ * of lines of the annotated file, a line number, and the ID of the commit
+ * which last changed this line.
+ */
+const struct got_error *got_blame_incremental(const char *,
+ struct got_object_id *, struct got_repository *,
+ const struct got_error *(*cb)(void *, int, int, struct got_object_id *),
+ void *);
blob - 52bd46fe2b3ac6189e515513a49f08371cabec86
blob + 16aa7b925adeefc4dbd6703da310ba7a98900ff4
--- include/got_object.h
+++ include/got_object.h
/*
* Read the entire content of a blob and write it to the specified file.
- * Flush and rewind the file as well, and indicate the amount of bytes
- * written in the size_t output argument.
+ * Flush and rewind the file as well. Indicate the amount of bytes
+ * written in the first size_t output argument, and the number of lines
+ * in the file in the second size_t output argument (NULL can be passed
+ * for either output argument).
*/
-const struct got_error *got_object_blob_dump_to_file(size_t *, FILE *,
- struct got_blob_object *);
+const struct got_error *got_object_blob_dump_to_file(size_t *, size_t *,
+ FILE *, struct got_blob_object *);
const struct got_error *
got_object_open_as_commit(struct got_commit_object **,
blob - 7f25ad2d36b9f21d0db48ab55c858472cf281a7e
blob + 1a2d03cda24f7afe7ef3cb27b7b9683cbf87fe65
--- lib/blame.c
+++ lib/blame.c
};
static const struct got_error *
-dump_blob_and_count_lines(size_t *nlines, FILE *outfile,
- struct got_blob_object *blob)
+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)
{
const struct got_error *err = NULL;
- size_t len, hdrlen;
- const uint8_t *buf;
- int i;
-
- hdrlen = got_object_blob_get_hdrlen(blob);
- *nlines = 0;
- do {
- err = got_object_blob_read_block(&len, blob);
- if (err)
- return err;
- if (len == 0)
- break;
- buf = got_object_blob_get_read_buf(blob);
- for (i = 0; i < len; i++) {
- if (buf[i] == '\n')
- (*nlines)++;
- }
- /* Skip blob object header first time around. */
- fwrite(buf + hdrlen, len - hdrlen, 1, outfile);
- hdrlen = 0;
- } while (len != 0);
-
-
- fflush(outfile);
- rewind(outfile);
-
- return NULL;
-}
-
-static void
-annotate_line(struct got_blame *blame, int lineno, struct got_object_id *id)
-{
struct got_blame_line *line;
if (lineno < 1 || lineno > blame->nlines)
- return;
+ return got_error(GOT_ERR_RANGE);
line = &blame->lines[lineno - 1];
if (line->annotated)
- return;
+ return NULL;
memcpy(&line->id, id, sizeof(line->id));
line->annotated = 1;
+ if (cb)
+ err = cb(arg, blame->nlines, lineno, id);
+ return err;
}
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)
+ struct got_object_id *pid, const char *path, struct got_repository *repo,
+ const struct got_error *(*cb)(void *, int, int, struct got_object_id *),
+ void *arg)
{
const struct got_error *err = NULL;
struct got_object *obj = NULL, *pobj = NULL;
int a = change->cv.a;
int b = change->cv.b;
int lineno;
- for (lineno = a; lineno <= b; lineno++)
- annotate_line(blame, lineno, id);
+ for (lineno = a; lineno <= b; lineno++) {
+ err = annotate_line(blame, lineno, id, cb, arg);
+ if (err)
+ goto done;
+ }
}
}
done:
static const struct got_error *
blame_open(struct got_blame **blamep, const char *path,
- struct got_object_id *start_commit_id, struct got_repository *repo)
+ struct got_object_id *start_commit_id, struct got_repository *repo,
+ const struct got_error *(*cb)(void *, int, int, struct got_object_id *),
+ void *arg)
{
const struct got_error *err = NULL;
struct got_object *obj = NULL;
err = got_error_from_errno();
goto done;
}
- err = dump_blob_and_count_lines(&blame->nlines, blame->f, blob);
+ err = got_object_blob_dump_to_file(NULL, &blame->nlines, blame->f,
+ blob);
if (err)
goto done;
if (pid == NULL)
break;
- err = blame_commit(blame, id, pid->id, path, repo);
+ err = blame_commit(blame, id, pid->id, path, repo, cb, arg);
if (err) {
if (err->code == GOT_ERR_ITER_COMPLETED)
err = NULL;
got_object_commit_close(commit);
err = got_object_open_as_commit(&commit, repo, id);
if (err)
- break;
+ goto done;
}
/* Annotate remaining non-annotated lines with last commit. */
- for (lineno = 1; lineno <= blame->nlines; lineno++)
- annotate_line(blame, lineno, id);
+ for (lineno = 1; lineno <= blame->nlines; lineno++) {
+ err = annotate_line(blame, lineno, id, cb, arg);
+ if (err)
+ goto done;
+ }
done:
free(id);
if (asprintf(&abspath, "%s%s", path[0] == '/' ? "" : "/", path) == -1)
return got_error_from_errno();
- err = blame_open(&blame, abspath, start_commit_id, repo);
+ err = blame_open(&blame, abspath, start_commit_id, repo, NULL, NULL);
if (err) {
free(abspath);
return err;
free(abspath);
return err;
}
+
+const struct got_error *
+got_blame_incremental(const char *path, struct got_object_id *commit_id,
+ struct got_repository *repo,
+ const struct got_error *(*cb)(void *, int, int, struct got_object_id *),
+ void *arg)
+{
+ const struct got_error *err = NULL;
+ struct got_blame *blame;
+ char *abspath;
+
+ if (asprintf(&abspath, "%s%s", path[0] == '/' ? "" : "/", path) == -1)
+ return got_error_from_errno();
+
+ err = blame_open(&blame, abspath, commit_id, repo, cb, arg);
+ free(abspath);
+ blame_close(blame);
+ return err;
+}
blob - abdee829510822e65a88b390d5b9f2bddb49d525
blob + 8fc337d544e52f440acacbbc75a073499ead13c7
--- lib/diff.c
+++ lib/diff.c
size1 = 0;
if (blob1) {
idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
- err = got_object_blob_dump_to_file(&size1, f1, blob1);
+ err = got_object_blob_dump_to_file(&size1, NULL, f1, blob1);
if (err)
goto done;
} else
size2 = 0;
if (blob2) {
idstr2 = got_object_blob_id_str(blob2, hex2, sizeof(hex2));
- err = got_object_blob_dump_to_file(&size2, f2, blob2);
+ err = got_object_blob_dump_to_file(&size2, NULL, f2, blob2);
if (err)
goto done;
} else
blob - 4cf62dd8461ada927c3e2522739feadbe7a4c211
blob + 8bc417f477f5c5bb085172a3adf35471edc7f006
--- lib/object.c
+++ lib/object.c
}
const struct got_error *
-got_object_blob_dump_to_file(size_t *total_len, FILE *outfile,
- struct got_blob_object *blob)
+got_object_blob_dump_to_file(size_t *total_len, size_t *nlines,
+ FILE *outfile, struct got_blob_object *blob)
{
const struct got_error *err = NULL;
size_t len, hdrlen;
+ const uint8_t *buf;
+ int i;
+
+ if (total_len)
+ *total_len = 0;
+ if (nlines)
+ *nlines = 0;
- *total_len = 0;
hdrlen = got_object_blob_get_hdrlen(blob);
do {
err = got_object_blob_read_block(&len, blob);
return err;
if (len == 0)
break;
- *total_len += len;
+ if (total_len)
+ *total_len += len;
+ buf = got_object_blob_get_read_buf(blob);
+ if (nlines) {
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '\n')
+ (*nlines)++;
+ }
+ }
/* Skip blob object header first time around. */
- fwrite(got_object_blob_get_read_buf(blob) + hdrlen,
- len - hdrlen, 1, outfile);
+ fwrite(buf + hdrlen, len - hdrlen, 1, outfile);
hdrlen = 0;
} while (len != 0);
blob - 76ebd6a1e50dc0e61ce0a0a9ad35306dff853b52
blob + 06bda662dc489c32771945b9cdb976211c884a6e
--- tog/Makefile
+++ tog/Makefile
sha1.c worktree.c utf8.c inflate.c
CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib
-LDADD = -lpanel -lncursesw -lutil -lz
+LDADD = -lpanel -lncursesw -lutil -lz -lpthread
DPADD = ${LIBZ} ${LIBUTIL}
.if defined(PROFILE)
CC = gcc
blob - c66824027f131e9b4a8456718212eb87e469c90c
blob + 343f4c6f06df7a59fcff7d6df42e716c0f0b1ea3
--- tog/tog.c
+++ tog/tog.c
#include <limits.h>
#include <wchar.h>
#include <time.h>
+#include <pthread.h>
#include "got_error.h"
#include "got_object.h"
getprogname());
exit(1);
}
+
+struct tog_blame_line {
+ int annotated;
+ struct got_object_id *id;
+};
static const struct got_error *
+draw_blame(WINDOW *window, FILE *f, struct tog_blame_line *lines, int nlines,
+ int *first_displayed_line, int *last_displayed_line, int *eof,
+ int max_lines)
+{
+ const struct got_error *err;
+ int lineno = 0, nprinted = 0;
+ char *line;
+ size_t len;
+ wchar_t *wline;
+ int width;
+ struct tog_blame_line *blame_line;
+
+ rewind(f);
+ werase(window);
+
+ *eof = 0;
+ while (nprinted < max_lines) {
+ line = parse_next_line(f, &len);
+ if (line == NULL) {
+ *eof = 1;
+ break;
+ }
+ if (++lineno < *first_displayed_line) {
+ free(line);
+ continue;
+ }
+
+ err = format_line(&wline, &width, line, COLS - 9);
+ if (err) {
+ free(line);
+ return err;
+ }
+
+ blame_line = &lines[lineno - 1];
+ if (blame_line->annotated) {
+ char *id_str;
+ err = got_object_id_str(&id_str, blame_line->id);
+ if (err) {
+ free(line);
+ return err;
+ }
+ wprintw(window, "%.8s ", id_str);
+ free(id_str);
+ } else
+ waddstr(window, " ");
+
+ waddwstr(window, wline);
+ if (width < COLS - 9)
+ waddch(window, '\n');
+ if (++nprinted == 1)
+ *first_displayed_line = lineno;
+ free(line);
+ }
+ *last_displayed_line = lineno;
+
+ update_panels();
+ doupdate();
+
+ return NULL;
+}
+
+struct tog_blame_cb_args {
+ pthread_mutex_t *mutex;
+ struct tog_blame_line *lines; /* one per line */
+ int nlines;
+
+ FILE *f;
+ WINDOW *window;
+ int *first_displayed_line;
+ int *last_displayed_line;
+};
+
+static const struct got_error *
+blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
+{
+ const struct got_error *err = NULL;
+ struct tog_blame_cb_args *a = arg;
+ struct tog_blame_line *line;
+ int eof;
+
+ if (nlines != a->nlines || lineno < 1 || lineno > a->nlines)
+ return got_error(GOT_ERR_RANGE);
+
+ if (pthread_mutex_lock(a->mutex) != 0)
+ return got_error_from_errno();
+
+ line = &a->lines[lineno - 1];
+ line->id = got_object_id_dup(id);
+ if (line->id == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ line->annotated = 1;
+
+ err = draw_blame(a->window, a->f, a->lines, a->nlines,
+ a->first_displayed_line, a->last_displayed_line, &eof, LINES);
+done:
+ if (pthread_mutex_unlock(a->mutex) != 0)
+ return got_error_from_errno();
+ return err;
+}
+
+struct tog_blame_thread_args {
+ const char *path;
+ struct got_object_id *commit_id;
+ struct got_repository *repo;
+ void *blame_cb_args;
+};
+
+static void *
+blame_thread(void *arg)
+{
+ struct tog_blame_thread_args *a = arg;
+ return (void *)got_blame_incremental(a->path, a->commit_id, a->repo,
+ blame_cb, a->blame_cb_args);
+}
+
+static const struct got_error *
show_blame_view(const char *path, struct got_object_id *commit_id,
struct got_repository *repo)
{
const struct got_error *err = NULL;
- FILE *f;
int ch, done = 0, first_displayed_line = 1, last_displayed_line = LINES;
int eof, i;
+ struct got_object *obj = NULL;
+ struct got_blob_object *blob = NULL;
+ FILE *f = NULL;
+ size_t filesize, nlines;
+ struct tog_blame_line *lines = NULL;
+ pthread_t thread = NULL;
+ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ struct tog_blame_cb_args blame_cb_args;
+ struct tog_blame_thread_args blame_thread_args;
- f = got_opentemp();
- if (f == NULL)
- return got_error_from_errno();
+ err = got_object_open_by_path(&obj, repo, commit_id, path);
+ if (err)
+ goto done;
+ if (got_object_get_type(obj) != GOT_OBJ_TYPE_BLOB) {
+ err = got_error(GOT_ERR_OBJ_TYPE);
+ got_object_close(obj);
+ goto done;
+ }
- err = got_blame(path, commit_id, repo, f);
+ err = got_object_blob_open(&blob, repo, obj, 8192);
+ got_object_close(obj);
if (err)
goto done;
+ f = got_opentemp();
+ if (f == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ err = got_object_blob_dump_to_file(&filesize, &nlines, f, blob);
+ if (err)
+ goto done;
- fflush(f);
+ lines = calloc(nlines, sizeof(*lines));
+ if (lines == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
if (tog_blame_view.window == NULL) {
tog_blame_view.window = newwin(0, 0, 0, 0);
} else
show_panel(tog_blame_view.panel);
+ if (pthread_mutex_init(&mutex, NULL) != 0) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ blame_cb_args.lines = lines;
+ blame_cb_args.nlines = nlines;
+ blame_cb_args.mutex = &mutex;
+ blame_cb_args.f = f;
+ blame_cb_args.window = tog_blame_view.window;
+ blame_cb_args.first_displayed_line = &first_displayed_line;
+ blame_cb_args.last_displayed_line = &last_displayed_line;
+
+ blame_thread_args.path = path;
+ blame_thread_args.commit_id = commit_id;
+ blame_thread_args.repo = repo;
+ blame_thread_args.blame_cb_args = &blame_cb_args;
+
+ if (pthread_create(&thread, NULL, blame_thread,
+ &blame_thread_args) != 0) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
while (!done) {
- err = draw_file(tog_blame_view.window, f, &first_displayed_line,
- &last_displayed_line, &eof, LINES);
+ if (pthread_mutex_lock(&mutex) != 0) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ err = draw_blame(tog_blame_view.window, f, lines, nlines,
+ &first_displayed_line, &last_displayed_line, &eof, LINES);
+ if (pthread_mutex_unlock(&mutex) != 0) {
+ err = got_error_from_errno();
+ goto done;
+ }
if (err)
break;
nodelay(stdscr, FALSE);
ch = wgetch(tog_blame_view.window);
nodelay(stdscr, TRUE);
+ if (pthread_mutex_lock(&mutex) != 0) {
+ err = got_error_from_errno();
+ goto done;
+ }
switch (ch) {
case 'q':
done = 1;
break;
default:
break;
+ }
+ if (pthread_mutex_unlock(&mutex) != 0) {
+ err = got_error_from_errno();
+ goto done;
}
}
done:
- fclose(f);
+ if (blob)
+ got_object_blob_close(blob);
+ if (f)
+ fclose(f);
+ free(lines);
+ if (thread) {
+ if (pthread_join(thread, (void **)&err) != 0)
+ err = got_error_from_errno();
+ }
return err;
}