vendor/libgit2/src/checkout.c in rugged-0.26.3 vs vendor/libgit2/src/checkout.c in rugged-0.26.6
- old
+ new
@@ -157,10 +157,23 @@
{
return (git_oid__cmp(&baseitem->id, workdir_id) == 0 ||
git_oid__cmp(&newitem->id, workdir_id) == 0);
}
+GIT_INLINE(bool) is_file_mode_changed(git_filemode_t a, git_filemode_t b)
+{
+#ifdef GIT_WIN32
+ /*
+ * On Win32 we do not support the executable bit; the file will
+ * always be 0100644 on disk, don't bother doing a test.
+ */
+ return false;
+#else
+ return (S_ISREG(a) && S_ISREG(b) && a != b);
+#endif
+}
+
static bool checkout_is_workdir_modified(
checkout_data *data,
const git_diff_file *baseitem,
const git_diff_file *newitem,
const git_index_entry *wditem)
@@ -190,20 +203,27 @@
git_submodule_free(sm);
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)
- 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
* the actual size of the data, so we can't rely on this shortcut.
*/
@@ -212,10 +232,13 @@
/* if the workdir item is a directory, it cannot be a modified file */
if (S_ISDIR(wditem->mode))
return false;
+ if (is_file_mode_changed(baseitem->mode, wditem->mode))
+ return true;
+
if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
return false;
/* Allow the checkout if the workdir is not modified *or* if the checkout
* target's contents are already in the working directory.
@@ -1247,17 +1270,17 @@
git_diff_delta *delta)
{
unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
if (action & CHECKOUT_ACTION__REMOVE) {
- if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
+ if (!git_path_isvalid(repo, delta->old_file.path, delta->old_file.mode, flags)) {
giterr_set(GITERR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path);
return -1;
}
}
if (action & ~CHECKOUT_ACTION__REMOVE) {
- if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
+ if (!git_path_isvalid(repo, delta->new_file.path, delta->new_file.mode, flags)) {
giterr_set(GITERR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path);
return -1;
}
}