summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2018-02-20 16:14:54 +0000
committerGitHub <noreply@github.com>2018-02-20 16:14:54 +0000
commit894ccf4b162f87745ce890485f198374e9404152 (patch)
tree1288dac33a8e54a36a5a7b49b8a66f8e27dc8fdf
parentafc5124bb6ce8ee9f3741276dd7427b0a499cef3 (diff)
parent4e4771dcc77c6f26b4a0b296d68f7b310cad0076 (diff)
downloadlibgit2-894ccf4b162f87745ce890485f198374e9404152.tar.gz
Merge pull request #4535 from libgit2/ethomson/checkout_typechange_with_index_and_wd
checkout: when examining index (instead of workdir), also examine mode
-rw-r--r--src/checkout.c26
-rw-r--r--tests/checkout/head.c45
2 files changed, 61 insertions, 10 deletions
diff --git a/src/checkout.c b/src/checkout.c
index 528fbdf92..2ad736afe 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -205,17 +205,23 @@ static bool checkout_is_workdir_modified(
return rval;
}
- /* Look at the cache to decide if the workdir is modified. If not,
- * we can simply compare the oid in the cache to the baseitem instead
- * of hashing the file. If so, we allow the checkout to proceed if the
- * oid is identical (ie, the staged item is what we're trying to check
- * out.)
+ /*
+ * Look at the cache to decide if the workdir is modified: if the
+ * cache contents match the workdir contents, then we do not need
+ * to examine the working directory directly, instead we can
+ * examine the cache to see if _it_ has been modified. This allows
+ * us to avoid touching the disk.
*/
- if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
- if (git_index_time_eq(&wditem->mtime, &ie->mtime) &&
- wditem->file_size == ie->file_size &&
- !is_file_mode_changed(wditem->mode, ie->mode))
- return !is_workdir_base_or_new(&ie->id, baseitem, newitem);
+ ie = git_index_get_bypath(data->index, wditem->path, 0);
+
+ if (ie != NULL &&
+ git_index_time_eq(&wditem->mtime, &ie->mtime) &&
+ wditem->file_size == ie->file_size &&
+ !is_file_mode_changed(wditem->mode, ie->mode)) {
+
+ /* The workdir is modified iff the index entry is modified */
+ return !is_workdir_base_or_new(&ie->id, baseitem, newitem) ||
+ is_file_mode_changed(baseitem->mode, ie->mode);
}
/* depending on where base is coming from, we may or may not know
diff --git a/tests/checkout/head.c b/tests/checkout/head.c
index ded86df33..46b22570d 100644
--- a/tests/checkout/head.c
+++ b/tests/checkout/head.c
@@ -136,3 +136,48 @@ void test_checkout_head__do_remove_tracked_subdir(void)
cl_assert(!git_path_isfile("testrepo/subdir/tracked-file"));
cl_assert(git_path_isfile("testrepo/subdir/untracked-file"));
}
+
+void test_checkout_head__typechange_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target;
+ struct stat st;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/new.txt", 0755));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(p_stat("testrepo/new.txt", &st));
+ cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
+
+ git_object_free(target);
+}
+
+void test_checkout_head__typechange_index_and_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target;
+ git_index *index;
+ struct stat st;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/new.txt", 0755));
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "new.txt"));
+ cl_git_pass(git_index_write(index));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(p_stat("testrepo/new.txt", &st));
+ cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
+
+ git_object_free(target);
+ git_index_free(index);
+}