diff options
author | Edward Thomson <ethomson@github.com> | 2016-06-15 17:17:26 -0500 |
---|---|---|
committer | Edward Thomson <ethomson@github.com> | 2016-06-25 18:05:44 -0400 |
commit | 2518419899d69762de4cbf5f64e83db7f0af6e0d (patch) | |
tree | af1b565fdfa0dd0ed8aab5aa015fd066517622de | |
parent | 1c89aadd73f4516bd6c7fa9dff39df69a2ee3316 (diff) | |
download | libgit2-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.c | 47 |
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); |