commit 78695fb724a1e3e628efb42140d48dbd88d562b3 from: Stefan Sperling date: Mon Aug 12 10:38:08 2019 UTC fix blame with single-commit history and with files without \n commit - 8d0fe45ad75f58cd5f797f72f4b621bab1282fdb commit + 78695fb724a1e3e628efb42140d48dbd88d562b3 blob - e00d1afadd8e8acb4b5fc54c640fe23ccfe9d3bd blob + 93f2913aab432dfc47a0e6bc7af1016b5e89ed80 --- lib/blame.c +++ lib/blame.c @@ -211,43 +211,45 @@ blame_commit(struct got_blame *blame, struct got_objec goto done; } - err = got_object_id_by_path(&pobj_id, repo, pid, path); - if (err) { - if (err->code == GOT_ERR_NO_TREE_ENTRY) { - /* Blob's history began in previous commit. */ - err = got_error(GOT_ERR_ITER_COMPLETED); + if (pid) { + err = got_object_id_by_path(&pobj_id, repo, pid, path); + if (err) { + if (err->code == GOT_ERR_NO_TREE_ENTRY) { + /* Blob's history began in previous commit. */ + err = got_error(GOT_ERR_ITER_COMPLETED); + } + goto done; } - goto done; - } - /* If IDs match then don't bother with diffing. */ - if (got_object_id_cmp(obj_id, pobj_id) == 0) { - if (cb) - err = cb(arg, blame->nlines, -1, id); - goto done; - } + /* If IDs match then don't bother with diffing. */ + if (got_object_id_cmp(obj_id, pobj_id) == 0) { + if (cb) + err = cb(arg, blame->nlines, -1, id); + goto done; + } - err = got_object_open(&pobj, repo, pobj_id); - if (err) - goto done; + err = got_object_open(&pobj, repo, pobj_id); + if (err) + goto done; - if (pobj->type != GOT_OBJ_TYPE_BLOB) { - /* - * Encountered a non-blob at the path (probably a tree). - * Blob's history began in previous commit. - */ - err = got_error(GOT_ERR_ITER_COMPLETED); - goto done; + if (pobj->type != GOT_OBJ_TYPE_BLOB) { + /* + * Encountered a non-blob at the path (probably a tree). + * Blob's history began in previous commit. + */ + err = got_error(GOT_ERR_ITER_COMPLETED); + goto done; + } + + err = got_object_blob_open(&pblob, repo, pobj, 8192); + if (err) + goto done; } err = got_object_blob_open(&blob, repo, obj, 8192); if (err) goto done; - err = got_object_blob_open(&pblob, repo, pobj, 8192); - if (err) - goto done; - err = got_diff_blob_lines_changed(&changes, pblob, blob); if (err) goto done; @@ -300,7 +302,7 @@ blame_open(struct got_blame **blamep, const char *path struct got_object_id *obj_id = NULL; struct got_blob_object *blob = NULL; struct got_blame *blame = NULL; - struct got_object_id *id = NULL; + struct got_object_id *id = NULL, *parent_id = NULL; int lineno; struct got_commit_graph *graph = NULL; @@ -352,12 +354,14 @@ blame_open(struct got_blame **blamep, const char *path id = NULL; for (;;) { - struct got_object_id *next_id; - - err = got_commit_graph_iter_next(&next_id, graph); + err = got_commit_graph_iter_next(&parent_id, graph); if (err) { if (err->code == GOT_ERR_ITER_COMPLETED) { - err = NULL; + if (id) + err = blame_commit(blame, id, + parent_id, path, repo, cb, arg); + else + err = NULL; break; } if (err->code != GOT_ERR_ITER_NEED_MORE) @@ -365,13 +369,10 @@ blame_open(struct got_blame **blamep, const char *path err = got_commit_graph_fetch_commits(graph, 1, repo); if (err) break; - else - continue; + continue; } - if (next_id == NULL) - break; if (id) { - err = blame_commit(blame, id, next_id, path, repo, + err = blame_commit(blame, id, parent_id, path, repo, cb, arg); if (err) { if (err->code == GOT_ERR_ITER_COMPLETED) @@ -381,7 +382,7 @@ blame_open(struct got_blame **blamep, const char *path if (blame->nannotated == blame->nlines) break; } - id = next_id; + id = parent_id; } if (id && blame->nannotated < blame->nlines) { blob - 42e0035977023ee76bd78dbd06474f29da5c19c5 blob + 90e8331aa0d5ab5db7909bb85d3c2623d80126eb --- lib/object.c +++ lib/object.c @@ -1161,34 +1161,46 @@ got_object_blob_dump_to_file(size_t *total_len, int *n if (len == 0) break; buf = got_object_blob_get_read_buf(blob); - for (i = hdrlen; i < len; i++) { - if (buf[i] != '\n') - continue; - if (nlines) - (*nlines)++; - if (line_offsets && nlines && noffsets < *nlines) { - off_t *o = recallocarray(*line_offsets, - noffsets, *nlines, sizeof(**line_offsets)); - if (o == NULL) { - free(*line_offsets); - *line_offsets = NULL; - return got_error_from_errno( - "recallocarray"); - } - *line_offsets = o; - noffsets = *nlines; + if (line_offsets && nlines) { + if (*line_offsets == NULL) { + /* Have some data but perhaps no '\n'. */ + noffsets = 1; + *nlines = 1; + *line_offsets = malloc(sizeof(**line_offsets)); + if (*line_offsets == NULL) + return got_error_from_errno("malloc"); + (*line_offsets)[0] = 0; } - if (line_offsets && nlines && total_len) { - (*line_offsets)[*nlines - 1] = off; - off = *total_len + i + 1 - got_object_blob_get_hdrlen(blob); + /* Scan '\n' offsets in this chunk of data. */ + for (i = hdrlen; i < len; i++) { + if (i > hdrlen && buf[i] == '\n') + (*nlines)++; + if (noffsets < *nlines) { + off_t *o = recallocarray(*line_offsets, + noffsets, *nlines, + sizeof(**line_offsets)); + if (o == NULL) { + free(*line_offsets); + *line_offsets = NULL; + return got_error_from_errno( + "recallocarray"); + } + *line_offsets = o; + noffsets = *nlines; + } + if (total_len) { + (*line_offsets)[*nlines - 1] = off; + off = *total_len + i - + got_object_blob_get_hdrlen(blob); + } } } - if (total_len) - *total_len += len; /* Skip blob object header first time around. */ n = fwrite(buf + hdrlen, 1, len - hdrlen, outfile); if (n != len - hdrlen) return got_ferror(outfile, GOT_ERR_IO); + if (total_len) + *total_len += len - hdrlen; hdrlen = 0; } while (len != 0); blob - ccd7980f4fa22dcd0dfb01b2e5a895ca27b01246 blob + bd295cf08b277cac199f4c1fc887139d66aecd2f --- regress/cmdline/blame.sh +++ regress/cmdline/blame.sh @@ -97,5 +97,63 @@ function test_blame_tag { test_done "$testroot" "$ret" } +function test_blame_file_single_line { + local testroot=`test_init blame_file_single_line` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo 1 > $testroot/wt/alpha + (cd $testroot/wt && got commit -m "change 1" > /dev/null) + local commit1=`git_show_head $testroot/repo` + + (cd $testroot/wt && got blame alpha > $testroot/stdout) + + local short_commit1=`trim_obj_id 32 $commit1` + + echo "$short_commit1 1" > $testroot/stdout.expected + + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + +function test_blame_file_single_line_no_newline { + local testroot=`test_init blame_file_single_line_no_newline` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo -n 1 > $testroot/wt/alpha + (cd $testroot/wt && got commit -m "change 1" > /dev/null) + local commit1=`git_show_head $testroot/repo` + + (cd $testroot/wt && got blame alpha > $testroot/stdout) + + local short_commit1=`trim_obj_id 32 $commit1` + + echo "$short_commit1 1" > $testroot/stdout.expected + + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + run_test test_blame_basic run_test test_blame_tag +run_test test_blame_file_single_line +run_test test_blame_file_single_line_no_newline