@@ -25,6 +25,8 @@
#include "cache-tree.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "dir.h"
+#include "entry.h"
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -127,12 +129,49 @@ static void update_index_from_diff(struct diff_queue_struct *q,
struct diff_options *opt, void *data)
{
int i;
+ int pos;
int intent_to_add = *(int *)data;
for (i = 0; i < q->nr; i++) {
struct diff_filespec *one = q->queue[i]->one;
+ struct diff_filespec *two = q->queue[i]->two;
int is_missing = !(one->mode && !is_null_oid(&one->oid));
+ int was_missing = !two->mode && is_null_oid(&two->oid);
struct cache_entry *ce;
+ struct cache_entry *ce_before;
+ struct checkout state = CHECKOUT_INIT;
+
+ /*
+ * When using the sparse-checkout feature the cache entries
+ * that are added here will not have the skip-worktree bit
+ * set. Without this code there is data that is lost because
+ * the files that would normally be in the working directory
+ * are not there and show as deleted for the next status.
+ * In the case of added files, they just disappear.
+ *
+ * We need to create the previous version of the files in
+ * the working directory so that they will have the right
+ * content and the next status call will show modified or
+ * untracked files correctly.
+ */
+ if (core_apply_sparse_checkout && !file_exists(two->path)) {
+ pos = cache_name_pos(two->path, strlen(two->path));
+ if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) &&
+ (is_missing || !was_missing)) {
+ state.force = 1;
+ state.refresh_cache = 1;
+ state.istate = &the_index;
+
+ ce_before = make_cache_entry(&the_index, two->mode,
+ &two->oid, two->path,
+ 0, 0);
+ if (!ce_before)
+ die(_("make_cache_entry failed for path '%s'"),
+ two->path);
+
+ checkout_entry(ce_before, &state, NULL, NULL);
+ }
+ }
if (is_missing && !intent_to_add) {
remove_file_from_cache(one->path);
@@ -459,9 +459,7 @@ test_expect_failure 'blame with pathspec outside sparse definition' '
test_all_match git blame deep/deeper2/deepest/a
'
-# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
-# in this scenario, but it shouldn't.
-test_expect_failure 'checkout and reset (mixed)' '
+test_expect_success 'checkout and reset (mixed)' '
init_repos &&
test_all_match git checkout -b reset-test update-deep &&
new file mode 100755
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='reset when using a sparse-checkout'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_tick &&
+ echo "checkout file" >c &&
+ echo "modify file" >m &&
+ echo "delete file" >d &&
+ git add . &&
+ git commit -m "initial commit" &&
+ echo "added file" >a &&
+ echo "modification of a file" >m &&
+ git rm d &&
+ git add . &&
+ git commit -m "second commit" &&
+ git checkout -b endCommit
+'
+
+test_expect_success 'reset when there is a sparse-checkout' '
+ echo "/c" >.git/info/sparse-checkout &&
+ test_config core.sparsecheckout true &&
+ git checkout -B resetBranch &&
+ test_path_is_missing m &&
+ test_path_is_missing a &&
+ test_path_is_missing d &&
+ git reset HEAD~1 &&
+ echo "checkout file" >expect &&
+ test_cmp expect c &&
+ echo "added file" >expect &&
+ test_cmp expect a &&
+ echo "modification of a file" >expect &&
+ test_cmp expect m &&
+ test_path_is_missing d
+'
+
+test_expect_success 'reset after deleting file without skip-worktree bit' '
+ git checkout -f endCommit &&
+ git clean -xdf &&
+ cat >.git/info/sparse-checkout <<-\EOF &&
+ /c
+ /m
+ EOF
+ test_config core.sparsecheckout true &&
+ git checkout -B resetAfterDelete &&
+ test_path_is_file m &&
+ test_path_is_missing a &&
+ test_path_is_missing d &&
+ rm -f m &&
+ git reset HEAD~1 &&
+ echo "checkout file" >expect &&
+ test_cmp expect c &&
+ echo "added file" >expect &&
+ test_cmp expect a &&
+ test_path_is_missing m &&
+ test_path_is_missing d
+'
+
+test_done