https://lore.kernel.org/git/20240311213928.1872437-1-sam@gentoo.org/ From dd8ed5e247032dc94f9b92aaa8be987154f58c27 Mon Sep 17 00:00:00 2001 Message-ID: From: Sam James Date: Fri, 16 Feb 2024 22:07:54 +0000 Subject: [PATCH 1/2] diff: implement config.diff.renames=copies-harder This patch adds a config value for 'diff.renames' called 'copies-harder' which make it so '-C -C' is in effect always passed for 'git log -p', 'git diff', etc. This allows specifying that 'git log -p', 'git diff', etc should always act as if '-C --find-copies-harder' was passed. It has proven this especially useful for certain types of repository (like Gentoo's ebuild repositories) because files are often copies of a previous version: Suppose a directory 'sys-devel/gcc' contains recipes for building GCC, with one file for each supported upstream branch: gcc-13.x.build.recipe gcc-12.x.build.recipe gcc-11.x.build.recipe gcc-10.x.build.recipe gcc-13.x.build.recipe was started as a copy of gcc-12.x.build.recipe (which was started as a copy of gcc-11.x.build.recipe, etc.). Previous versions are kept around to support parallel installation of multiple versions. Being able to easily observe the diff relative to other recipes within the directory has been a quality of life improvement for such repo layouts. Signed-off-by: Sam James --- Documentation/config/status.adoc | 4 +++- diff.c | 11 +++++++++-- diff.h | 1 + diffcore-rename.c | 6 ++++-- merge-ort.c | 2 +- merge-recursive.c | 2 +- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Documentation/config/status.adoc b/Documentation/config/status.adoc index 8caf90f51c..e15add32a3 100644 --- a/Documentation/config/status.adoc +++ b/Documentation/config/status.adoc @@ -33,7 +33,9 @@ status.renames:: Whether and how Git detects renames in linkgit:git-status[1] and linkgit:git-commit[1] . If set to "false", rename detection is disabled. If set to "true", basic rename detection is enabled. - If set to "copies" or "copy", Git will detect copies, as well. + If set to "copies" or "copy", Git will detect copies, as well. If set + to "copies-harder", Git will spend extra cycles to find more copies even + in unmodified paths, see '--find-copies-harder' in linkgit:git-diff[1] (Gentoo patch). Defaults to the value of diff.renames. status.showStash:: diff --git a/diff.c b/diff.c index c89c15d98e..743a504f6f 100644 --- a/diff.c +++ b/diff.c @@ -212,6 +212,8 @@ int git_config_rename(const char *var, const char *value) { if (!value) return DIFF_DETECT_RENAME; + if (!strcasecmp(value, "copies-harder")) + return DIFF_DETECT_COPY_HARDER; if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy")) return DIFF_DETECT_COPY; return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0; @@ -4900,8 +4902,12 @@ void diff_setup_done(struct diff_options *options) else options->flags.diff_from_contents = 0; - if (options->flags.find_copies_harder) + /* Just fold this in as it makes the patch-to-git smaller */ + if (options->flags.find_copies_harder || + options->detect_rename == DIFF_DETECT_COPY_HARDER) { + options->flags.find_copies_harder = 1; options->detect_rename = DIFF_DETECT_COPY; + } if (!options->flags.relative_name) options->prefix = NULL; @@ -5340,7 +5346,8 @@ static int diff_opt_find_copies(const struct option *opt, if (*arg != 0) return error(_("invalid argument to %s"), opt->long_name); - if (options->detect_rename == DIFF_DETECT_COPY) + if (options->detect_rename == DIFF_DETECT_COPY || + options->detect_rename == DIFF_DETECT_COPY_HARDER) options->flags.find_copies_harder = 1; else options->detect_rename = DIFF_DETECT_COPY; diff --git a/diff.h b/diff.h index ff0348e4a9..08cb6f587c 100644 --- a/diff.h +++ b/diff.h @@ -561,6 +561,7 @@ int git_config_rename(const char *var, const char *value); #define DIFF_DETECT_RENAME 1 #define DIFF_DETECT_COPY 2 +#define DIFF_DETECT_COPY_HARDER 3 #define DIFF_PICKAXE_ALL 1 #define DIFF_PICKAXE_REGEX 2 diff --git a/diffcore-rename.c b/diffcore-rename.c index 91b77993c7..1219882f0d 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -302,7 +302,8 @@ static int find_identical_files(struct hashmap *srcs, } /* Give higher scores to sources that haven't been used already */ score = !source->rename_used; - if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY) + if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY && + options->detect_rename != DIFF_DETECT_COPY_HARDER) continue; score += basename_same(source, target); if (score > best_score) { @@ -1407,7 +1408,8 @@ void diffcore_rename_extended(struct diff_options *options, trace2_region_enter("diff", "setup", options->repo); info.setup = 0; assert(!dir_rename_count || strmap_empty(dir_rename_count)); - want_copies = (detect_rename == DIFF_DETECT_COPY); + want_copies = (detect_rename == DIFF_DETECT_COPY || + detect_rename == DIFF_DETECT_COPY_HARDER); if (dirs_removed && (break_idx || want_copies)) BUG("dirs_removed incompatible with break/copy detection"); if (break_idx && relevant_sources) diff --git a/merge-ort.c b/merge-ort.c index 46e78c3ffa..94699789bd 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -4921,7 +4921,7 @@ static void merge_start(struct merge_options *opt, struct merge_result *result) * sanity check them anyway. */ assert(opt->detect_renames >= -1 && - opt->detect_renames <= DIFF_DETECT_COPY); + opt->detect_renames <= DIFF_DETECT_COPY_HARDER); assert(opt->verbosity >= 0 && opt->verbosity <= 5); assert(opt->buffer_output <= 2); assert(opt->obuf.len == 0); diff --git a/merge-recursive.c b/merge-recursive.c index 884ccf99a5..b979d43b85 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -3771,7 +3771,7 @@ static int merge_start(struct merge_options *opt, struct tree *head) assert(opt->branch1 && opt->branch2); assert(opt->detect_renames >= -1 && - opt->detect_renames <= DIFF_DETECT_COPY); + opt->detect_renames <= DIFF_DETECT_COPY_HARDER); assert(opt->detect_directory_renames >= MERGE_DIRECTORY_RENAMES_NONE && opt->detect_directory_renames <= MERGE_DIRECTORY_RENAMES_TRUE); assert(opt->rename_limit >= -1); -- 2.48.1