From cb1cb24ca9588e43edfb8b37e008c2e83af580af Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 10:18:58 +0100 Subject: [PATCH] blame: use size_t for line counts in git_blame_hunk It is not unreasonable to have versioned files with a line count exceeding 2^16. Upon blaming such files we fail to correctly keep track of the lines as `git_blame_hunk` stores them in `uint16_t` fields. Fix this by converting the line fields of `git_blame_hunk` to `size_t`. Add test to verify behavior. --- include/git2/blame.h | 12 ++++----- src/blame.c | 24 +++++++++--------- tests/blame/blame_helpers.c | 4 +-- tests/blame/blame_helpers.h | 6 ++--- tests/blame/simple.c | 12 +++++++++ .../37/681a80ca21064efd5c3bf2ef41eb3d05a1428b | Bin 0 -> 106 bytes .../4e/ecfea484f8005d101e547f6bfb07c99e2b114e | Bin 0 -> 163 bytes .../5a/572e2e94825f54b95417eacaa089d560c5a5e9 | Bin 0 -> 324 bytes .../66/53ff42313eb5c82806f145391b18a9699800c7 | Bin 0 -> 160 bytes .../ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 | Bin 0 -> 106 bytes .../de/9fe35f9906e1994e083cc59c87232bf418795b | Bin 0 -> 331 bytes .../resources/blametest.git/refs/heads/master | Bin 41 -> 41 bytes 12 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b create mode 100644 tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e create mode 100644 tests/resources/blametest.git/objects/5a/572e2e94825f54b95417eacaa089d560c5a5e9 create mode 100644 tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 create mode 100644 tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 create mode 100644 tests/resources/blametest.git/objects/de/9fe35f9906e1994e083cc59c87232bf418795b diff --git a/include/git2/blame.h b/include/git2/blame.h index 173e9994b..84bb7f94c 100644 --- a/include/git2/blame.h +++ b/include/git2/blame.h @@ -74,8 +74,8 @@ typedef struct git_blame_options { uint16_t min_match_characters; git_oid newest_commit; git_oid oldest_commit; - uint32_t min_line; - uint32_t max_line; + size_t min_line; + size_t max_line; } git_blame_options; #define GIT_BLAME_OPTIONS_VERSION 1 @@ -113,15 +113,15 @@ GIT_EXTERN(int) git_blame_init_options( * root, or the commit specified in git_blame_options.oldest_commit) */ typedef struct git_blame_hunk { - uint16_t lines_in_hunk; + size_t lines_in_hunk; git_oid final_commit_id; - uint16_t final_start_line_number; + size_t final_start_line_number; git_signature *final_signature; git_oid orig_commit_id; const char *orig_path; - uint16_t orig_start_line_number; + size_t orig_start_line_number; git_signature *orig_signature; char boundary; @@ -156,7 +156,7 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex( */ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline( git_blame *blame, - uint32_t lineno); + size_t lineno); /** * Get the blame for a single file. diff --git a/src/blame.c b/src/blame.c index 08a90dcfd..2daf91591 100644 --- a/src/blame.c +++ b/src/blame.c @@ -23,8 +23,8 @@ static int hunk_byfinalline_search_cmp(const void *key, const void *entry) git_blame_hunk *hunk = (git_blame_hunk*)entry; size_t lineno = *(size_t*)key; - size_t lines_in_hunk = (size_t)hunk->lines_in_hunk; - size_t final_start_line_number = (size_t)hunk->final_start_line_number; + size_t lines_in_hunk = hunk->lines_in_hunk; + size_t final_start_line_number = hunk->final_start_line_number; if (lineno < final_start_line_number) return -1; @@ -44,7 +44,7 @@ static int hunk_cmp(const void *_a, const void *_b) static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line) { - return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1); + return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1); } static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line) @@ -53,9 +53,9 @@ static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line) } static git_blame_hunk* new_hunk( - uint16_t start, - uint16_t lines, - uint16_t orig_start, + size_t start, + size_t lines, + size_t orig_start, const char *path) { git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk)); @@ -166,9 +166,9 @@ const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t inde return (git_blame_hunk*)git_vector_get(&blame->hunks, index); } -const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno) +const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno) { - size_t i, new_lineno = (size_t)lineno; + size_t i, new_lineno = lineno; assert(blame); if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) { @@ -223,8 +223,8 @@ static git_blame_hunk *split_hunk_in_vector( } new_line_count = hunk->lines_in_hunk - rel_line; - nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count, - (uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path); + nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count, + hunk->orig_start_line_number + rel_line, hunk->orig_path); if (!nh) return NULL; @@ -233,7 +233,7 @@ static git_blame_hunk *split_hunk_in_vector( git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id); /* Adjust hunk that was split */ - hunk->lines_in_hunk -= (uint16_t)new_line_count; + hunk->lines_in_hunk -= new_line_count; git_vector_insert_sorted(vec, nh, NULL); { git_blame_hunk *ret = return_new ? nh : hunk; @@ -442,7 +442,7 @@ static int buffer_line_cb( } else { /* Create a new buffer-blame hunk with this line */ shift_hunks_by(&blame->hunks, blame->current_diff_line, 1); - blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path); + blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path); GITERR_CHECK_ALLOC(blame->current_hunk); git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL); diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c index b305ba1e3..61e87350c 100644 --- a/tests/blame/blame_helpers.c +++ b/tests/blame/blame_helpers.c @@ -4,7 +4,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) { va_list arglist; - printf("Hunk %"PRIuZ" (line %d +%d): ", idx, + printf("Hunk %"PRIuZ" (line %"PRIuZ" +%"PRIuZ"): ", idx, hunk->final_start_line_number, hunk->lines_in_hunk-1); va_start(arglist, fmt); @@ -15,7 +15,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) } void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx, - int start_line, int len, char boundary, const char *commit_id, const char *orig_path) + size_t start_line, size_t len, char boundary, const char *commit_id, const char *orig_path) { char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0}; const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx); diff --git a/tests/blame/blame_helpers.h b/tests/blame/blame_helpers.h index 94321a5b5..fd5a35d2c 100644 --- a/tests/blame/blame_helpers.h +++ b/tests/blame/blame_helpers.h @@ -7,10 +7,8 @@ void check_blame_hunk_index( git_repository *repo, git_blame *blame, int idx, - int start_line, - int len, + size_t start_line, + size_t len, char boundary, const char *commit_id, const char *orig_path); - - diff --git a/tests/blame/simple.c b/tests/blame/simple.c index 83e5e056b..30b78168f 100644 --- a/tests/blame/simple.c +++ b/tests/blame/simple.c @@ -281,6 +281,18 @@ void test_blame_simple__can_restrict_lines_both(void) check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt"); } +void test_blame_simple__can_blame_huge_file(void) +{ + git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + + cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git"))); + + cl_git_pass(git_blame_file(&g_blame, g_repo, "huge.txt", &opts)); + cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame)); + check_blame_hunk_index(g_repo, g_blame, 0, 1, 65536, 0, "4eecfea", "huge.txt"); + check_blame_hunk_index(g_repo, g_blame, 1, 65537, 1, 0, "6653ff4", "huge.txt"); +} + /* * $ git blame -n branch_file.txt be3563a..HEAD * orig line no final line no diff --git a/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b b/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b new file mode 100644 index 0000000000000000000000000000000000000000..a6ca0fb71bc4e4628b259c54724aefb548e19f20 GIT binary patch literal 106 zcmV-w0G0oE0V^p=O;s>7Fk&z?FfcPQQApG)sVHIK*|p_L{584Ig(oxX=g-+tCG;@F z1F9eitl+)TeP7jy&XdhWk9!9GG`iWToN^JWAfq%r6|5#ITu*OGQ+&wI5b;;17Ia=s MIJ)#D0Ko1mZLd!-CIA2c literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e b/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e new file mode 100644 index 0000000000000000000000000000000000000000..79e0ada916ce5490fbde3d8007cf2b9dc2483c7b GIT binary patch literal 163 zcmV;U09^lg0gcZ~3c@fDfMM4;MRr{XlF4%lB7)#r@C1`&LQCm`G~QoO58wel`2V!d z^Vp<@{?ID3G{GYfsze%;w_LJICKk=b0!NdTBd{8y*r@W-WK1DBN;*LE=r8w`vFfo40Hcy7m3*$}=mr8YUcQU}R?FkuVr#U<5*a K0)x|A=6C=U8&RYH literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 b/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 new file mode 100644 index 0000000000000000000000000000000000000000..1f1140931319f17adda09ce614c93aca093ec200 GIT binary patch literal 160 zcmV;R0AK%j0hP~93c@fDKw;N8MRr{Xl9^7L1VjWEZUs*;NhVlIe@Nr~1wDZWc<{Zn z&hywbLO8T4qB(~YNjPDj3l^~gI1qZr);knNhEjY)wQ-fDX%b0Am^>A4jLl#`EV(ko z5{*kC2u1)B#qai7tA22+W194{$+*0@=BCfve%+@1G|m?dM83Dy1L>myV^sS3n*3j? O+i$JAcj^PtVntJpk4Ve_ literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 b/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 new file mode 100644 index 0000000000000000000000000000000000000000..077e658643b8f7d336e451489a3471e959f84683 GIT binary patch literal 106 zcmV-w0G0oE0V^p=O;s>7Fk&z?FfcPQQApG)sVHIK*|p_L{584Ig(oxX=g-+tCG;@F z1F9eitl+)TeP7jy&XdhWk9!9GG`iWToN^JWAfq%r6|Cmo{KxS#*&fdH;M1& literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/refs/heads/master b/tests/resources/blametest.git/refs/heads/master index b763025d8c3aa0971dcd4ac03c25690bb24c47c6..d1bc4ca6b35e80309e786a6bd051b8671a82a715 100644 GIT binary patch literal 41 vcmV~$!4Uu;2m`Rc(*OmeAc?$rJUM*Y*c{fcs1#dNE?HrsNw7Wu1q)yR