mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
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.
This commit is contained in:
@@ -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.
|
||||
|
||||
24
src/blame.c
24
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
bc7c5ac2bafe828a68e9d1d460343718d6fbe136
|
||||
6653ff42313eb5c82806f145391b18a9699800c7
|
||||
|
||||
Reference in New Issue
Block a user