summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@github.com>2016-06-15 17:17:26 -0500
committerEdward Thomson <ethomson@github.com>2016-06-25 18:05:44 -0400
commit2518419899d69762de4cbf5f64e83db7f0af6e0d (patch)
treeaf1b565fdfa0dd0ed8aab5aa015fd066517622de
parent1c89aadd73f4516bd6c7fa9dff39df69a2ee3316 (diff)
downloadlibgit2-2518419899d69762de4cbf5f64e83db7f0af6e0d.tar.gz
checkout: treat files as modified if index differs
When performing a forced checkout, treat files as modified when the workdir is identical to the target, but the index differs. This ensures that we update the index to match, instead of assuming that since the working directory does not differ, there's nothing to do. Otherwise we would keep the index as-is and the staged change would propagate past the force checkout.
-rw-r--r--src/checkout.c47
1 files changed, 42 insertions, 5 deletions
diff --git a/src/checkout.c b/src/checkout.c
index f39c341d4..b4e5e3d86 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -160,7 +160,7 @@ GIT_INLINE(bool) is_workdir_base_or_new(
git_oid__cmp(&newitem->id, workdir_id) == 0);
}
-static bool checkout_is_workdir_modified(
+static bool is_workdir_modified(
checkout_data *data,
const git_diff_file *baseitem,
const git_diff_file *newitem,
@@ -220,6 +220,43 @@ static bool checkout_is_workdir_modified(
return !is_workdir_base_or_new(&oid, baseitem, newitem);
}
+static bool is_index_modified(
+ checkout_data *data,
+ const git_diff_file *baseitem,
+ const git_diff_file *newitem,
+ const git_index_entry *wditem)
+{
+ const git_index_entry *ie;
+
+ /* Don't bother investigate if we're checking out the current
+ * index, it is canonical.
+ */
+ if (data->index == git_iterator_index(data->target))
+ return false;
+
+ if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) == NULL)
+ return true;
+
+ /* consider the index entry modified if it's different than both
+ * the base _and_ the target.
+ */
+ return (
+ (git_oid__cmp(&baseitem->id, &ie->id) != 0 ||
+ baseitem->mode != ie->mode) &&
+ (git_oid__cmp(&newitem->id, &ie->id) != 0 ||
+ newitem->mode != ie->mode));
+}
+
+static bool is_workdir_or_index_modified(
+ checkout_data *data,
+ const git_diff_file *baseitem,
+ const git_diff_file *newitem,
+ const git_index_entry *wditem)
+{
+ return is_workdir_modified(data, baseitem, newitem, wditem) ||
+ is_index_modified(data, baseitem, newitem, wditem);
+}
+
#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO)
@@ -463,7 +500,7 @@ static int checkout_action_with_wd(
switch (delta->status) {
case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
- if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) {
+ if (is_workdir_or_index_modified(data, &delta->old_file, &delta->new_file, wd)) {
GITERR_CHECK_ERROR(
checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
@@ -476,14 +513,14 @@ static int checkout_action_with_wd(
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
break;
case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
- if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+ if (is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
break;
case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
if (wd->mode != GIT_FILEMODE_COMMIT &&
- checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+ is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
@@ -506,7 +543,7 @@ static int checkout_action_with_wd(
} else
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
}
- else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+ else if (is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);