Merge pull request #4668 from novalis/bad-stash Fix stash save bug with fast path index check
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
diff --git a/src/diff_generate.c b/src/diff_generate.c
index e11cbe4..0105c17 100644
--- a/src/diff_generate.c
+++ b/src/diff_generate.c
@@ -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:
diff --git a/tests/stash/save.c b/tests/stash/save.c
index edcee82..5c2d494 100644
--- a/tests/stash/save.c
+++ b/tests/stash/save.c
@@ -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)
{