Skip to content

Commit 5a9253c

Browse files
committed
Merge branch 'vd/sparse-stash'
Teach "git stash" to work better with sparse index entries. * vd/sparse-stash: unpack-trees: preserve index sparsity stash: apply stash using 'merge_ort_nonrecursive()' read-cache: set sparsity when index is new sparse-index: expose 'is_sparse_index_allowed()' stash: integrate with sparse index stash: expand sparse-checkout compatibility testing
2 parents 945b9f2 + 0f329b9 commit 5a9253c

File tree

7 files changed

+131
-9
lines changed

7 files changed

+131
-9
lines changed

builtin/stash.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "cache-tree.h"
88
#include "unpack-trees.h"
99
#include "merge-recursive.h"
10+
#include "merge-ort-wrappers.h"
1011
#include "strvec.h"
1112
#include "run-command.h"
1213
#include "dir.h"
@@ -492,13 +493,13 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
492493
static int do_apply_stash(const char *prefix, struct stash_info *info,
493494
int index, int quiet)
494495
{
495-
int ret;
496+
int clean, ret;
496497
int has_index = index;
497498
struct merge_options o;
498499
struct object_id c_tree;
499500
struct object_id index_tree;
500-
struct commit *result;
501-
const struct object_id *bases[1];
501+
struct tree *head, *merge, *merge_base;
502+
struct lock_file lock = LOCK_INIT;
502503

503504
read_cache_preload(NULL);
504505
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
@@ -541,6 +542,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
541542

542543
o.branch1 = "Updated upstream";
543544
o.branch2 = "Stashed changes";
545+
o.ancestor = "Stash base";
544546

545547
if (oideq(&info->b_tree, &c_tree))
546548
o.branch1 = "Version stash was based on";
@@ -551,10 +553,26 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
551553
if (o.verbosity >= 3)
552554
printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
553555

554-
bases[0] = &info->b_tree;
556+
head = lookup_tree(o.repo, &c_tree);
557+
merge = lookup_tree(o.repo, &info->w_tree);
558+
merge_base = lookup_tree(o.repo, &info->b_tree);
559+
560+
repo_hold_locked_index(o.repo, &lock, LOCK_DIE_ON_ERROR);
561+
clean = merge_ort_nonrecursive(&o, head, merge, merge_base);
562+
563+
/*
564+
* If 'clean' >= 0, reverse the value for 'ret' so 'ret' is 0 when the
565+
* merge was clean, and nonzero if the merge was unclean or encountered
566+
* an error.
567+
*/
568+
ret = clean >= 0 ? !clean : clean;
569+
570+
if (ret < 0)
571+
rollback_lock_file(&lock);
572+
else if (write_locked_index(o.repo->index, &lock,
573+
COMMIT_LOCK | SKIP_IF_UNCHANGED))
574+
ret = error(_("could not write index"));
555575

556-
ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
557-
&result);
558576
if (ret) {
559577
rerere(0);
560578

@@ -1770,6 +1788,9 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
17701788
argc = parse_options(argc, argv, prefix, options, git_stash_usage,
17711789
PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
17721790

1791+
prepare_repo_settings(the_repository);
1792+
the_repository->settings.command_requires_full_index = 0;
1793+
17731794
index_file = get_index_file();
17741795
strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
17751796
(uintmax_t)pid);

read-cache.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2260,6 +2260,20 @@ static unsigned long load_cache_entries_threaded(struct index_state *istate, con
22602260
return consumed;
22612261
}
22622262

2263+
static void set_new_index_sparsity(struct index_state *istate)
2264+
{
2265+
/*
2266+
* If the index's repo exists, mark it sparse according to
2267+
* repo settings.
2268+
*/
2269+
if (istate->repo) {
2270+
prepare_repo_settings(istate->repo);
2271+
if (!istate->repo->settings.command_requires_full_index &&
2272+
is_sparse_index_allowed(istate, 0))
2273+
istate->sparse_index = 1;
2274+
}
2275+
}
2276+
22632277
/* remember to discard_cache() before reading a different cache! */
22642278
int do_read_index(struct index_state *istate, const char *path, int must_exist)
22652279
{
@@ -2281,8 +2295,10 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
22812295
istate->timestamp.nsec = 0;
22822296
fd = open(path, O_RDONLY);
22832297
if (fd < 0) {
2284-
if (!must_exist && errno == ENOENT)
2298+
if (!must_exist && errno == ENOENT) {
2299+
set_new_index_sparsity(istate);
22852300
return 0;
2301+
}
22862302
die_errno(_("%s: index file open failed"), path);
22872303
}
22882304

sparse-index.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ static int index_has_unmerged_entries(struct index_state *istate)
118118
return 0;
119119
}
120120

121-
static int is_sparse_index_allowed(struct index_state *istate, int flags)
121+
int is_sparse_index_allowed(struct index_state *istate, int flags)
122122
{
123123
if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
124124
return 0;

sparse-index.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
struct index_state;
55
#define SPARSE_INDEX_MEMORY_ONLY (1 << 0)
6+
int is_sparse_index_allowed(struct index_state *istate, int flags);
67
int convert_to_sparse(struct index_state *istate, int flags);
78
void ensure_correct_sparsity(struct index_state *istate);
89
void clear_skip_worktree_from_present_files(struct index_state *istate);

t/perf/p2000-sparse-operations.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ test_perf_on_all () {
106106
}
107107

108108
test_perf_on_all git status
109+
test_perf_on_all 'git stash && git stash pop'
110+
test_perf_on_all 'echo >>new && git stash -u && git stash pop'
109111
test_perf_on_all git add -A
110112
test_perf_on_all git add .
111113
test_perf_on_all git commit -a -m A

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,55 @@ test_expect_success 'cherry-pick with conflicts' '
10341034
test_all_match test_must_fail git cherry-pick to-cherry-pick
10351035
'
10361036

1037+
test_expect_success 'stash' '
1038+
init_repos &&
1039+
1040+
write_script edit-contents <<-\EOF &&
1041+
echo text >>$1
1042+
EOF
1043+
1044+
# Stash a sparse directory (folder1)
1045+
test_all_match git checkout -b test-branch rename-base &&
1046+
test_all_match git reset --soft rename-out-to-out &&
1047+
test_all_match git stash &&
1048+
test_all_match git status --porcelain=v2 &&
1049+
1050+
# Apply the sparse directory stash without reinstating the index
1051+
test_all_match git stash apply -q &&
1052+
test_all_match git status --porcelain=v2 &&
1053+
1054+
# Reset to state where stash can be applied
1055+
test_sparse_match git sparse-checkout reapply &&
1056+
test_all_match git reset --hard rename-out-to-out &&
1057+
1058+
# Apply the sparse directory stash *with* reinstating the index
1059+
test_all_match git stash apply --index -q &&
1060+
test_all_match git status --porcelain=v2 &&
1061+
1062+
# Reset to state where we will get a conflict applying the stash
1063+
test_sparse_match git sparse-checkout reapply &&
1064+
test_all_match git reset --hard update-folder1 &&
1065+
1066+
# Apply the sparse directory stash with conflicts
1067+
test_all_match test_must_fail git stash apply --index -q &&
1068+
test_all_match test_must_fail git stash apply -q &&
1069+
test_all_match git status --porcelain=v2 &&
1070+
1071+
# Reset to base branch
1072+
test_sparse_match git sparse-checkout reapply &&
1073+
test_all_match git reset --hard base &&
1074+
1075+
# Stash & unstash an untracked file outside of the sparse checkout
1076+
# definition.
1077+
run_on_sparse mkdir -p folder1 &&
1078+
run_on_all ../edit-contents folder1/new &&
1079+
test_all_match git stash -u &&
1080+
test_all_match git status --porcelain=v2 &&
1081+
1082+
test_all_match git stash pop -q &&
1083+
test_all_match git status --porcelain=v2
1084+
'
1085+
10371086
test_expect_success 'checkout-index inside sparse definition' '
10381087
init_repos &&
10391088
@@ -1222,7 +1271,10 @@ test_expect_success 'index.sparse disabled inline uses full index' '
12221271

12231272
ensure_not_expanded () {
12241273
rm -f trace2.txt &&
1225-
echo >>sparse-index/untracked.txt &&
1274+
if test -z "$WITHOUT_UNTRACKED_TXT"
1275+
then
1276+
echo >>sparse-index/untracked.txt
1277+
fi &&
12261278

12271279
if test "$1" = "!"
12281280
then
@@ -1326,6 +1378,30 @@ test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
13261378
)
13271379
'
13281380

1381+
test_expect_success 'sparse-index is not expanded: stash' '
1382+
init_repos &&
1383+
1384+
echo >>sparse-index/a &&
1385+
ensure_not_expanded stash &&
1386+
ensure_not_expanded stash list &&
1387+
ensure_not_expanded stash show stash@{0} &&
1388+
ensure_not_expanded stash apply stash@{0} &&
1389+
ensure_not_expanded stash drop stash@{0} &&
1390+
1391+
echo >>sparse-index/deep/new &&
1392+
ensure_not_expanded stash -u &&
1393+
(
1394+
WITHOUT_UNTRACKED_TXT=1 &&
1395+
ensure_not_expanded stash pop
1396+
) &&
1397+
1398+
ensure_not_expanded stash create &&
1399+
oid=$(git -C sparse-index stash create) &&
1400+
ensure_not_expanded stash store -m "test" $oid &&
1401+
ensure_not_expanded reset --hard &&
1402+
ensure_not_expanded stash pop
1403+
'
1404+
13291405
test_expect_success 'sparse index is not expanded: diff' '
13301406
init_repos &&
13311407

unpack-trees.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "refs.h"
1212
#include "attr.h"
1313
#include "split-index.h"
14+
#include "sparse-index.h"
1415
#include "submodule.h"
1516
#include "submodule-config.h"
1617
#include "fsmonitor.h"
@@ -1839,6 +1840,11 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
18391840
o->result.fsmonitor_last_update =
18401841
xstrdup_or_null(o->src_index->fsmonitor_last_update);
18411842

1843+
if (!o->src_index->initialized &&
1844+
!repo->settings.command_requires_full_index &&
1845+
is_sparse_index_allowed(&o->result, 0))
1846+
o->result.sparse_index = 1;
1847+
18421848
/*
18431849
* Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
18441850
*/

0 commit comments

Comments
 (0)