Commit 5a7d454b by David Turner

Fix stash save bug with fast path index check

If the index contains stat data for a modified file, and the file is
not racily dirty, and there exists an untracked working tree directory
alphabetically after that file, and there are no other changes to the
repo, then git_stash_save would fail. It would confuse the untracked
working tree directory for the modified file, because they have the
same sha: zero.  The wt directory has a sha of zero because it's a
directory, and the file would have a zero sha because we wouldn't read
the file -- we would just know that it doesn't match the index.  To
fix this confusion, we simply check mode as well as SHA.
parent bae6ed62
......@@ -273,7 +273,8 @@ static git_diff_delta *diff_delta__last_for_item(
break;
case GIT_DELTA_MODIFIED:
if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 ||
git_oid__cmp(&delta->new_file.id, &item->id) == 0)
(delta->new_file.mode == item->mode &&
git_oid__cmp(&delta->new_file.id, &item->id) == 0))
return delta;
break;
default:
......
......@@ -188,6 +188,46 @@ void test_stash_save__can_include_untracked_and_ignored_files(void)
cl_assert(!git_path_exists("stash/just.ignore"));
}
/*
* Note: this test was flaky prior to fixing #4101 -- run it several
* times to get a failure. The issues is that whether the fast
* (stat-only) codepath is used inside stash's diff operation depends
* on whether files are "racily clean", and there doesn't seem to be
* an easy way to force the exact required state.
*/
void test_stash_save__untracked_regression(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
const char *paths[] = {"what", "where", "how", "why"};
git_reference *head;
git_commit *head_commit;
git_buf untracked_dir;
const char* workdir = git_repository_workdir(repo);
git_buf_init(&untracked_dir, 0);
git_buf_printf(&untracked_dir, "%sz", workdir);
cl_assert(!p_mkdir(untracked_dir.ptr, 0777));
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
opts.paths.strings = (char **)paths;
opts.paths.count = 4;
cl_git_pass(git_checkout_tree(repo, (git_object*)head_commit, &opts));
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
assert_commit_message_contains("refs/stash", "WIP on master");
git_buf_free(&untracked_dir);
}
#define MESSAGE "Look Ma! I'm on TV!"
void test_stash_save__can_accept_a_message(void)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment