mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-20 00:42:38 +00:00
Fix bug making split deltas a COPIED targets
When FIND_COPIES is used in combination with BREAK_REWRITES for rename detection, there was a bug where the split MODIFIED delta was only used as a target for RENAME records and not for COPIED records. This fixes that, converting the split into a pair of DELETED and COPIED deltas when that circumstance arises.
This commit is contained in:
parent
14984af6cb
commit
2123a17f83
@ -366,12 +366,28 @@ static int normalize_find_opts(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insert_delete_side_of_split(
|
||||
git_diff *diff, git_vector *onto, const git_diff_delta *delta)
|
||||
{
|
||||
/* make new record for DELETED side of split */
|
||||
git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool);
|
||||
GITERR_CHECK_ALLOC(deleted);
|
||||
|
||||
deleted->status = GIT_DELTA_DELETED;
|
||||
deleted->nfiles = 1;
|
||||
memset(&deleted->new_file, 0, sizeof(deleted->new_file));
|
||||
deleted->new_file.path = deleted->old_file.path;
|
||||
deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
|
||||
return git_vector_insert(onto, deleted);
|
||||
}
|
||||
|
||||
static int apply_splits_and_deletes(
|
||||
git_diff *diff, size_t expected_size, bool actually_split)
|
||||
{
|
||||
git_vector onto = GIT_VECTOR_INIT;
|
||||
size_t i;
|
||||
git_diff_delta *delta, *deleted;
|
||||
git_diff_delta *delta;
|
||||
|
||||
if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
|
||||
return -1;
|
||||
@ -384,17 +400,7 @@ static int apply_splits_and_deletes(
|
||||
if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) {
|
||||
delta->similarity = 0;
|
||||
|
||||
/* make new record for DELETED side of split */
|
||||
if (!(deleted = diff_delta__dup(delta, &diff->pool)))
|
||||
goto on_error;
|
||||
|
||||
deleted->status = GIT_DELTA_DELETED;
|
||||
deleted->nfiles = 1;
|
||||
memset(&deleted->new_file, 0, sizeof(deleted->new_file));
|
||||
deleted->new_file.path = deleted->old_file.path;
|
||||
deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
|
||||
if (git_vector_insert(&onto, deleted) < 0)
|
||||
if (insert_delete_side_of_split(diff, &onto, delta) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR)
|
||||
@ -1058,10 +1064,7 @@ find_best_matches:
|
||||
}
|
||||
}
|
||||
|
||||
else if (delta_is_new_only(tgt)) {
|
||||
if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES))
|
||||
continue;
|
||||
|
||||
else if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
|
||||
if (tgt2src_copy[t].similarity < opts.copy_threshold)
|
||||
continue;
|
||||
|
||||
@ -1069,10 +1072,21 @@ find_best_matches:
|
||||
best_match = &tgt2src_copy[t];
|
||||
src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
|
||||
|
||||
if (delta_is_split(tgt)) {
|
||||
error = insert_delete_side_of_split(diff, &diff->deltas, tgt);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
num_rewrites--;
|
||||
}
|
||||
|
||||
if (!delta_is_split(tgt) && !delta_is_new_only(tgt))
|
||||
continue;
|
||||
|
||||
tgt->status = GIT_DELTA_COPIED;
|
||||
tgt->similarity = best_match->similarity;
|
||||
tgt->nfiles = 2;
|
||||
memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
|
||||
tgt->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
|
||||
num_updates++;
|
||||
}
|
||||
|
@ -1284,3 +1284,52 @@ void test_diff_rename__rewrite_on_single_file(void)
|
||||
git_diff_free(diff);
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_diff_rename__can_find_copy_to_split(void)
|
||||
{
|
||||
git_buf c1 = GIT_BUF_INIT;
|
||||
git_index *index;
|
||||
git_tree *tree;
|
||||
git_diff *diff;
|
||||
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
diff_expects exp;
|
||||
|
||||
cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
|
||||
cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0));
|
||||
|
||||
cl_git_pass(
|
||||
git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
cl_git_pass(git_index_read_tree(index, tree));
|
||||
cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
|
||||
|
||||
diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
|
||||
|
||||
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]);
|
||||
|
||||
opts.flags = GIT_DIFF_FIND_ALL;
|
||||
cl_git_pass(git_diff_find_similar(diff, &opts));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||
cl_assert_equal_i(5, exp.files);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
|
||||
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]);
|
||||
|
||||
git_diff_free(diff);
|
||||
git_tree_free(tree);
|
||||
git_index_free(index);
|
||||
|
||||
git_buf_free(&c1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user