From: "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: "Christian Couder" <christian.couder@gmail.com>,
"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
"Jeff King" <peff@peff.net>, "Junio C Hamano" <gitster@pobox.com>,
"Derrick Stolee" <derrickstolee@github.com>,
"Johannes Schindelin" <johannes.schindelin@gmx.de>,
"Victoria Dye" <vdye@github.com>,
"Elijah Newren" <newren@gmail.com>,
"Emily Shaffer" <emilyshaffer@google.com>,
"Matheus Tavares Bernardino" <matheus.bernardino@usp.br>,
"Shaoxuan Yuan" <shaoxuan.yuan02@gmail.com>,
"ZheNing Hu" <adlternative@gmail.com>,
"ZheNing Hu" <adlternative@gmail.com>
Subject: [PATCH] [RFC] diff: introduce scope option
Date: Mon, 31 Oct 2022 04:11:52 +0000 [thread overview]
Message-ID: <pull.1398.git.1667189512579.gitgitgadget@gmail.com> (raw)
From: ZheNing Hu <adlternative@gmail.com>
When we use sparse-checkout, we often want the set of files
that some commands operate on to be restricted to the
sparse-checkout specification.
So introduce the `--scope` option to git diff, which have two
value: "sparse" and "all". "sparse" mean that diff is performed
restrict to paths which matching sparse-checkout specification,
"all" mean that diff is performed regardless of whether the path
meets the sparse-checkout specification. `--no-scope` is the default
option for now.
Add `diff.scope={sparse, all}` config, which can also have the same
capabilities as `--scope`, and it will be covered by `--scope` option.
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
[RFC] diff: introduce scope option
In [1], we discovered that users working on different sparse-checkout
specification may download unnecessary blobs from each other's
specification in collaboration. In [2] Junio suggested that maybe we can
restrict some git command's filespec in sparse-checkout specification to
elegantly solve this problem above. In [3]: Newren and Derrick Stolee
prefer to name the option --scope={sparse, all}.
So this patch is attempt to do this thing on git diff:
v1:
1. add --restrict option to git diff, which restrict diff filespec in
sparse-checkout specification. [4] v2.
2. rename --restrict to --scope={sparse, all}, support --no-scope.
3. add config: diff.scope={sparse,all}.
Unresolved work:
1. how to properly pass this --scope={sparse, all} to other commands
like git log, git format-patch, etc.
2. how to set the default value of scope for different diff commands.
[1]:
https://lore.kernel.org/git/CAOLTT8SHo66kGbvWr=+LQ9UVd1NHgqGGEYK2qq6==QgRCgLZqQ@mail.gmail.com/
[2]: https://lore.kernel.org/git/xmqqzgeqw0sy.fsf@gitster.g/ [3]:
https://lore.kernel.org/git/07a25d48-e364-0d9b-6ffa-41a5984eb5db@github.com/
[4]:
https://lore.kernel.org/git/pull.1368.git.1664036052741.gitgitgadget@gmail.com/
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1398%2Fadlternative%2Fzh%2Fdiff-scope-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1398/adlternative/zh/diff-scope-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1398
Documentation/config/diff.txt | 12 +
Documentation/diff-options.txt | 18 +
builtin/diff.c | 4 +
diff-lib.c | 36 +-
diff-no-index.c | 4 +
diff.c | 39 +++
diff.h | 11 +
t/t4070-diff-sparse-checkout-scope.sh | 469 ++++++++++++++++++++++++++
tree-diff.c | 5 +
9 files changed, 597 insertions(+), 1 deletion(-)
create mode 100644 t/t4070-diff-sparse-checkout-scope.sh
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 35a7bf86d77..52707e1b2d6 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -201,6 +201,18 @@ diff.algorithm::
--
+
+diff.scope::
+ Choose diff scope. The variants are as follows:
++
+--
+`sparse`;;
+ Restrict diff paths to those matching sparse-checkout specification.
+`all`;;
+ Without restriction, diff is performed regardless of whether the path
+ meets the sparse-checkout specification.
+--
++
+
diff.wsErrorHighlight::
Highlight whitespace errors in the `context`, `old` or `new`
lines of the diff. Multiple values are separated by comma,
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 3674ac48e92..04bf83e8be1 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -195,6 +195,24 @@ For instance, if you configured the `diff.algorithm` variable to a
non-default value and want to use the default one, then you
have to use `--diff-algorithm=default` option.
+ifndef::git-format-patch[]
+ifndef::git-log[]
+
+--scope={sparse|all}::
+ Choose diff scope. The variants are as follows:
++
+--
+`--sparse`;;
+ Restrict diff paths to those matching sparse-checkout specification.
+`--all`;;
+ Without restriction, diff is performed regardless of whether the path
+ meets the sparse-checkout specification.
+--
++
+
+endif::git-log[]
+endif::git-format-patch[]
+
--stat[=<width>[,<name-width>[,<count>]]]::
Generate a diffstat. By default, as much space as necessary
will be used for the filename part, and the rest for the graph
diff --git a/builtin/diff.c b/builtin/diff.c
index 854d2c5a5c4..6b450f7184c 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -54,6 +54,10 @@ static void stuff_change(struct diff_options *opt,
oideq(old_oid, new_oid) && (old_mode == new_mode))
return;
+ if (opt->scope == DIFF_SCOPE_SPARSE &&
+ !diff_paths_in_sparse_checkout(old_path, new_path))
+ return;
+
if (opt->flags.reverse_diff) {
SWAP(old_mode, new_mode);
SWAP(old_oid, new_oid);
diff --git a/diff-lib.c b/diff-lib.c
index 2edea41a234..a3381f2e0ff 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -88,6 +88,22 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
return changed;
}
+int diff_path_in_sparse_checkout(const char *path) {
+ if (core_sparse_checkout_cone)
+ return path_in_cone_mode_sparse_checkout(path, the_repository->index);
+ else
+ return path_in_sparse_checkout(path, the_repository->index);
+}
+
+int diff_paths_in_sparse_checkout(const char *one, const char*two) {
+ if (one == two || !strcmp(one, two))
+ return diff_path_in_sparse_checkout(one);
+ else
+ return diff_path_in_sparse_checkout(one) &&
+ diff_path_in_sparse_checkout(two);
+}
+
+
int run_diff_files(struct rev_info *revs, unsigned int option)
{
int entries, i;
@@ -113,6 +129,9 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (diff_can_quit_early(&revs->diffopt))
break;
+ if (revs->diffopt.scope == DIFF_SCOPE_SPARSE &&
+ !diff_path_in_sparse_checkout(ce->name))
+ continue;
if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
continue;
@@ -202,7 +221,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
continue;
}
- if (ce_uptodate(ce) || ce_skip_worktree(ce))
+ if (ce_uptodate(ce) ||
+ (revs->diffopt.scope != DIFF_SCOPE_ALL && ce_skip_worktree(ce)))
continue;
/*
@@ -439,6 +459,20 @@ static void do_oneway_diff(struct unpack_trees_options *o,
return; /* nothing to diff.. */
}
+ if (revs->diffopt.scope == DIFF_SCOPE_SPARSE) {
+ if (idx && tree) {
+ if (!diff_paths_in_sparse_checkout(idx->name, tree->name))
+ return;
+ } else if (idx) {
+ if (!diff_path_in_sparse_checkout(idx->name))
+ return;
+ } else if (tree) {
+ if (!diff_path_in_sparse_checkout(tree->name))
+ return;
+ } else
+ return;
+ }
+
/* if the entry is not checked out, don't examine work tree */
cached = o->index_only ||
(idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx)));
diff --git a/diff-no-index.c b/diff-no-index.c
index 18edbdf4b59..ea94a104ea4 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -281,6 +281,10 @@ int diff_no_index(struct rev_info *revs,
fixup_paths(paths, &replacement);
+ if (revs->diffopt.scope == DIFF_SCOPE_SPARSE &&
+ !diff_paths_in_sparse_checkout(paths[0], paths[1]))
+ goto out;
+
revs->diffopt.skip_stat_unmatch = 1;
if (!revs->diffopt.output_format)
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
diff --git a/diff.c b/diff.c
index 285d6e2d575..9de4044ae05 100644
--- a/diff.c
+++ b/diff.c
@@ -48,6 +48,7 @@ static int diff_interhunk_context_default;
static const char *diff_word_regex_cfg;
static const char *external_diff_cmd_cfg;
static const char *diff_order_file_cfg;
+static const char *external_diff_scope_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
@@ -57,6 +58,7 @@ static int diff_dirstat_permille_default = 30;
static struct diff_options default_diff_options;
static long diff_algorithm;
static unsigned ws_error_highlight_default = WSEH_NEW;
+static enum diff_scope external_diff_scope;
static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
@@ -423,6 +425,16 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (!strcmp(var, "diff.scope")) {
+ git_config_string(&external_diff_scope_cfg, var, value);
+ if (!strcmp(value, "all"))
+ external_diff_scope = DIFF_SCOPE_ALL;
+ else if (!strcmp(value, "sparse"))
+ external_diff_scope = DIFF_SCOPE_SPARSE;
+ else
+ return -1;
+ }
+
if (git_color_config(var, value, cb) < 0)
return -1;
@@ -4663,6 +4675,7 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
options->color_moved = diff_color_moved_default;
options->color_moved_ws_handling = diff_color_moved_ws_default;
+ options->scope = external_diff_scope;
prep_parse_options(options);
}
@@ -4914,6 +4927,29 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
return 1;
}
+static int diff_opt_diff_scope(const struct option *option,
+ const char *optarg, int unset)
+{
+ struct diff_options *opt = option->value;
+
+ if (unset) {
+ opt->scope = DIFF_SCOPE_NONE;
+ } else if (optarg) {
+ if (!strcmp(optarg, "all")) {
+ if (core_apply_sparse_checkout) {
+ opt->scope = DIFF_SCOPE_ALL;
+ }
+ } else if (!strcmp(optarg, "sparse")) {
+ if (core_apply_sparse_checkout) {
+ opt->scope = DIFF_SCOPE_SPARSE;
+ }
+ } else
+ return error(_("invalid --scope value: %s"), optarg);
+ }
+
+ return 0;
+}
+
static int diff_opt_diff_filter(const struct option *option,
const char *optarg, int unset)
{
@@ -5683,6 +5719,9 @@ static void prep_parse_options(struct diff_options *options)
OPT_CALLBACK_F(0, "diff-filter", options, N_("[(A|C|D|M|R|T|U|X|B)...[*]]"),
N_("select files by diff type"),
PARSE_OPT_NONEG, diff_opt_diff_filter),
+ OPT_CALLBACK_F(0, "scope", options, N_("[sparse|all]"),
+ N_("choose diff scope"),
+ PARSE_OPT_OPTARG, diff_opt_diff_scope),
{ OPTION_CALLBACK, 0, "output", options, N_("<file>"),
N_("output to a specific file"),
PARSE_OPT_NONEG, NULL, 0, diff_opt_output },
diff --git a/diff.h b/diff.h
index 8ae18e5ab1e..90f7512034c 100644
--- a/diff.h
+++ b/diff.h
@@ -230,6 +230,12 @@ enum diff_submodule_format {
DIFF_SUBMODULE_INLINE_DIFF
};
+enum diff_scope {
+ DIFF_SCOPE_NONE = 0,
+ DIFF_SCOPE_ALL,
+ DIFF_SCOPE_SPARSE,
+};
+
/**
* the set of options the calling program wants to affect the operation of
* diffcore library with.
@@ -285,6 +291,9 @@ struct diff_options {
/* diff-filter bits */
unsigned int filter, filter_not;
+ /* diff sparse-checkout scope */
+ enum diff_scope scope;
+
int use_color;
/* Number of context lines to generate in patch output. */
@@ -696,4 +705,6 @@ void print_stat_summary(FILE *fp, int files,
int insertions, int deletions);
void setup_diff_pager(struct diff_options *);
+int diff_path_in_sparse_checkout(const char *path);
+int diff_paths_in_sparse_checkout(const char *one, const char *two);
#endif /* DIFF_H */
diff --git a/t/t4070-diff-sparse-checkout-scope.sh b/t/t4070-diff-sparse-checkout-scope.sh
new file mode 100644
index 00000000000..dca75a3308b
--- /dev/null
+++ b/t/t4070-diff-sparse-checkout-scope.sh
@@ -0,0 +1,469 @@
+#!/bin/sh
+
+test_description='diff sparse-checkout scope'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+ git init temp &&
+ (
+ cd temp &&
+ mkdir sub1 &&
+ mkdir sub2 &&
+ echo sub1/file1 >sub1/file1 &&
+ echo sub2/file2 >sub2/file2 &&
+ echo file1 >file1 &&
+ echo file2 >file2 &&
+ git add --all &&
+ git commit -m init &&
+ echo sub1/file1 >>sub1/file1 &&
+ echo sub1/file2 >>sub1/file2 &&
+ echo sub2/file1 >>sub2/file1 &&
+ echo sub2/file2 >>sub2/file2 &&
+ echo file1 >>file1 &&
+ echo file2 >>file2 &&
+ git add --all &&
+ git commit -m change1 &&
+ echo sub1/file1 >>sub1/file1 &&
+ echo sub1/file2 >>sub1/file2 &&
+ echo sub2/file1 >>sub2/file1 &&
+ echo sub2/file2 >>sub2/file2 &&
+ echo file1 >>file1 &&
+ echo file2 >>file2 &&
+ git add --all &&
+ git commit -m change2
+ )
+'
+
+reset_repo () {
+ rm -rf repo &&
+ git clone --no-checkout temp repo
+}
+
+reset_with_sparse_checkout() {
+ reset_repo &&
+ git -C repo sparse-checkout set $1 sub1 &&
+ git -C repo checkout
+}
+
+change_worktree_and_index() {
+ (
+ cd repo &&
+ mkdir sub2 sub3 &&
+ echo sub1/file3 >sub1/file3 &&
+ echo sub2/file3 >sub2/file3 &&
+ echo sub3/file3 >sub3/file3 &&
+ echo file3 >file3 &&
+ git add --all --sparse &&
+ echo sub1/file3 >>sub1/file3 &&
+ echo sub2/file3 >>sub2/file3 &&
+ echo sub3/file3 >>sub3/file3 &&
+ echo file3 >>file3
+ )
+}
+
+diff_scope() {
+ title=$1
+ need_change_worktree_and_index=$2
+ sparse_checkout_option=$3
+ scope_option=$4
+ expect=$5
+ shift 5
+ args=("$@")
+
+ test_expect_success "$title $sparse_checkout_option $scope_option" "
+ reset_with_sparse_checkout $sparse_checkout_option &&
+ if test \"$need_change_worktree_and_index\" = \"true\" ; then
+ change_worktree_and_index
+ fi &&
+ git -C repo diff $scope_option ${args[*]} >actual &&
+ if test -z \"$expect\" ; then
+ >expect
+ else
+ cat > expect <<-EOF
+$expect
+ EOF
+ fi &&
+ test_cmp expect actual
+ "
+}
+
+args=("--name-only" "HEAD" "HEAD~")
+diff_scope builtin_diff_tree false "--no-cone" "--scope=sparse" \
+"sub1/file1
+sub1/file2" "${args[@]}"
+
+diff_scope builtin_diff_tree false "--no-cone" "--scope=all" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+diff_scope builtin_diff_tree false "--no-cone" "--no-scope" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+diff_scope builtin_diff_tree false "--cone" "--scope=sparse" \
+"file1
+file2
+sub1/file1
+sub1/file2" "${args[@]}"
+
+diff_scope builtin_diff_tree false "--cone" "--scope=all" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+diff_scope builtin_diff_tree false "--cone" "--no-scope" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+args=("--name-only" "HEAD~")
+diff_scope builtin_diff_index true "--no-cone" "--scope=sparse" \
+"sub1/file1
+sub1/file2
+sub1/file3" "${args[@]}"
+
+diff_scope builtin_diff_index true "--no-cone" "--scope=all" \
+"file1
+file2
+file3
+sub1/file1
+sub1/file2
+sub1/file3
+sub2/file1
+sub2/file2
+sub2/file3
+sub3/file3" "${args[@]}"
+
+diff_scope builtin_diff_index true "--no-cone" "--no-scope" \
+"file1
+file2
+file3
+sub1/file1
+sub1/file2
+sub1/file3
+sub2/file1
+sub2/file2
+sub2/file3
+sub3/file3" "${args[@]}"
+
+diff_scope builtin_diff_index true "--cone" "--scope=sparse" \
+"file1
+file2
+file3
+sub1/file1
+sub1/file2
+sub1/file3" "${args[@]}"
+
+diff_scope builtin_diff_index true "--cone" "--scope=all" \
+"file1
+file2
+file3
+sub1/file1
+sub1/file2
+sub1/file3
+sub2/file1
+sub2/file2
+sub2/file3
+sub3/file3" "${args[@]}"
+
+diff_scope builtin_diff_index true "--cone" "--no-scope" \
+"file1
+file2
+file3
+sub1/file1
+sub1/file2
+sub1/file3
+sub2/file1
+sub2/file2
+sub2/file3
+sub3/file3" "${args[@]}"
+
+args=("--name-only" "file3" "sub1/" "sub2/")
+
+diff_scope builtin_diff_files true "--no-cone" "--scope=sparse" \
+"sub1/file3" "${args[@]}"
+
+diff_scope builtin_diff_files true "--no-cone" "--scope=all" \
+"file3
+sub1/file3
+sub2/file1
+sub2/file2
+sub2/file3" "${args[@]}"
+
+diff_scope builtin_diff_files true "--no-cone" "--no-scope" \
+"file3
+sub1/file3
+sub2/file3" "${args[@]}"
+
+diff_scope builtin_diff_files true "--cone" "--scope=sparse" \
+"file3
+sub1/file3" "${args[@]}"
+
+diff_scope builtin_diff_files true "--cone" "--scope=all" \
+"file3
+sub1/file3
+sub2/file1
+sub2/file2
+sub2/file3" "${args[@]}"
+
+diff_scope builtin_diff_files true "--cone" "--no-scope" \
+"file3
+sub1/file3
+sub2/file3" "${args[@]}"
+
+
+args=("--name-only" "HEAD~:sub2/file2" "sub1/file2")
+
+diff_scope builtin_diff_b_f true "--no-cone" "--scope=sparse" \
+"" "${args[@]}"
+
+diff_scope builtin_diff_b_f true "--no-cone" "--scope=all" \
+"sub1/file2" "${args[@]}"
+
+diff_scope builtin_diff_b_f true "--no-cone" "--no-scope" \
+"sub1/file2" "${args[@]}"
+
+args=("--name-only" "HEAD~:sub1/file1" "file3")
+
+diff_scope builtin_diff_b_f true "--cone" "--scope=sparse" \
+"file3" "${args[@]}"
+
+diff_scope builtin_diff_b_f true "--cone" "--scope=all" \
+"file3" "${args[@]}"
+
+diff_scope builtin_diff_b_f true "--cone" "--no-scope" \
+"file3" "${args[@]}"
+
+args=("--name-only" HEAD~:sub2/file2 HEAD:sub1/file2)
+
+diff_scope builtin_diff_blobs true "--no-cone" "--scope=sparse" \
+"" "${args[@]}"
+
+diff_scope builtin_diff_blobs true "--no-cone" "--scope=all" \
+"sub1/file2" "${args[@]}"
+
+diff_scope builtin_diff_blobs true "--no-cone" "--no-scope" \
+"sub1/file2" "${args[@]}"
+
+args=("--name-only" HEAD~:sub1/file1 HEAD:file2)
+
+diff_scope builtin_diff_blobs false "--cone" "--scope=sparse" \
+"file2" "${args[@]}"
+
+diff_scope builtin_diff_blobs false "--cone" "--scope=all" \
+"file2" "${args[@]}"
+
+diff_scope builtin_diff_blobs false "--cone" "--no-scope" \
+"file2" "${args[@]}"
+
+args=("--name-only" HEAD~2 HEAD~ HEAD)
+
+diff_scope builtin_diff_combined false "--no-cone" "--scope=sparse" \
+"sub1/file1
+sub1/file2" "${args[@]}"
+
+diff_scope builtin_diff_combined false "--no-cone" "--scope=all" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+diff_scope builtin_diff_combined false "--no-cone" "--no-scope" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+diff_scope builtin_diff_combined false "--cone" "--scope=sparse" \
+"file1
+file2
+sub1/file1
+sub1/file2" "${args[@]}"
+
+diff_scope builtin_diff_combined false "--cone" "--scope=all" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+diff_scope builtin_diff_combined false "--cone" "--no-scope" \
+"file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2" "${args[@]}"
+
+test_expect_success 'diff_no_index --no-cone, --scope=sparse' '
+ reset_with_sparse_checkout --no-cone &&
+ (
+ cd repo &&
+ mkdir sub3 &&
+ echo sub3/file3 >sub3/file3
+ ) &&
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=sparse sub1/file1 sub1/file2 >actual &&
+ cat > expect <<-EOF &&
+sub1/file2
+ EOF
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=sparse sub1/file1 sub3/file3 >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff_no_index --no-cone, --scope=all' '
+ reset_with_sparse_checkout --no-cone &&
+ (
+ cd repo &&
+ mkdir sub3 &&
+ echo sub3/file3 >sub3/file3
+ ) &&
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=all sub1/file1 sub1/file2 >actual &&
+ cat > expect <<-EOF &&
+sub1/file2
+ EOF
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=all sub1/file1 sub3/file3 >actual &&
+ cat > expect <<-EOF &&
+sub3/file3
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff_no_index --no-cone, --no-scope' '
+ reset_with_sparse_checkout --no-cone &&
+ (
+ cd repo &&
+ mkdir sub3 &&
+ echo sub3/file3 >sub3/file3
+ ) &&
+ test_expect_code 1 git -C repo diff --no-index --name-only --no-scope sub1/file1 sub1/file2 >actual &&
+ cat > expect <<-EOF &&
+sub1/file2
+ EOF
+ test_expect_code 1 git -C repo diff --no-index --name-only --no-scope sub1/file1 sub3/file3 >actual &&
+ cat > expect <<-EOF &&
+sub3/file3
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff_no_index --cone, --scope=sparse' '
+ reset_with_sparse_checkout --cone &&
+ (
+ cd repo &&
+ echo file3 >file3 &&
+ mkdir sub3 &&
+ echo sub3/file3 >sub3/file3
+ ) &&
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=sparse sub1/file1 file3 >actual &&
+ cat > expect <<-EOF &&
+file3
+ EOF
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=sparse sub1/file1 sub3/file3 >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff_no_index --cone, --scope=all' '
+ reset_with_sparse_checkout --cone &&
+ (
+ cd repo &&
+ echo file3 >file3 &&
+ mkdir sub3 &&
+ echo sub3/file3 >sub3/file3
+ ) &&
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=all sub1/file1 file3 >actual &&
+ cat > expect <<-EOF &&
+file3
+ EOF
+ test_expect_code 1 git -C repo diff --no-index --name-only --scope=all sub1/file1 sub3/file3 >actual &&
+ cat > expect <<-EOF &&
+sub3/file3
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff_no_index --cone, --no-scope' '
+ reset_with_sparse_checkout --cone &&
+ (
+ cd repo &&
+ echo file3 >file3 &&
+ mkdir sub3 &&
+ echo sub3/file3 >sub3/file3
+ ) &&
+ test_expect_code 1 git -C repo diff --no-index --name-only --no-scope sub1/file1 file3 >actual &&
+ cat > expect <<-EOF &&
+file3
+ EOF
+ test_expect_code 1 git -C repo diff --no-index --name-only --no-scope sub1/file1 sub3/file3 >actual &&
+ cat > expect <<-EOF &&
+sub3/file3
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff scope config sparse' '
+ reset_with_sparse_checkout --cone &&
+ git -C repo -c diff.scope=sparse diff --name-only HEAD~ >actual &&
+ cat > expect <<-EOF &&
+file1
+file2
+sub1/file1
+sub1/file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff scope config all' '
+ reset_with_sparse_checkout --cone &&
+ git -C repo -c diff.scope=all diff --name-only HEAD~ >actual &&
+ cat > expect <<-EOF &&
+file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff scope config override by option' '
+ reset_with_sparse_checkout --cone &&
+ git -C repo -c diff.scope=sparse diff --name-only --scope=all HEAD~ >actual &&
+ cat > expect <<-EOF &&
+file1
+file2
+sub1/file1
+sub1/file2
+sub2/file1
+sub2/file2
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/tree-diff.c b/tree-diff.c
index 69031d7cbae..67f99c8e4df 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -76,6 +76,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)
static int emit_diff_first_parent_only(struct diff_options *opt, struct combine_diff_path *p)
{
struct combine_diff_parent *p0 = &p->parent[0];
+
+ if (opt->scope == DIFF_SCOPE_SPARSE &&
+ !diff_path_in_sparse_checkout(p->path))
+ return 0;
+
if (p->mode && p0->mode) {
opt->change(opt, p0->mode, p->mode, &p0->oid, &p->oid,
1, 1, p->path, 0, 0);
base-commit: 63bba4fdd86d80ef061c449daa97a981a9be0792
--
gitgitgadget
next reply other threads:[~2022-10-31 4:12 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-31 4:11 ZheNing Hu via GitGitGadget [this message]
2022-11-01 1:34 ` [PATCH] [RFC] diff: introduce scope option Taylor Blau
2022-11-01 2:13 ` ZheNing Hu
2022-11-01 5:18 ` Elijah Newren
2022-11-06 2:11 ` ZheNing Hu
2022-11-06 6:58 ` Elijah Newren
2022-11-14 9:08 ` ZheNing Hu
2022-11-25 2:45 ` [PATCH v2] [RFC] diff: introduce --scope option ZheNing Hu via GitGitGadget
2022-11-29 12:00 ` [PATCH v3 0/2] [RFC] diff: introduce scope option ZheNing Hu via GitGitGadget
2022-11-29 12:00 ` [PATCH v3 1/2] [RFC] diff: introduce --scope option ZheNing Hu via GitGitGadget
2022-11-29 12:00 ` [PATCH v3 2/2] [RPC] grep: " ZheNing Hu via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.1398.git.1667189512579.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=adlternative@gmail.com \
--cc=avarab@gmail.com \
--cc=christian.couder@gmail.com \
--cc=derrickstolee@github.com \
--cc=emilyshaffer@google.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=johannes.schindelin@gmx.de \
--cc=matheus.bernardino@usp.br \
--cc=newren@gmail.com \
--cc=peff@peff.net \
--cc=shaoxuan.yuan02@gmail.com \
--cc=vdye@github.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).