Merge branch 'ds/mergies-with-sparse-index'

Various mergy operations have been prepared to work efficiently
with the sparse index.

* ds/mergies-with-sparse-index:
  sparse-index: integrate with cherry-pick and rebase
  sequencer: ensure full index if not ORT strategy
  t1092: add cherry-pick, rebase tests
  merge-ort: expand only for out-of-cone conflicts
  merge: make sparse-aware with ORT
  diff: ignore sparse paths in diffstat
This commit is contained in:
Junio C Hamano 2021-09-20 15:20:44 -07:00
commit a16dd13740
8 changed files with 129 additions and 10 deletions

View File

@ -1276,6 +1276,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
/*
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.

View File

@ -560,6 +560,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
if (!is_null_oid(&squash_onto))
opts.squash_onto = &squash_onto;
@ -1431,6 +1434,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */

View File

@ -136,6 +136,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_UNKNOWN);
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
/* implies allow_empty */
if (opts->keep_redundant_commits)
opts->allow_empty = 1;

8
diff.c
View File

@ -26,6 +26,7 @@
#include "parse-options.h"
#include "help.h"
#include "promisor-remote.h"
#include "dir.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
@ -3907,6 +3908,13 @@ static int reuse_worktree_file(struct index_state *istate,
if (!want_file && would_convert_to_git(istate, name))
return 0;
/*
* If this path does not match our sparse-checkout definition,
* then the file will not be in the working directory.
*/
if (!path_in_sparse_checkout(name, istate))
return 0;
len = strlen(name);
pos = index_name_pos(istate, name, len);
if (pos < 0)

View File

@ -4074,6 +4074,21 @@ static int record_conflicted_index_entries(struct merge_options *opt)
if (strmap_empty(&opt->priv->conflicted))
return 0;
/*
* We are in a conflicted state. These conflicts might be inside
* sparse-directory entries, so check if any entries are outside
* of the sparse-checkout cone preemptively.
*
* We set original_cache_nr below, but that might change if
* index_name_pos() calls ask for paths within sparse directories.
*/
strmap_for_each_entry(&opt->priv->conflicted, &iter, e) {
if (!path_in_sparse_checkout(e->key, index)) {
ensure_full_index(index);
break;
}
}
/* If any entries have skip_worktree set, we'll have to check 'em out */
state.force = 1;
state.quiet = 1;

View File

@ -3747,6 +3747,9 @@ int merge_recursive(struct merge_options *opt,
assert(opt->ancestor == NULL ||
!strcmp(opt->ancestor, "constructed merge base"));
prepare_repo_settings(opt->repo);
opt->repo->settings.command_requires_full_index = 1;
if (merge_start(opt, repo_get_commit_tree(opt->repo, h1)))
return -1;
clean = merge_recursive_internal(opt, h1, h2, merge_bases, result);

View File

@ -664,6 +664,7 @@ static int do_recursive_merge(struct repository *r,
merge_switch_to_result(&o, head_tree, &result, 1, show_output);
clean = result.clean;
} else {
ensure_full_index(r->index);
clean = merge_trees(&o, head_tree, next_tree, base_tree);
if (is_rebase_i(opts) && clean <= 0)
fputs(o.obuf.buf, stdout);
@ -2359,6 +2360,7 @@ static int read_and_refresh_cache(struct repository *r,
_(action_name(opts)));
}
refresh_index(r->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
if (index_fd >= 0) {
if (write_locked_index(r->index, &index_lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED)) {
@ -2366,6 +2368,13 @@ static int read_and_refresh_cache(struct repository *r,
_(action_name(opts)));
}
}
/*
* If we are resolving merges in any way other than "ort", then
* expand the sparse index.
*/
if (opts->strategy && strcmp(opts->strategy, "ort"))
ensure_full_index(r->index);
return 0;
}

View File

@ -47,7 +47,7 @@ test_expect_success 'setup' '
git checkout -b base &&
for dir in folder1 folder2 deep
do
git checkout -b update-$dir &&
git checkout -b update-$dir base &&
echo "updated $dir" >$dir/a &&
git commit -a -m "update $dir" || return 1
done &&
@ -481,14 +481,17 @@ test_expect_success 'checkout and reset (mixed) [sparse]' '
test_sparse_match git reset update-folder2
'
test_expect_success 'merge' '
test_expect_success 'merge, cherry-pick, and rebase' '
init_repos &&
test_all_match git checkout -b merge update-deep &&
test_all_match git merge -m "folder1" update-folder1 &&
test_all_match git rev-parse HEAD^{tree} &&
test_all_match git merge -m "folder2" update-folder2 &&
test_all_match git rev-parse HEAD^{tree}
for OPERATION in "merge -m merge" cherry-pick rebase
do
test_all_match git checkout -B temp update-deep &&
test_all_match git $OPERATION update-folder1 &&
test_all_match git rev-parse HEAD^{tree} &&
test_all_match git $OPERATION update-folder2 &&
test_all_match git rev-parse HEAD^{tree} || return 1
done
'
# NEEDSWORK: This test is documenting current behavior, but that
@ -524,6 +527,38 @@ test_expect_success 'merge with conflict outside cone' '
test_all_match git rev-parse HEAD^{tree}
'
test_expect_success 'cherry-pick/rebase with conflict outside cone' '
init_repos &&
for OPERATION in cherry-pick rebase
do
test_all_match git checkout -B tip &&
test_all_match git reset --hard merge-left &&
test_all_match git status --porcelain=v2 &&
test_all_match test_must_fail git $OPERATION merge-right &&
test_all_match git status --porcelain=v2 &&
# Resolve the conflict in different ways:
# 1. Revert to the base
test_all_match git checkout base -- deep/deeper2/a &&
test_all_match git status --porcelain=v2 &&
# 2. Add the file with conflict markers
test_all_match git add folder1/a &&
test_all_match git status --porcelain=v2 &&
# 3. Rename the file to another sparse filename and
# accept conflict markers as resolved content.
run_on_all mv folder2/a folder2/z &&
test_all_match git add folder2 &&
test_all_match git status --porcelain=v2 &&
test_all_match git $OPERATION --continue &&
test_all_match git status --porcelain=v2 &&
test_all_match git rev-parse HEAD^{tree} || return 1
done
'
test_expect_success 'merge with outside renames' '
init_repos &&
@ -617,8 +652,17 @@ test_expect_success 'sparse-index is expanded and converted back' '
ensure_not_expanded () {
rm -f trace2.txt &&
echo >>sparse-index/untracked.txt &&
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
git -C sparse-index "$@" &&
if test "$1" = "!"
then
shift &&
test_must_fail env \
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
git -C sparse-index "$@" || return 1
else
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
git -C sparse-index "$@" || return 1
fi &&
test_region ! index ensure_full_index trace2.txt
}
@ -647,7 +691,35 @@ test_expect_success 'sparse-index is not expanded' '
echo >>sparse-index/extra.txt &&
ensure_not_expanded add extra.txt &&
echo >>sparse-index/untracked.txt &&
ensure_not_expanded add .
ensure_not_expanded add . &&
ensure_not_expanded checkout -f update-deep &&
test_config -C sparse-index pull.twohead ort &&
(
sane_unset GIT_TEST_MERGE_ALGORITHM &&
for OPERATION in "merge -m merge" cherry-pick rebase
do
ensure_not_expanded merge -m merge update-folder1 &&
ensure_not_expanded merge -m merge update-folder2 || return 1
done
)
'
test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
init_repos &&
for side in right left
do
git -C sparse-index checkout -b expand-$side base &&
echo $side >sparse-index/deep/a &&
git -C sparse-index commit -a -m "$side" || return 1
done &&
(
sane_unset GIT_TEST_MERGE_ALGORITHM &&
git -C sparse-index config pull.twohead ort &&
ensure_not_expanded ! merge -m merged expand-right
)
'
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout