mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 12:49:25 +00:00
Merge pull request #1764 from ethomson/status_renames_from_rewrites
Add rename from rewrites to status
This commit is contained in:
commit
fbb6c0c84c
@ -107,7 +107,7 @@ typedef enum {
|
||||
* - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
|
||||
* should be processed between the head and the index and enables
|
||||
* the GIT_STATUS_INDEX_RENAMED as a possible status flag.
|
||||
* - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename
|
||||
* - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
|
||||
* detection should be run between the index and the working directory
|
||||
* and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
|
||||
* - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
|
||||
@ -116,6 +116,8 @@ typedef enum {
|
||||
* - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
|
||||
* sensitivity for the file system and forces the output to be in
|
||||
* case-insensitive order
|
||||
* - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
|
||||
* should include rewritten files
|
||||
*
|
||||
* Calling `git_status_foreach()` is like calling the extended version
|
||||
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
||||
@ -134,6 +136,7 @@ typedef enum {
|
||||
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
|
||||
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
|
||||
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
|
||||
} git_status_opt_t;
|
||||
|
||||
#define GIT_STATUS_OPT_DEFAULTS \
|
||||
|
77
src/diff.c
77
src/diff.c
@ -258,6 +258,26 @@ int git_diff_delta__casecmp(const void *a, const void *b)
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
|
||||
{
|
||||
return delta->old_file.path ?
|
||||
delta->old_file.path : delta->new_file.path;
|
||||
}
|
||||
|
||||
int git_diff_delta__i2w_cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
int git_diff_delta__i2w_casecmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta)
|
||||
{
|
||||
@ -1276,7 +1296,7 @@ int git_diff__paired_foreach(
|
||||
git_diff_delta *h2i, *i2w;
|
||||
size_t i, j, i_max, j_max;
|
||||
int (*strcomp)(const char *, const char *) = git__strcmp;
|
||||
bool icase_mismatch;
|
||||
bool h2i_icase, i2w_icase, icase_mismatch;
|
||||
|
||||
i_max = head2idx ? head2idx->deltas.length : 0;
|
||||
j_max = idx2wd ? idx2wd->deltas.length : 0;
|
||||
@ -1291,24 +1311,35 @@ int git_diff__paired_foreach(
|
||||
* Therefore the main thing we need to do here is make sure the diffs
|
||||
* are traversed in a compatible order. To do this, we temporarily
|
||||
* resort a mismatched diff to get the order correct.
|
||||
*
|
||||
* In order to traverse renames in the index->workdir, we need to
|
||||
* ensure that we compare the index name on both sides, so we
|
||||
* always sort by the old name in the i2w list.
|
||||
*/
|
||||
icase_mismatch =
|
||||
(head2idx != NULL && idx2wd != NULL &&
|
||||
((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
|
||||
h2i_icase = head2idx != NULL &&
|
||||
(head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
|
||||
/* force case-sensitive delta sort */
|
||||
if (icase_mismatch) {
|
||||
if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
} else {
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
i2w_icase = idx2wd != NULL &&
|
||||
(idx2wd->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
|
||||
icase_mismatch =
|
||||
(head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
|
||||
|
||||
if (icase_mismatch && h2i_icase) {
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
}
|
||||
else if (head2idx != NULL && head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)
|
||||
|
||||
if (i2w_icase && !icase_mismatch) {
|
||||
strcomp = git__strcasecmp;
|
||||
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
} else if (idx2wd != NULL) {
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < i_max || j < j_max; ) {
|
||||
h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
|
||||
i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
|
||||
@ -1332,14 +1363,16 @@ int git_diff__paired_foreach(
|
||||
}
|
||||
|
||||
/* restore case-insensitive delta sort */
|
||||
if (icase_mismatch) {
|
||||
if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
} else {
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
if (icase_mismatch && h2i_icase) {
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
}
|
||||
|
||||
/* restore idx2wd sort by new path */
|
||||
if (idx2wd != NULL) {
|
||||
git_vector_set_cmp(&idx2wd->deltas,
|
||||
i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
29
src/status.c
29
src/status.c
@ -225,24 +225,6 @@ static git_status_list *git_status_list_alloc(git_index *index)
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
static int newfile_cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *delta_a = a;
|
||||
const git_diff_delta *delta_b = b;
|
||||
|
||||
return git__strcmp(delta_a->new_file.path, delta_b->new_file.path);
|
||||
}
|
||||
|
||||
static int newfile_casecmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *delta_a = a;
|
||||
const git_diff_delta *delta_b = b;
|
||||
|
||||
return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path);
|
||||
}
|
||||
*/
|
||||
|
||||
int git_status_list_new(
|
||||
git_status_list **out,
|
||||
git_repository *repo,
|
||||
@ -251,7 +233,7 @@ int git_status_list_new(
|
||||
git_index *index = NULL;
|
||||
git_status_list *status = NULL;
|
||||
git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_tree *head = NULL;
|
||||
git_status_show_t show =
|
||||
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
@ -284,6 +266,7 @@ int git_status_list_new(
|
||||
}
|
||||
|
||||
diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
|
||||
findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
|
||||
|
||||
if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
|
||||
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
@ -300,7 +283,9 @@ int git_status_list_new(
|
||||
if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
|
||||
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
|
||||
|
||||
findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED;
|
||||
if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
|
||||
findopt.flags = findopt.flags | GIT_DIFF_FIND_AND_BREAK_REWRITES |
|
||||
GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
|
||||
|
||||
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
|
||||
if ((error = git_diff_tree_to_index(
|
||||
@ -308,7 +293,7 @@ int git_status_list_new(
|
||||
goto done;
|
||||
|
||||
if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
|
||||
(error = git_diff_find_similar(status->head2idx, NULL)) < 0)
|
||||
(error = git_diff_find_similar(status->head2idx, &findopt)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -318,7 +303,7 @@ int git_status_list_new(
|
||||
goto done;
|
||||
|
||||
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
|
||||
(error = git_diff_find_similar(status->idx2wd, &findopts_i2w)) < 0)
|
||||
(error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -153,6 +153,65 @@ void test_status_renames__head2index_two(void)
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_status_renames__head2index_no_rename_from_rewrite(void)
|
||||
{
|
||||
git_index *index;
|
||||
git_status_list *statuslist;
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
struct status_entry expected[] = {
|
||||
{ GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
|
||||
{ GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" },
|
||||
};
|
||||
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
|
||||
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
|
||||
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
|
||||
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
|
||||
|
||||
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
|
||||
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
|
||||
cl_git_pass(git_index_write(index));
|
||||
|
||||
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||
test_status(statuslist, expected, 2);
|
||||
git_status_list_free(statuslist);
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_status_renames__head2index_rename_from_rewrite(void)
|
||||
{
|
||||
git_index *index;
|
||||
git_status_list *statuslist;
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
struct status_entry expected[] = {
|
||||
{ GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" },
|
||||
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" },
|
||||
};
|
||||
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
|
||||
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
|
||||
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
|
||||
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
|
||||
|
||||
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
|
||||
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
|
||||
cl_git_pass(git_index_write(index));
|
||||
|
||||
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||
test_status(statuslist, expected, 2);
|
||||
git_status_list_free(statuslist);
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_status_renames__index2workdir_one(void)
|
||||
{
|
||||
git_status_list *statuslist;
|
||||
@ -197,6 +256,32 @@ void test_status_renames__index2workdir_two(void)
|
||||
git_status_list_free(statuslist);
|
||||
}
|
||||
|
||||
void test_status_renames__index2workdir_rename_from_rewrite(void)
|
||||
{
|
||||
git_index *index;
|
||||
git_status_list *statuslist;
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
struct status_entry expected[] = {
|
||||
{ GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" },
|
||||
{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" },
|
||||
};
|
||||
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
|
||||
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
|
||||
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
|
||||
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
|
||||
|
||||
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||
test_status(statuslist, expected, 2);
|
||||
git_status_list_free(statuslist);
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_status_renames__both_one(void)
|
||||
{
|
||||
git_index *index;
|
||||
@ -274,6 +359,50 @@ void test_status_renames__both_two(void)
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
|
||||
void test_status_renames__both_rename_from_rewrite(void)
|
||||
{
|
||||
git_index *index;
|
||||
git_status_list *statuslist;
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
struct status_entry expected[] = {
|
||||
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||
"songof7cities.txt", "ikeepsix.txt" },
|
||||
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||
"ikeepsix.txt", "sixserving.txt" },
|
||||
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||
"sixserving.txt", "songof7cities.txt" },
|
||||
};
|
||||
|
||||
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
|
||||
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
|
||||
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
|
||||
rename_file(g_repo, "songof7cities.txt", "sixserving.txt");
|
||||
rename_file(g_repo, "_temp_.txt", "songof7cities.txt");
|
||||
|
||||
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
|
||||
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
|
||||
cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
|
||||
cl_git_pass(git_index_write(index));
|
||||
|
||||
rename_file(g_repo, "songof7cities.txt", "_temp_.txt");
|
||||
rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt");
|
||||
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
|
||||
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
|
||||
|
||||
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||
test_status(statuslist, expected, 3);
|
||||
git_status_list_free(statuslist);
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_status_renames__both_casechange_one(void)
|
||||
{
|
||||
git_index *index;
|
||||
|
Loading…
Reference in New Issue
Block a user