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; } }