add additional config settings for merge
cover add additional config settings for merge
This enables the user to set a couple of additional options for merge.
1. merge.aggressive - this is to try to resolve a few more trivial merge cases. It is documented in read-tree and is not something you can pass into merge itself.
2. merge.renames - this is to save git from having to go through the entire 3 trees to see if there were any renames that happened.
For the work item repro that I have been using this drops the merge time from ~1 hour to ~5 minutes and the unmerged entries goes down from ~40,000 to 1.
Base Ref: master
Web-Diff: https://github.com/benpeart/git/commit/a3d157f5be
Checkout: git fetch https://github.com/benpeart/git merge-options-v1 && git checkout a3d157f5be
Ben Peart (2):
merge: Add merge.renames config setting
merge: Add merge.aggressive config setting
Documentation/merge-config.txt | 9 +++++++++ merge-recursive.c | 2 ++ 2 files changed, 11 insertions(+)
base-commit: 0b0cc9f86731f894cff8dd25299a9b38c254569e
Signature · 1 line
2.17.0.windows.1
PATCH 1/2 merge: Add merge.renames config setting
Add the ability to control rename detection for merge via a config setting.
--- Documentation/merge-config.txt | 5 +++++ merge-recursive.c | 1 + 2 files changed, 6 insertions(+)
Documentation/merge-config.txt +5 -0
@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit. +merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default. + merge.renormalize:: Tell Git that canonical representation of files in the repository has changed over time (e.g. earlier commits record
merge-recursive.c +1 -0
@@ -3256,6 +3256,7 @@ static void merge_recursive_config(struct merge_options *o)git_config_get_int ("merge.verbosity" , &o->verbosity);git_config_get_int ("diff.renamelimit" , &o->diff_rename_limit);git_config_get_int ("merge.renamelimit" , &o->merge_rename_limit); +git_config_get_bool ("merge.renames" , &o->detect_rename);git_config (git_xmerge_config,NULL ); }
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Documentation/merge-config.txt merge.renameLimit:: · line 43
Quoted 10 lines · Ben Peart
Documentation/merge-config.txt +4
-0@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit. +merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.
One can already control o->detect_rename via the -Xno-renames and -Xfind-renames options. I think the documentation should mention that "false" is the same as passing -Xno-renames, and "true" is the same as passing -Xfind-renames. However, find-renames does take similarity threshold as a parameter, so there's a question whether this option should provide some way to do the same. I'm not sure the answer to that; it may be that we'd want a separate config option for that, and we can wait to add it until someone actually wants it.
Quoted 8 lines
Documentation/merge-config.txt +0
-0merge.renormalize:: Tell Git that canonical representation of files in the repository has changed over time (e.g. earlier commits recordmerge-recursive.c +0
-0
merge-recursive.c static void merge_recursive_config(struct merge_options *o) · line 3261
Quoted 6 lines
merge-recursive.c +1
-0@@ -3256,6 +3256,7 @@ static void merge_recursive_config(struct merge_options *o)git_config_get_int ("merge.verbosity" , &o->verbosity);git_config_get_int ("diff.renamelimit" , &o->diff_rename_limit);git_config_get_int ("merge.renamelimit" , &o->merge_rename_limit); +git_config_get_bool ("merge.renames" , &o->detect_rename);git_config (git_xmerge_config,NULL ); }
I would expect an explicitly passed -Xno-renames or -Xfind-renames to override the config setting. Could you check if that's the case?
Also, if someone sets merge.renameLimit (to anything) and sets merge.renames to false, then they've got a contradictory setup. Does it make sense to check and warn about that anywhere?
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 7 lines · Elijah Newren
Quoted 6 lines · Ben Peart
Documentation/merge-config.txt +0
-0@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit.
Quoted 13 lines
Documentation/merge-config.txt merge.renameLimit:: · line 43
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options. I think the documentation should mention that "false" is the same as passing -Xno-renames, and "true" is the same as passing -Xfind-renames. However, find-renames does take similarity threshold as a parameter, so there's a question whether this option should provide some way to do the same. I'm not sure the answer to that; it may be that we'd want a separate config option for that, and we can wait to add it until someone actually wants it.
I just realized another issue, though it also affects -Xno-renames. Even if rename detection is turned off for the merge, it is unconditionally turned on for the diffstat. In builtin/merge.c, function finish(), there is the code:
if (new_head && show_diffstat) {
...
opts.detect_rename = DIFF_DETECT_RENAME;It seems that this option should affect that line as well. (Do you have diffstat turned off by chance? If not, you may be able to improve your performance even more...)
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 27 lines · Elijah Newren
Quoted 7 lines · Elijah Newren
Quoted 6 lines · Ben Peart
Documentation/merge-config.txt +0
-0@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit.Quoted 13 lines
Documentation/merge-config.txt merge.renameLimit:: · line 43
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options. I think the documentation should mention that "false" is the same as passing -Xno-renames, and "true" is the same as passing -Xfind-renames. However, find-renames does take similarity threshold as a parameter, so there's a question whether this option should provide some way to do the same. I'm not sure the answer to that; it may be that we'd want a separate config option for that, and we can wait to add it until someone actually wants it.I just realized another issue, though it also affects -Xno-renames. Even if rename detection is turned off for the merge, it is unconditionally turned on for the diffstat. In
builtin/merge.c, functionfinish(), there is the code:
Quoted 8 lines
if (new_head && show_diffstat) { ... opts.detect_rename = DIFF_DETECT_RENAME;It seems that this option should affect that line as well. (Do you have diffstat turned off by chance? If not, you may be able to improve your performance even more...)
Seems reasonable to me. I'll update the patch to do that.
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
In reply to Elijah NewrenQuoted 7 lines · Elijah Newren
Quoted 6 lines · Ben Peart
Documentation/merge-config.txt +0
-0@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit.
Quoted 7 lines
Documentation/merge-config.txt merge.renameLimit:: · line 43
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options.
Yes, but that requires people to know they need to do that and then remember to pass it on the command line every time. We've found that doesn't typically happen, we just get someone complaining about slow merges. :)
That is why we added them as config options which change the default. That way we can then set them on the repo and the default behavior gives them better performance. They can still always override the config setting with the command line options.
I think the documentation should mention that
Quoted 6 lines
"false" is the same as passing -Xno-renames, and "true" is the same as passing -Xfind-renames. However, find-renames does take similarity threshold as a parameter, so there's a question whether this option should provide some way to do the same. I'm not sure the answer to that; it may be that we'd want a separate config option for that, and we can wait to add it until someone actually wants it.
I'm of the opinion that we shouldn't bother adding features that we aren't sure someone will want/use. If it comes up, we can certainly add it at a later date.
Quoted 10 lines
Quoted 10 lines
Documentation/merge-config.txt +0
-0merge.renormalize:: Tell Git that canonical representation of files in the repository has changed over time (e.g. earlier commits recordmerge-recursive.c +0
-0@@ -3256,6 +3256,7 @@ static void merge_recursive_config(struct merge_options *o)git_config_get_int ("merge.verbosity" , &o->verbosity);
Quoted 9 lines
merge-recursive.c static void merge_recursive_config(struct merge_options *o) · line 3261
Quoted 5 lines
merge-recursive.c +1
-0git_config_get_int ("diff.renamelimit" , &o->diff_rename_limit);git_config_get_int ("merge.renamelimit" , &o->merge_rename_limit); +git_config_get_bool ("merge.renames" , &o->detect_rename);git_config (git_xmerge_config,NULL ); }I would expect an explicitly passed -Xno-renames or -Xfind-renames to override the config setting. Could you check if that's the case?
Yes, command line options override the config settings. You can see that in the code where the call to init_merge_options() which loads the config settings is followed by parse_merge_opt() which loads the command line options. I've also verified the behavior in the debugger (it's on by default in the code, the config setting turns it off, then the command line option turns it back on).
Quoted 4 lines
Also, if someone sets merge.renameLimit (to anything) and sets merge.renames to false, then they've got a contradictory setup. Does it make sense to check and warn about that anywhere?
I don't think we need to. The merge.renameLimit is only used if detect_rename it turned on no matter how that gets turned on (default, config setting, command line option) so there isn't really a change in behavior here.
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Hi Ben,
Quoted 12 lines · Ben Peart
Quoted 10 lines · Elijah Newren
Quoted 7 lines · Ben Peart
Documentation/merge-config.txt +0
-0@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit.
Quoted 8 lines
Quoted 8 lines
Documentation/merge-config.txt merge.renameLimit:: · line 43
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options.
This statement wasn't meant to be independent of the sentence that followed it...
Quoted 8 lines
Yes, but that requires people to know they need to do that and then remember to pass it on the command line every time. We've found that doesn't typically happen, we just get someone complaining about slow merges. :)
That is why we added them as config options which change the default. That way we can then set them on the repo and the default behavior gives them better performance. They can still always override the config setting with the command line options.
Sorry, I think I wasn't being clear. The documentation for the config options for e.g. diff.renameLimit, fetch.prune, log.abbrevCommit, and merge.ff all mention the equivalent command line parameters. Your patch doesn't do that for merge.renames, but I think it would be helpful if it did.
Also, a link in the documentation the other way, from Documentation/merge-strategies.txt under the entries for -Xno-renames and -Xfind-renames should probably mention this new merge.renames config setting (much like the -Xno-renormalize flag mentions the merge.renomralize config option).
(In general, I think having this as a configuration option makes sense, though I hope my other performance patches would be enough to make people consider switching back to the defaults and use rename detection again.)
<snip>
Quoted 3 lines
I'm of the opinion that we shouldn't bother adding features that we aren't sure someone will want/use. If it comes up, we can certainly add it at a later date.
Works for me; I was mostly throwing it out there for thought.
Quoted 1 line
Yes, command line options override the config settings.
Good. :-)
Quoted 8 lines
Quoted 3 lines
Also, if someone sets merge.renameLimit (to anything) and sets merge.renames to false, then they've got a contradictory setup. Does it make sense to check and warn about that anywhere?
I don't think we need to. The merge.renameLimit is only used if
detect_renameit turned on no matter how that gets turned on (default, config setting, command line option) so there isn't really a change in behavior here.
I agree that's the pre-existing behavior, but prior to this patch turning off rename detection could only be done manually with every invocation. I'm slightly concerned that users might be confused if merge.renames was set to false somewhere -- perhaps even in a global /etc/gitconfig that they had no knowledge of or control over -- and in an attempt to get rename detection to work they started passing larger and larger values for renameLimit all to no avail.
The easy fix here may just be documenting the diff.renameLimit and merge.renameLimit options that they have no effect if rename detection is turned off.
Or maybe I'm just worrying too much, but we (folks at $dayjob) were bit pretty hard by renameLimit silently being capped at a value less than the user specified and in a way that wasn't documented anywhere.
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 6 lines · Elijah Newren
Quoted 6 lines
Quoted 6 lines
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options....
Sorry, I think I wasn't being clear. The documentation for the config options for e.g. diff.renameLimit, fetch.prune, log.abbrevCommit, and merge.ff all mention the equivalent command line parameters. Your patch doesn't do that for merge.renames, but I think it would be helpful if it did.
Yes, and if we are adding a new configuration, we should do so in such a way that we do not have to come back and extend it when we know what the command line option does and the configuration being proposed is less capable already. I wonder if we can just add a single configuration whose value can be "never" to pretend as if "--Xno-renames" were given, and some similarity score like "50" to pretend as if "--Xfind-renames=50" were given.
That is, merge.renames does not have to a simple "yes-no to control the --Xno-renames option". And it would of course be better to document it.
I also had to wonder how "merge -s resolve" faired, if the project is not interested in renamed paths at all.
Thanks.
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 17 lines · Junio C Hamano
Quoted 6 lines · Elijah Newren
Quoted 6 lines
Quoted 6 lines
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options....
Sorry, I think I wasn't being clear. The documentation for the config options for e.g. diff.renameLimit, fetch.prune, log.abbrevCommit, and merge.ff all mention the equivalent command line parameters. Your patch doesn't do that for merge.renames, but I think it would be helpful if it did.
Quoted 4 lines
Yes, and if we are adding a new configuration, we should do so in such a way that we do not have to come back and extend it when we know what the command line option does and the configuration being proposed is less capable already.
Between all the different command line options, config settings, merge strategies and the interactions between the diff and merge versions, I was trying to keep things as simple and consistent as possible. To that end 'merge.renames' was modeled after the existing 'diff.renames.'
I wonder if we can just add a
Quoted 8 lines
single configuration whose value can be "never" to pretend as if "--Xno-renames" were given, and some similarity score like "50" to pretend as if "--Xfind-renames=50" were given.
That is, merge.renames does not have to a simple "yes-no to control the --Xno-renames option". And it would of course be better to document it.
With the existing differences in how these options are passed on the command line, I'm hesitant to add yet another pattern in the config settings that combines 'renames' and '--find-renames[=<n>]'.
I _have_ wondered why this all isn't configured via find-renames with find-renames=0 meaning renames=false (instead of mapping 0 to 32K). I think that could have eliminated the need for splitting rename across two different settings (which is what I think you are proposing above). I'd then want the config setting and command line option to be the same syntax and behavior.
Moving the existing settings to this model and updating the config and command line options to be consistent without breaking backwards compatibility is outside the intended scope of this patch.
Quoted 3 lines
I also had to wonder how "merge -s resolve" faired, if the project is not interested in renamed paths at all.
To be clear, it isn't that we're not interested in detecting renamed files and paths. We're just opposed to it taking an hour to figure that out!
Quoted 2 lines
Thanks.
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 7 lines · Ben Peart
Quoted 3 lines
I also had to wonder how "merge -s resolve" faired, if the project is not interested in renamed paths at all.
To be clear, it isn't that we're not interested in detecting renamed files and paths. We're just opposed to it taking an hour to figure that out!
Yeah, but as opposed to passing "oh, let's see if we can get a reasonable result without rename detection just this time" from the command line, configuring merge.renames=false in would mean exactly
that: "we don't need rename detection, just want to skip the cycles spent for it". That is why I wondered how well the resolve strategy would have fit your needs.
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Hi Junio,
Quoted 10 lines · Junio C Hamano
Quoted 7 lines · Ben Peart
Quoted 3 lines
I also had to wonder how "merge -s resolve" faired, if the project is not interested in renamed paths at all.
To be clear, it isn't that we're not interested in detecting renamed files and paths. We're just opposed to it taking an hour to figure that out!
Quoted 6 lines
Yeah, but as opposed to passing "oh, let's see if we can get a reasonable result without rename detection just this time" from the command line, configuring merge.renames=false in would mean exactly
that: "we don't need rename detection, just want to skip the cycles spent for it". That is why I wondered how well the resolve strategy would have fit your needs.
Please do not forget that the context is GVFS, where you would cause a lot of pain and suffering by letting users forget to specify that command-line option all the time, resulting in several gigabytes of objects having to be downloaded just for the sake of rename detection.
So there is a pretty good point in doing this as a config option.
Ciao,
Dscho
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Hi Dscho,
Quoted 11 lines · Johannes Schindelin
Hi Junio,
Quoted 6 lines · Junio C Hamano
Yeah, but as opposed to passing "oh, let's see if we can get a reasonable result without rename detection just this time" from the command line, configuring merge.renames=false in would mean exactly
that: "we don't need rename detection, just want to skip the cycles spent for it". That is why I wondered how well the resolve strategy would have fit your needs.
Quoted 6 lines
Please do not forget that the context is GVFS, where you would cause a lot of pain and suffering by letting users forget to specify that command-line option all the time, resulting in several gigabytes of objects having to be downloaded just for the sake of rename detection.
So there is a pretty good point in doing this as a config option.
I agree you need a config option, but I think Junio has a good point that it's worth at least checking out the possibility of a different one. In particular, you could add a merge.defaultStrategy (or maybe merge.twohead to be similar to pull.twohead??) that is set to 'resolve', and use that to avoid rename detection.
Perhaps performance considerations rule out the resolve strategy and favor recursive, or maybe you need the 'recursive' part of the recursive strategy (rather than the rename part), or perhaps there's some other special reason you need to go this route, but since you are avoiding renames right now it's at least worth considering the resolve strategy.
Elijah
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Hi Elijah,
Quoted 25 lines · Elijah Newren
Quoted 10 lines · Johannes Schindelin
Quoted 6 lines · Junio C Hamano
Yeah, but as opposed to passing "oh, let's see if we can get a reasonable result without rename detection just this time" from the command line, configuring merge.renames=false in would mean exactly
that: "we don't need rename detection, just want to skip the cycles spent for it". That is why I wondered how well the resolve strategy would have fit your needs.Quoted 6 lines
Please do not forget that the context is GVFS, where you would cause a lot of pain and suffering by letting users forget to specify that command-line option all the time, resulting in several gigabytes of objects having to be downloaded just for the sake of rename detection.
So there is a pretty good point in doing this as a config option.
I agree you need a config option, but I think Junio has a good point that it's worth at least checking out the possibility of a different one. In particular, you could add a merge.defaultStrategy (or maybe merge.twohead to be similar to pull.twohead??) that is set to 'resolve', and use that to avoid rename detection.
Quoted 6 lines
Perhaps performance considerations rule out the resolve strategy and favor recursive, or maybe you need the 'recursive' part of the recursive strategy (rather than the rename part), or perhaps there's some other special reason you need to go this route, but since you are avoiding renames right now it's at least worth considering the resolve strategy.
I would really hesitate to go to a different merge strategy. The recursive strategy really has the best track record in general, and we have to use all kinds of branching models (including heavily criss-crossed ones) with GVFS Git.
Ciao,
Dscho
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
In reply to Elijah NewrenQuoted 5 lines · Elijah Newren
Sorry, I think I wasn't being clear. The documentation for the config options for e.g. diff.renameLimit, fetch.prune, log.abbrevCommit, and merge.ff all mention the equivalent command line parameters. Your patch doesn't do that for merge.renames, but I think it would be helpful if it did.
I wonder here what the relation to the diff.* options should be in this regard anyway. There is already diff.renames. Naively, I would assume that these options are in sync, that is you control the behavior of both the normal diff family like git show and git merge. The reasoning, at least for me, is to keep consistency between the outcome of rename detection while merging and a later simple "git show MERGE_BASE..HEAD". I would expect those to give me the same style of rename detection.
Hence, I would like to use diff.renames and maybe enhance this option to also carry the score in backward compatible way (or introduce a second configuration option?). Is this idea going in a good direction? If yes, I will try to submit a patch for this.
Ah, by the way: for people that have not touched diff.renames there will be no visible change in how Git behaves - the default for diff.renames is a rename with 50% score with is the same for merge. So it will only change if one has tweaked diff.renames already. But I wonder if one does that and expect the merge to use a different rename detection anyway.
Greetings,
Eckhard
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 15 lines · Eckhard Maaß
Quoted 5 lines · Elijah Newren
Sorry, I think I wasn't being clear. The documentation for the config options for e.g. diff.renameLimit, fetch.prune, log.abbrevCommit, and merge.ff all mention the equivalent command line parameters. Your patch doesn't do that for merge.renames, but I think it would be helpful if it did.
I wonder here what the relation to the diff.* options should be in this regard anyway. There is already diff.renames. Naively, I would assume that these options are in sync, that is you control the behavior of both the normal diff family like git show and git merge. The reasoning, at least for me, is to keep consistency between the outcome of rename detection while merging and a later simple "git show MERGE_BASE..HEAD". I would expect those to give me the same style of rename detection.
Quoted 4 lines
Hence, I would like to use diff.renames and maybe enhance this option to also carry the score in backward compatible way (or introduce a second configuration option?). Is this idea going in a good direction? If yes, I will try to submit a patch for this.
It's a fair question. If you look at all the options in Documentation/merge-config.txt, you will see many merge specific settings. I think the ability to control these settings separately is pretty well established.
In commit 2a2ac926547 when merge.renamelimit was added, it was decided to have separate settings for merge and diff to give users the ability to control that behavior. In this particular case, it will default to the value of diff.renamelimit when it isn't set. That isn't consistent with the other merge settings.
Changing that behavior across the rest of the merge settings is outside the scope of this patch. I don't have a strong opinion as to whether that is a good or bad thing.
Quoted 10 lines
Ah, by the way: for people that have not touched diff.renames there will be no visible change in how Git behaves - the default for diff.renames is a rename with 50% score with is the same for merge. So it will only change if one has tweaked diff.renames already. But I wonder if one does that and expect the merge to use a different rename detection anyway.
Greetings,
Eckhard
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 5 lines · Ben Peart
In commit
2a2ac926547when merge.renamelimit was added, it was decided to have separate settings for merge and diff to give users the ability to control that behavior. In this particular case, it will default to the value of diff.renamelimit when it isn't set. That isn't consistent with the other merge settings.
However, it seems like a desirable way to do it.
Maybe let me throw in some code for discussion (test and documentation is missing, mainly to form an idea what the change in options should be). I admit the patch below is concerned only with diff.renames, but whatever we come up with for merge should be reflected there, too, doesn't it?
Greetings,
Eckhard
-- >8 --
From e8a88111f2aaf338a4c19e83251c7178f7152129 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eckhard=20S=2E=20Maa=C3=9F?= <[email protected]>
Date: Sun, 22 Apr 2018 23:29:08 +0200
Subject: [PATCH] diff: enhance diff.renames to be able to set rename score
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
--- diff.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-)
diff.c +28 -7
@@ -30,6 +30,7 @@#endif static int diff_detect_rename_default; +static int diff_rename_score_default;static int diff_indent_heuristic =1 ;static int diff_rename_limit_default =400 ;static int diff_suppress_blank_empty;
@@ -177,13 +178,33 @@ static int parse_submodule_params(struct diff_options *options, const char *valureturn 0 ; } +int parse_rename_score (const char **cp_p); + +static int git_config_rename_score (const char *value) +{ +int parsed_rename_score =parse_rename_score (&value); +if (parsed_rename_score ==-1 ) +return error ("invalid argument to diff.renamescore: %s" , value); + diff_rename_score_default = parsed_rename_score; +return 0 ; +} +static int git_config_rename (const char *var,const char *value) { -if (!value) -return DIFF_DETECT_RENAME ; -if (!strcasecmp (value,"copies" ) || !strcasecmp (value,"copy" )) -return DIFF_DETECT_COPY ; -return git_config_bool (var,value) ?DIFF_DETECT_RENAME :0 ; +if (!value) { + diff_detect_rename_default =DIFF_DETECT_RENAME ; +return 0 ; + } +if (skip_to_optional_arg (value,"copies" , &value) ||skip_to_optional_arg (value,"copy" , &value)) { + diff_detect_rename_default =DIFF_DETECT_COPY ; +return git_config_rename_score (value); + } +if (skip_to_optional_arg (value,"renames" , &value) ||skip_to_optional_arg (value,"rename" , &value)) { + diff_detect_rename_default =DIFF_DETECT_RENAME ; +return git_config_rename_score (value); + } + diff_detect_rename_default =git_config_bool (var,value) ?DIFF_DETECT_RENAME :0 ; +return 0 ; }long parse_algorithm_value (const char *value)
@@ -307,8 +328,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)return 0 ; }if (!strcmp (var,"diff.renames" )) { - diff_detect_rename_default =git_config_rename (var, value); -return 0 ; +return git_config_rename (var, value); }if (!strcmp (var,"diff.autorefreshindex" )) { diff_auto_refresh_index =git_config_bool (var, value);
@@ -4116,6 +4136,7 @@ void diff_setup(struct diff_options *options) options->add_remove = diff_addremove; options->use_color = diff_use_color_default; options->detect_rename = diff_detect_rename_default; + options->rename_score = diff_rename_score_default; options->xdl_opts |= diff_algorithm;if (diff_indent_heuristic)DIFF_XDL_SET (options,INDENT_HEURISTIC );
Signature · 1 line
2.17.0.252.gfe0a9eaf31
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
Quoted 8 lines · Eckhard Maaß
Quoted 5 lines · Ben Peart
In commit
2a2ac926547when merge.renamelimit was added, it was decided to have separate settings for merge and diff to give users the ability to control that behavior. In this particular case, it will default to the value of diff.renamelimit when it isn't set. That isn't consistent with the other merge settings.However, it seems like a desirable way to do it.
I'm just one opinion among many but I personally believe the cascading settings are complicated enough just with the various config files and command line options and which overwrite the others. I'd rather not complicate them further by having settings inherited from one feature (diff) to another (merge).
There are currently ~15 merge specific config settings and only merge.renamelimit currently does this inheritance. That said, at least one other person thought it was a good idea. :)
Quoted 94 lines
Maybe let me throw in some code for discussion (test and documentation is missing, mainly to form an idea what the change in options should be). I admit the patch below is concerned only with diff.renames, but whatever we come up with for merge should be reflected there, too, doesn't it >
Greetings,
Eckhard-- >8 --
From
e8a88111f2aaf338a4c19e83251c7178f7152129Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eckhard=20S=2E=20Maa=C3=9F?= <[email protected]>
Date: Sun, 22 Apr 2018 23:29:08 +0200
Subject: [PATCH] diff: enhance diff.renames to be able to set rename score
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bitSigned-off-by Eckhard S. Maaß <[email protected]>--- diff.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-)diff.c +28
-7@@ -30,6 +30,7 @@#endif static int diff_detect_rename_default; +static int diff_rename_score_default;static int diff_indent_heuristic =1 ;static int diff_rename_limit_default =400 ;static int diff_suppress_blank_empty;@@ -177,13 +178,33 @@ static int parse_submodule_params(struct diff_options *options, const char *valureturn 0 ; } +int parse_rename_score (const char **cp_p); + +static int git_config_rename_score (const char *value) +{ +int parsed_rename_score =parse_rename_score (&value); +if (parsed_rename_score ==-1 ) +return error ("invalid argument to diff.renamescore: %s" , value); + diff_rename_score_default = parsed_rename_score; +return 0 ; +} +static int git_config_rename (const char *var,const char *value) { -if (!value) -return DIFF_DETECT_RENAME ; -if (!strcasecmp (value,"copies" ) || !strcasecmp (value,"copy" )) -return DIFF_DETECT_COPY ; -return git_config_bool (var,value) ?DIFF_DETECT_RENAME :0 ; +if (!value) { + diff_detect_rename_default =DIFF_DETECT_RENAME ; +return 0 ; + } +if (skip_to_optional_arg (value,"copies" , &value) ||skip_to_optional_arg (value,"copy" , &value)) { + diff_detect_rename_default =DIFF_DETECT_COPY ; +return git_config_rename_score (value); + } +if (skip_to_optional_arg (value,"renames" , &value) ||skip_to_optional_arg (value,"rename" , &value)) { + diff_detect_rename_default =DIFF_DETECT_RENAME ; +return git_config_rename_score (value); + } + diff_detect_rename_default =git_config_bool (var,value) ?DIFF_DETECT_RENAME :0 ; +return 0 ; }long parse_algorithm_value (const char *value)@@ -307,8 +328,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)return 0 ; }if (!strcmp (var,"diff.renames" )) { - diff_detect_rename_default =git_config_rename (var, value); -return 0 ; +return git_config_rename (var, value); }if (!strcmp (var,"diff.autorefreshindex" )) { diff_auto_refresh_index =git_config_bool (var, value);@@ -4116,6 +4136,7 @@ void diff_setup(struct diff_options *options) options->add_remove = diff_addremove; options->use_color = diff_use_color_default; options->detect_rename = diff_detect_rename_default; + options->rename_score = diff_rename_score_default; options->xdl_opts |= diff_algorithm;if (diff_indent_heuristic)DIFF_XDL_SET (options,INDENT_HEURISTIC );
Re: [PATCH v1 1/2] merge: Add merge.renames config setting
In reply to Elijah NewrenQuoted 42 lines · Elijah Newren
Hi Ben,
Quoted 12 lines · Ben Peart
Quoted 10 lines · Elijah Newren
Quoted 7 lines · Ben Peart
Documentation/merge-config.txt +0
-0@@ -37,6 +37,11 @@ merge.renameLimit:: during a merge; if not specified, defaults to the value of diff.renameLimit.Quoted 8 lines
Quoted 8 lines
Documentation/merge-config.txt merge.renameLimit:: · line 43
Quoted 4 lines
Documentation/merge-config.txt +4
-0+merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default.One can already control
o->detect_renamevia the -Xno-renames and -Xfind-renames options.This statement wasn't meant to be independent of the sentence that followed it...
Quoted 8 lines
Yes, but that requires people to know they need to do that and then remember to pass it on the command line every time. We've found that doesn't typically happen, we just get someone complaining about slow merges. :)
That is why we added them as config options which change the default. That way we can then set them on the repo and the default behavior gives them better performance. They can still always override the config setting with the command line options.
Sorry, I think I wasn't being clear. The documentation for the config options for e.g. diff.renameLimit, fetch.prune, log.abbrevCommit, and merge.ff all mention the equivalent command line parameters. Your patch doesn't do that for merge.renames, but I think it would be helpful if it did.
Quoted 6 lines
Also, a link in the documentation the other way, from
Documentation/merge-strategies.txtunder the entries for -Xno-renames and -Xfind-renames should probably mention this new merge.renames config setting (much like the -Xno-renormalize flag mentions the merge.renomralize config option).
I'm all in favor of having more information in the documentation. I'm of the opinion that if someone has made the effort to actually _read_ the documentation, we should be as descriptive and complete as possible.
I'll take a cut at adding the things you have pointed out would be helpful.
Quoted 11 lines
I agree that's the pre-existing behavior, but prior to this patch turning off rename detection could only be done manually with every invocation. I'm slightly concerned that users might be confused if merge.renames was set to false somewhere -- perhaps even in a global
/etc/gitconfigthat they had no knowledge of or control over -- and in an attempt to get rename detection to work they started passing larger and larger values for renameLimit all to no avail.The easy fix here may just be documenting the diff.renameLimit and merge.renameLimit options that they have no effect if rename detection is turned off.
I can add this additional documentation as well. While some might think it is stating the obvious, I'm sure someone will benefit from it being explicitly called out.
Quoted 5 lines
Or maybe I'm just worrying too much, but we (folks at $dayjob) were bit pretty hard by renameLimit silently being capped at a value less than the user specified and in a way that wasn't documented anywhere.
PATCH 2/2 merge: Add merge.aggressive config setting
In reply to Ben PeartAdd the ability to control the aggressive flag passed to read-tree via a config setting.
--- Documentation/merge-config.txt | 4 ++++ merge-recursive.c | 1 + 2 files changed, 5 insertions(+)
Documentation/merge-config.txt +4 -0
@@ -1,3 +1,7 @@ +merge.aggressive:: + Passes "aggressive" to read-tree which makes the command resolve + a few more cases internally. See "--aggressive" in linkgit:git-read-tree[1]. + merge.conflictStyle:: Specify the style in which conflicted hunks are written out to working tree files upon merge. The default is "merge", which
merge-recursive.c +1 -0
@@ -355,6 +355,7 @@ static int git_merge_trees(struct merge_options *o, o->unpack_opts.fn = threeway_merge; o->unpack_opts.src_index = &the_index; o->unpack_opts.dst_index = &the_index; +git_config_get_bool ("merge.aggressive" , (int *)&o->unpack_opts.aggressive);setup_unpack_trees_porcelain (&o->unpack_opts,"merge" );init_tree_desc_from_tree (t+0 , common);
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v1 2/2] merge: Add merge.aggressive config setting
Quoted 1 line · Ben Peart
Add the ability to control the aggressive flag passed to read-tree via a config setting.
This feels like a workaround to the performance problems with index updates in merge-recursive.c. That said, it makes sense to me to do this when rename detection is turned off. In fact, I think you'd automatically want to set aggressive to true whenever rename detection is turned off (whether by your merge.renames option or the -Xno-renames flag).
I can't think of any reason this setting would be useful separate from turning rename detection off, and it'd actively harm rename detection performance improvements I have in the pipeline. I'd really prefer to not add this option, and instead combine the setting of aggressive with the other flag. Do you have an independent reason for wanting this?
Thanks,
Elijah
Re: [PATCH v1 2/2] merge: Add merge.aggressive config setting
Quoted 5 lines · Elijah Newren
Quoted 1 line · Ben Peart
Add the ability to control the aggressive flag passed to read-tree via a config setting.
This feels like a workaround to the performance problems with index updates in
merge-recursive.c.
This change wasn't done to solve performance problems. We turned it on because it reduced the number of unmerged entries (from 40K to 1) in the particular merge we were looking at. The additional 3 scenarios that --aggressive resolves made that much difference.
That said, it makes sense to me to do
Quoted 11 lines
this when rename detection is turned off. In fact, I think you'd automatically want to set aggressive to true whenever rename detection is turned off (whether by your merge.renames option or the -Xno-renames flag).
Quoted 1 line
I can't think of any reason this setting would be useful separate from
turning rename detection off, and it'd actively harm rename detection performance improvements I have in the pipeline. I'd really prefer to not add this option, and instead combine the setting of aggressive with the other flag. Do you have an independent reason for wanting this?
While combining them would work for our specific use scenario (since we turn both on already along with turning off merge.stat), I really hesitate to tie these two different flags and code paths together with a single config setting.
While I don't want to needlessly complicate your optimizations in this area (they are already complex enough!) I believe we need to keep the option to turn on --aggressive without turning off rename detection as a viable option. Perhaps if that is the case, your optimizations have less impact or don't apply but the user should be able to make that choice for their specific situation.
Quoted 3 lines
Thanks,
Elijah
Re: [PATCH v1 2/2] merge: Add merge.aggressive config setting
Hi Ben,
Quoted 10 lines · Ben Peart
Quoted 8 lines · Elijah Newren
Quoted 3 lines · Ben Peart
Add the ability to control the aggressive flag passed to read-tree via a config setting.
This feels like a workaround to the performance problems with index updates in
merge-recursive.c.
Quoted 6 lines
This change wasn't done to solve performance problems. We turned it on because it reduced the number of unmerged entries (from 40K to 1) in the particular merge we were looking at. The additional 3 scenarios that --aggressive resolves made that much difference.
That said, it makes sense to me to do
Um...color me perplexed here. aggressive exists just to do some resolutions that higher-level strategies can and totally ought to be able to handle easily (the rules are almost trivially straight-forward), but deferring allows the higher level strategies (either merge-recursive or resolve's git-merge-one-file) to handle slightly differently (e.g. by detecting renames). merge-recursive should be able to resolve anything that the unpack_trees aggressive setting handles. If it can't, it sounds like there's a horrible bug somewhere.
Perhaps fixing that bug is the real problem?
Is there any chance you can dig out more details about any of these conflicts or come up with a simple testcase where running 'git merge -X no-renames' gives a merge conflict but running with this option would run to completion?
Quoted 17 lines
Quoted 11 lines
this when rename detection is turned off. In fact, I think you'd automatically want to set aggressive to true whenever rename detection is turned off (whether by your merge.renames option or the -Xno-renames flag).
Quoted 1 line
I can't think of any reason this setting would be useful separate from
turning rename detection off, and it'd actively harm rename detection performance improvements I have in the pipeline. I'd really prefer to not add this option, and instead combine the setting of aggressive with the other flag. Do you have an independent reason for wanting this?
While combining them would work for our specific use scenario (since we turn both on already along with turning off merge.stat), I really hesitate to tie these two different flags and code paths together with a single config setting.
Quoted 6 lines
While I don't want to needlessly complicate your optimizations in this area (they are already complex enough!) I believe we need to keep the option to turn on --aggressive without turning off rename detection as a viable option. Perhaps if that is the case, your optimizations have less impact or don't apply but the user should be able to make that choice for their specific situation.
I totally buy that you need at least one option to avoid waiting for (current) rename detection in some fashion, and that you don't want lots of spurious conflicts. But I don't understand why you believe that we need to keep the option to turn on the aggressive flag independently. What's the usecase? It wasn't possible before in the code, no one else has asked for it, and even you say you don't need it as a separate option. Is it a concern that turning on aggressive whenever rename-detection is turned off will break something? The only reason I can see to keep the aggressive codepath in unpack_trees behind a branch instead of it always running unconditionally for every single caller throughout the codebase is because of renames. So the fact that you're turning renames off, to me, suggests that aggressive flag should automatically be turned on. I'd even call pre-existing code (e.g. the -X no-renames option in merge-recursive) that doesn't turn on the aggressive flag buggy (even if the only result is suboptimal-performance).
I don't see how an option to turn on the aggressive flag independently is possibly useful to anyone. Further, we have strong reason to believe it will soon be actively harmful. So...why? It's totally possible I'm just missing something. If there's a good reason for it, providing some kind of benefit that the user could weigh in a tradeoff, then I can get on board with providing it as an option, but right now I just don't see it.
Re: [PATCH v1 2/2] merge: Add merge.aggressive config setting
In reply to Ben PeartQuoted 11 lines · Ben Peart
That said, it makes sense to me to do
Quoted 5 lines
this when rename detection is turned off. In fact, I think you'd automatically want to set aggressive to true whenever rename detection is turned off (whether by your merge.renames option or the -Xno-renames flag).
...While combining them would work for our specific use scenario (since we turn both on already along with turning off merge.stat), I really hesitate to tie these two different flags and code paths together with a single config setting.
The cases that non-agressive variant leaves unmerged are not auto-resolved only because marking them as merged will rob the chance from the rename detection logic to notice which ones are "new" paths that could be matched with "deleted" ones to turn into renames. If rename deteciton is not done, there is no reason to leave it non aggressive, as "#1 = missing, #2 = something and #3 = missing" entry (just one example that is not auto-resolved by non-agressive, but the principle is the same) left unmerged in the index will get resolved to keep the current entry by the post processing logic anyway.
In fact, checking git-merge-resolve would tell us that we already use "aggresive" variant there unconditionally.
So, I think Elijah is correct---there is no reason not to enable this setting when the other one to refuse rename detection is in effect.
Re: [PATCH v1 2/2] merge: Add merge.aggressive config setting
Quoted 25 lines · Junio C Hamano
Quoted 11 lines · Ben Peart
That said, it makes sense to me to do
Quoted 5 lines
this when rename detection is turned off. In fact, I think you'd automatically want to set aggressive to true whenever rename detection is turned off (whether by your merge.renames option or the -Xno-renames flag).
...While combining them would work for our specific use scenario (since we turn both on already along with turning off merge.stat), I really hesitate to tie these two different flags and code paths together with a single config setting.
The cases that non-agressive variant leaves unmerged are not auto-resolved only because marking them as merged will rob the chance from the rename detection logic to notice which ones are "new" paths that could be matched with "deleted" ones to turn into renames. If rename deteciton is not done, there is no reason to leave it non aggressive, as "#1 = missing, #2 = something and #3 = missing" entry (just one example that is not auto-resolved by non-agressive, but the principle is the same) left unmerged in the index will get resolved to keep the current entry by the post processing logic anyway.
Quoted 7 lines
In fact, checking git-merge-resolve would tell us that we already use "aggresive" variant there unconditionally.
So, I think Elijah is correct---there is no reason not to enable this setting when the other one to refuse rename detection is in effect.
Thank you. I understand this description and it make sense to me. I'm unfamiliar with the merge code so have to rely on you who are the experts for a sanity check.
I will remove the separate merge.aggressive config setting and instead, set the aggressive flag when the renames is turned off (whether by the new merge.renames setting or by the -Xno-renames flag) as Elijah suggests.
Quoted 6 lines · Ben Peart
This enables the user to set a couple of additional options for merge.
1. merge.aggressive - this is to try to resolve a few more trivial merge cases. It is documented in read-tree and is not something you can pass into merge itself.
Quoted 6 lines
2. merge.renames - this is to save git from having to go through the entire 3 trees to see if there were any renames that happened.
For the work item repro that I have been using this drops the merge time from ~1 hour to ~5 minutes and the unmerged entries goes down from ~40,000 to 1.
Ooh, this is *very* interesting. Is there any chance I could also get you to test performing the same merge with the version of git at
https://github.com/newren/git/tree/big-repo-small-cherry-pick and report on your timings?
The 'big-repo-small-cherry-pick' name could be improved, but that branch has a number of performance fixes for really poor rename detection performance during merges. From your description, I'm pretty sure it'll apply to your case. For my specific testcase, I got a speedup factor of 30. Someone else on the list saw a factor of 24[1]. Results are highly dependent on the specific repo, but it's certainly possible that it gets much of your factor of 12 speedup that you saw with these new config settings you added.
However, what makes this case even more interesting to me is that my branch may not be quite as effective as your workarounds. There are other other performance issues in merge that I am aware of, but for which I haven't had the time to write the patches yet (I've been waiting for the directory rename detection stuff to land and settle down before working more on the performance aspects). I do not know how big a factor those other performance issues are, but your workarounds (namely the aggressive setting) may get around some of those other issues as well, so I'm very interested to see how my current branch compares to the speedups you got with these settings.
Thanks,
Elijah
Quoted 10 lines · Elijah Newren
Quoted 9 lines · Ben Peart
This enables the user to set a couple of additional options for merge.
1. merge.aggressive - this is to try to resolve a few more trivial merge cases. It is documented in read-tree and is not something you can pass into merge itself.
2. merge.renames - this is to save git from having to go through the entire 3 trees to see if there were any renames that happened.
Quoted 9 lines
Quoted 3 lines
For the work item repro that I have been using this drops the merge time from ~1 hour to ~5 minutes and the unmerged entries goes down from ~40,000 to 1.
Ooh, this is *very* interesting. Is there any chance I could also get you to test performing the same merge with the version of git at
https://github.com/newren/git/tree/big-repo-small-cherry-pick and report on your timings?
Unfortunately, it isn't quite that simple. My repo is _really_ big (3.2M files and ~100K commits per week) and requires me to use a custom fork of git that works with our GVFS solution for it to work at all.
I've been watching your work in this area and am hoping it pays off for us if/when we have users that want to do rename detection and override our defaults.
Quoted 26 lines
The 'big-repo-small-cherry-pick' name could be improved, but that branch has a number of performance fixes for really poor rename detection performance during merges. From your description, I'm pretty sure it'll apply to your case. For my specific testcase, I got a speedup factor of 30. Someone else on the list saw a factor of 24[1]. Results are highly dependent on the specific repo, but it's certainly possible that it gets much of your factor of 12 speedup that you saw with these new config settings you added.
However, what makes this case even more interesting to me is that my branch may not be quite as effective as your workarounds. There are other other performance issues in merge that I am aware of, but for which I haven't had the time to write the patches yet (I've been waiting for the directory rename detection stuff to land and settle down before working more on the performance aspects). I do not know how big a factor those other performance issues are, but your workarounds (namely the aggressive setting) may get around some of those other issues as well, so I'm very interested to see how my current branch compares to the speedups you got with these settings.
Thanks,
Elijah
v2 cover add additional config settings for merge
In reply to Ben PeartUpdated in response to feedback. Mostly documentation changes but the diffstat at the end of the merge (if on) now honors the new merge.rename setting as well.
Base Ref: master
Web-Diff: https://github.com/benpeart/git/commit/653bfe6e01
Checkout: git fetch https://github.com/benpeart/git merge-options-v2 && git checkout 653bfe6e01
### Interdiff (v1..v2):
Documentation/diff-config.txt +2 -1
@@ -112,7 +112,8 @@ diff.orderFile:: diff.renameLimit:: The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off. diff.renames:: Whether and how Git detects renames. If set to "false",
Documentation/merge-config.txt +2 -1
@@ -39,7 +39,8 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off. merge.renames:: Whether and how Git detects renames. If set to "false",
Documentation/merge-strategies.txt +4 -2
@@ -84,12 +84,14 @@ no-renormalize;; `merge.renormalize` configuration variable. no-renames;; - Turn off rename detection. + Turn off rename detection. This overrides the `merge.renames` + configuration variable. See also linkgit:git-diff[1] `--no-renames`. find-renames[=<n>];; Turn on rename detection, optionally setting the similarity - threshold. This is the default. + threshold. This is the default. This overrides the + 'merge.renames' configuration variable. See also linkgit:git-diff[1] `--find-renames`. rename-threshold=<n>;;
builtin/merge.c +1 -0
@@ -424,6 +424,7 @@ static void finish(struct commit *head_commit, opts.output_format |=DIFF_FORMAT_SUMMARY |DIFF_FORMAT_DIFFSTAT ; opts.detect_rename =DIFF_DETECT_RENAME ; +git_config_get_bool ("merge.renames" , &opts.detect_rename);diff_setup_done (&opts);diff_tree_oid (head, new_head,"" , &opts);diffcore_std (&opts);
### Patches
Ben Peart (2):
merge: Add merge.renames config setting
merge: Add merge.aggressive config setting
Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 12 +++++++++++- Documentation/merge-strategies.txt | 6 ++++-- builtin/merge.c | 1 + merge-recursive.c | 2 ++ 5 files changed, 20 insertions(+), 4 deletions(-)
base-commit: 0b0cc9f86731f894cff8dd25299a9b38c254569e
Signature · 1 line
2.17.0.windows.1
PATCH v2 1/2 merge: Add merge.renames config setting
Add the ability to control rename detection for merge via a config setting.
--- Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 8 +++++++- Documentation/merge-strategies.txt | 6 ++++-- builtin/merge.c | 1 + merge-recursive.c | 1 + 5 files changed, 15 insertions(+), 4 deletions(-)
Documentation/diff-config.txt +2 -1
@@ -112,7 +112,8 @@ diff.orderFile:: diff.renameLimit:: The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off. diff.renames:: Whether and how Git detects renames. If set to "false",
Documentation/merge-config.txt +7 -1
@@ -35,7 +35,13 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off. + +merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. This is the default. merge.renormalize:: Tell Git that canonical representation of files in the
Documentation/merge-strategies.txt +4 -2
@@ -84,12 +84,14 @@ no-renormalize;; `merge.renormalize` configuration variable. no-renames;; - Turn off rename detection. + Turn off rename detection. This overrides the `merge.renames` + configuration variable. See also linkgit:git-diff[1] `--no-renames`. find-renames[=<n>];; Turn on rename detection, optionally setting the similarity - threshold. This is the default. + threshold. This is the default. This overrides the + 'merge.renames' configuration variable. See also linkgit:git-diff[1] `--find-renames`. rename-threshold=<n>;;
builtin/merge.c +1 -0
@@ -424,6 +424,7 @@ static void finish(struct commit *head_commit, opts.output_format |=DIFF_FORMAT_SUMMARY |DIFF_FORMAT_DIFFSTAT ; opts.detect_rename =DIFF_DETECT_RENAME ; +git_config_get_bool ("merge.renames" , &opts.detect_rename);diff_setup_done (&opts);diff_tree_oid (head, new_head,"" , &opts);diffcore_std (&opts);
merge-recursive.c +1 -0
@@ -3256,6 +3256,7 @@ static void merge_recursive_config(struct merge_options *o)git_config_get_int ("merge.verbosity" , &o->verbosity);git_config_get_int ("diff.renamelimit" , &o->diff_rename_limit);git_config_get_int ("merge.renamelimit" , &o->merge_rename_limit); +git_config_get_bool ("merge.renames" , &o->detect_rename);git_config (git_xmerge_config,NULL ); }
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v2 1/2] merge: Add merge.renames config setting
Quoted 1 line · Ben Peart
Add the ability to control rename detection for merge via a config setting.
Sweet, thanks for including the documentation updates.
I lean towards the side of the argument that says that since merge.renameLimit inherits from diff.renameLimit, merge.renames should inherit default value from diff.renames (allow people to not have to repeat themselves as much if they want to use the same rename settings for all cases). Sounds like you and Johannes disagree. I don't feel super strongly about this item, but it'd probably be good to get some other git folks' opinions on this particular point.
Other than that unresolved question, and the separate one about whether to go with a different option instead (e.g. merge.defaultStrategy), as being discussed elsewhere in this thread, the patch looks good to me.
Re: [PATCH v2 1/2] merge: Add merge.renames config setting
In reply to Ben PeartSorry, I noticed something else I missed on my last reading...
builtin/merge.c static void finish(struct commit *head_commit, · line 430
Quoted 12 lines · Ben Peart
builtin/merge.c +1
-0@@ -424,6 +424,7 @@ static void finish(struct commit *head_commit, opts.output_format |=DIFF_FORMAT_SUMMARY |DIFF_FORMAT_DIFFSTAT ; opts.detect_rename =DIFF_DETECT_RENAME ; +git_config_get_bool ("merge.renames" , &opts.detect_rename);diff_setup_done (&opts);diff_tree_oid (head, new_head,"" , &opts);diffcore_std (&opts);
Shouldn't this also be turned off if either (a) merge.renames is unset and diff.renames is false, or (b) the user specifies -Xno-renames?
Re: [PATCH v2 1/2] merge: Add merge.renames config setting
Quoted 19 lines · Elijah Newren
Sorry, I noticed something else I missed on my last reading...
builtin/merge.c static void finish(struct commit *head_commit, · line 430
Quoted 12 lines · Ben Peart
builtin/merge.c +1
-0@@ -424,6 +424,7 @@ static void finish(struct commit *head_commit, opts.output_format |=DIFF_FORMAT_SUMMARY |DIFF_FORMAT_DIFFSTAT ; opts.detect_rename =DIFF_DETECT_RENAME ; +git_config_get_bool ("merge.renames" , &opts.detect_rename);diff_setup_done (&opts);diff_tree_oid (head, new_head,"" , &opts);diffcore_std (&opts);Shouldn't this also be turned off if either (a) merge.renames is unset and diff.renames is false, or (b) the user specifies -Xno-renames?
This makes me think that I should probably remove the line that overrides the detect_rename setting with the merge config setting. As I look at the code, none of the other merge options are reflected in the diffstat; instead, all the settings are pretty much hard coded. Perhaps I shouldn't rock that boat.
Re: [PATCH v2 1/2] merge: Add merge.renames config setting
Quoted 23 lines · Ben Peart
Quoted 12 lines · Elijah Newren
Quoted 10 lines · Ben Peart
builtin/merge.c +1
-0@@ -424,6 +424,7 @@ static void finish(struct commit *head_commit, opts.output_format |=DIFF_FORMAT_SUMMARY |DIFF_FORMAT_DIFFSTAT ; opts.detect_rename =DIFF_DETECT_RENAME ; + git_config_get_bool("merge.renames",Quoted 9 lines
Quoted 4 lines
builtin/merge.c +0
-0&opts.detect_rename);diff_setup_done (&opts);diff_tree_oid (head, new_head,"" , &opts);diffcore_std(&opts);Shouldn't this also be turned off if either (a) merge.renames is unset and diff.renames is false, or (b) the user specifies -Xno-renames?
Quoted 5 lines
This makes me think that I should probably remove the line that overrides the
detect_renamesetting with the merge config setting. As I look at the code, none of the other merge options are reflected in the diffstat; instead, all the settings are pretty much hard coded. Perhaps I shouldn't rock that boat.
Actually, stat_graph_width respects the diff.statGraphWidth config option, even though it's slightly hidden due to the magic value of -1, and being handled from diff.c.
However, trying to get this suggestion of mine hooked up, particularly with -Xno-renames and -Xfind-renames (the latter because it might need to override a merge.renames or diff.renames config setting), might be slightly tricky because the -X options are only passed down to a single merge strategy but this code is outside of the merge strategies. So making it a separate patch, or even a separate patch series may make sense. I'm still interested in this change if you aren't, but I'm fine with it not being part of your series if you don't want to tackle it.
PATCH v2 2/2 merge: Add merge.aggressive config setting
In reply to Ben PeartAdd the ability to control the aggressive flag passed to read-tree via a config setting.
--- Documentation/merge-config.txt | 4 ++++ merge-recursive.c | 1 + 2 files changed, 5 insertions(+)
Documentation/merge-config.txt +4 -0
@@ -1,3 +1,7 @@ +merge.aggressive:: + Passes "aggressive" to read-tree which makes the command resolve + a few more cases internally. See "--aggressive" in linkgit:git-read-tree[1]. + merge.conflictStyle:: Specify the style in which conflicted hunks are written out to working tree files upon merge. The default is "merge", which
merge-recursive.c +1 -0
@@ -355,6 +355,7 @@ static int git_merge_trees(struct merge_options *o, o->unpack_opts.fn = threeway_merge; o->unpack_opts.src_index = &the_index; o->unpack_opts.dst_index = &the_index; +git_config_get_bool ("merge.aggressive" , (int *)&o->unpack_opts.aggressive);setup_unpack_trees_porcelain (&o->unpack_opts,"merge" );init_tree_desc_from_tree (t+0 , common);
Signature · 1 line
2.17.0.windows.1
Documentation/diff-config.txt diff.orderFile:: · line 116
Quoted 5 lines · Ben Peart
diff.renameLimit::
Documentation/diff-config.txt +2
-1The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off.
You mean "turned off via diff.renames"?
This is not meant as a suggestion to rewrite this paragraph further---but if the answer is "no", then that might be an indication that the sentence is inviting a misunderstanding.
Documentation/merge-config.txt include::fmt-merge-msg-config.txt[] · line 43
Quoted 13 lines
diff.renames::
(quoted diff) +0
-0Whether and how Git detects renames. If set to "false",Documentation/merge-config.txt +2
-1@@ -39,7 +39,8 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off.
Ditto. If your design is to make the merge machinery completely ignore diff.renames and only pay attention to merge.renames [*1*], then it probably is a good idea to be more specific here, by saying "... is turned off via ...", though.
Documentation/merge-config.txt include::fmt-merge-msg-config.txt[] · line 46
Quoted 2 lines
Documentation/merge-config.txt +0
-0merge.renames:: Whether and how Git detects renames. If set to "false",
[Footnote]
*1* ...which I do not think is such a good idea, by the way. I'd personally expect merge.renames to allow overriding and falling back to diff.renames, just like the {merge,diff}.renameLimit pair does.
Quoted 10 lines · Junio C Hamano
Documentation/diff-config.txt diff.orderFile:: · line 116
Quoted 5 lines · Ben Peart
diff.renameLimit::
The number of files to consider when performing the copy/renameDocumentation/diff-config.txt +2
-1- detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off.You mean "turned off via diff.renames"?
Quoted 4 lines
This is not meant as a suggestion to rewrite this paragraph further---but if the answer is "no", then that might be an indication that the sentence is inviting a misunderstanding.
Yes, this is referring to turned off via the config setting "diff.renames" but it could also be turned off by passing "--no-renames" on the command line.
To be clear, this documentation change isn't trying to document any changes to the code or behavior - it is just an attempt to clarify what the existing behavior is. If it isn't helping, I can remove it.
Quoted 19 lines
Documentation/merge-config.txt include::fmt-merge-msg-config.txt[] · line 43
Quoted 13 lines
diff.renames::
Whether and how Git detects renames. If set to "false",Documentation/merge-config.txt +2
-1@@ -39,7 +39,8 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off.Ditto. If your design is to make the merge machinery completely ignore diff.renames and only pay attention to merge.renames [*1*], then it probably is a good idea to be more specific here, by saying "... is turned off via ...", though.
Quoted 9 lines
Documentation/merge-config.txt include::fmt-merge-msg-config.txt[] · line 46
Quoted 2 lines
Documentation/merge-config.txt +0
-0merge.renames:: Whether and how Git detects renames. If set to "false",[Footnote]
*1* ...which I do not think is such a good idea, by the way. I'd personally expect merge.renames to allow overriding and falling back to diff.renames, just like the {merge,diff}.renameLimit pair does.
It looks like I'm in the minority on whether the merge settings should inherit from the corresponding diff settings so I will submit a new patch series that does it the same way as the {merge,diff}.renameLimit pair works.
I'll leave it as an exercise for someone else [1] to change any other merge settings that should behave that way.
[1] https://public-inbox.org/git/[email protected]/T/#m52a3dbd0945360bfb873fd3b553472558ef3b796
Quoted 3 lines · Ben Peart
To be clear, this documentation change isn't trying to document any changes to the code or behavior - it is just an attempt to clarify what the existing behavior is.
Yeah, I know, and I do think the new text is a good first step in the right direction; I merely was trying to help in the clarifying effort ;-)
v3 cover add merge.renames config setting
In reply to Ben PeartThis is a complete rewrite based on the feedback from earlier patches.
Update the documentation to better indicate command line options that override various config settings related to merge.
Add a new config merge.renames setting to to control the rename detection behavior of merge. This setting will default to the value of diff.renames.
Also adds logic so that when rename detection is turned off, the aggressive flag is passed to read_tree() so that it can auto resolve more cases that would have been handled by rename detection.
For the repro that I have been using this drops the merge time from ~1 hour to ~5 minutes and the unmerged entries goes down from ~40,000 to 1.
Base Ref: master
Web-Diff: https://github.com/benpeart/git/commit/6a8372d517
Checkout: git fetch https://github.com/benpeart/git merge-options-v3 && git checkout 6a8372d517
### Patches
Ben Peart (3):
merge: update documentation for {merge,diff}.renameLimit
merge: Add merge.renames config setting
merge: pass aggressive when rename detection is turned off
Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 9 +++++++- Documentation/merge-strategies.txt | 6 +++-- diff.c | 2 +- diff.h | 2 ++ merge-recursive.c | 27 +++++++++++++++++------ merge-recursive.h | 8 ++++++- t/t3034-merge-recursive-rename-options.sh | 18 +++++++++++++++ 8 files changed, 62 insertions(+), 13 deletions(-)
base-commit: 1f1cddd558b54bb0ce19c8ace353fd07b758510d
Signature · 1 line
2.17.0.windows.1
PATCH v3 1/3 merge: update documentation for {merge,diff}.renameLimit
Update the documentation to better indicate that the renameLimit setting is ignored if rename detection is turned off via command line options or config settings.
--- Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-)
Documentation/diff-config.txt +2 -1
@@ -112,7 +112,8 @@ diff.orderFile:: diff.renameLimit:: The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off. diff.renames:: Whether and how Git detects renames. If set to "false",
Documentation/merge-config.txt +2 -1
@@ -35,7 +35,8 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off. merge.renormalize:: Tell Git that canonical representation of files in the
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v3 1/3] merge: update documentation for {merge,diff}.renameLimit
Quoted 39 lines · Ben Peart
Update the documentation to better indicate that the renameLimit setting is ignored if rename detection is turned off via command line options or config settings.
Signed-off-by Ben Peart <[email protected]>--- Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-)Documentation/diff-config.txt +2
-1@@ -112,7 +112,8 @@ diff.orderFile:: diff.renameLimit:: The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off. diff.renames:: Whether and how Git detects renames. If set to "false",Documentation/merge-config.txt +2
-2@@ -35,7 +35,8 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off. merge.renormalize:: Tell Git that canonical representation of files in the --
Quoted 1 line
2.17.0.windows.1
Patch looks fine, but it's hard for me not to notice a separate issue in this area independent of your series: I'm curious if we should document that the value of 0 is special here (as per Jonathan Tan's commit 89973554b52c ("diffcore-rename: make diff-tree -l0 mean -l<large>", 2017-11-29)), and doesn't actually drop the limit to 0. cc'ing Jonathan Tan for his thoughts.
Re: [PATCH v3 1/3] merge: update documentation for {merge,diff}.renameLimit
Quoted 6 lines · On Thu, 26 Apr 2018 16:11:50 -0700 Elijah Newren <[email protected]> wrote:
Patch looks fine, but it's hard for me not to notice a separate issue in this area independent of your series: I'm curious if we should document that the value of 0 is special here (as per Jonathan Tan's commit
89973554b52c("diffcore-rename: make diff-tree -l0 mean -l<large>", 2017-11-29)), and doesn't actually drop the limit to 0. cc'ing Jonathan Tan for his thoughts.
Documenting that the value of 0 is special does make sense to me. I think this patch can go in as-is, though - it is already an improvement.
PATCH v3 2/3 merge: Add merge.renames config setting
In reply to Ben PeartAdd the ability to control rename detection for merge via a config setting. This setting behaves the same and defaults to the value of diff.renames but only applies to merge.
--- Documentation/merge-config.txt | 6 ++++++ Documentation/merge-strategies.txt | 6 ++++-- diff.c | 2 +- diff.h | 2 ++ merge-recursive.c | 23 +++++++++++++++++------ merge-recursive.h | 8 +++++++- t/t3034-merge-recursive-rename-options.sh | 18 ++++++++++++++++++ 7 files changed, 55 insertions(+), 10 deletions(-)
Documentation/merge-config.txt +6 -0
@@ -38,6 +38,12 @@ merge.renameLimit:: diff.renameLimit. This setting has no effect if rename detection is turned off. +merge.renames:: + Whether and how Git detects renames. 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. Defaults to the value of diff.renames. + merge.renormalize:: Tell Git that canonical representation of files in the repository has changed over time (e.g. earlier commits record
Documentation/merge-strategies.txt +4 -2
@@ -84,12 +84,14 @@ no-renormalize;; `merge.renormalize` configuration variable. no-renames;; - Turn off rename detection. + Turn off rename detection. This overrides the `merge.renames` + configuration variable. See also linkgit:git-diff[1] `--no-renames`. find-renames[=<n>];; Turn on rename detection, optionally setting the similarity - threshold. This is the default. + threshold. This is the default. This overrides the + 'merge.renames' configuration variable. See also linkgit:git-diff[1] `--find-renames`. rename-threshold=<n>;;
diff.c +1 -1
@@ -177,7 +177,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valureturn 0 ; } -static int git_config_rename (const char *var,const char *value) +int git_config_rename (const char *var,const char *value) {if (!value)return DIFF_DETECT_RENAME ;
diff.h +2 -0
@@ -324,6 +324,8 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb);extern void diff_setup (struct diff_options *);extern int diff_opt_parse (struct diff_options *,const char **,int ,const char *);extern void diff_setup_done (struct diff_options *); +extern int git_config_rename (const char *var,const char *value); +#define DIFF_DETECT_RENAME 1#define DIFF_DETECT_COPY 2
merge-recursive.c +17 -6
@@ -555,13 +555,13 @@ static struct string_list *get_renames(struct merge_options *o,struct diff_options opts; renames =xcalloc (1 ,sizeof (struct string_list )); -if (!o->detect_rename) +if (!merge_detect_rename (o))return renames;diff_setup (&opts); opts.flags.recursive =1 ; opts.flags.rename_empty =0 ; - opts.detect_rename =DIFF_DETECT_RENAME ; + opts.detect_rename =merge_detect_rename (o); opts.rename_limit = o->merge_rename_limit >=0 ? o->merge_rename_limit : o->diff_rename_limit >=0 ? o->diff_rename_limit :1000 ;
@@ -2232,9 +2232,18 @@ int merge_recursive_generic(struct merge_options *o,static void merge_recursive_config (struct merge_options *o) { +char *value =NULL ;git_config_get_int ("merge.verbosity" , &o->verbosity);git_config_get_int ("diff.renamelimit" , &o->diff_rename_limit);git_config_get_int ("merge.renamelimit" , &o->merge_rename_limit); +if (!git_config_get_string ("diff.renames" , &value)) { + o->diff_detect_rename =git_config_rename ("diff.renames" , value); +free (value); + } +if (!git_config_get_string ("merge.renames" , &value)) { + o->merge_detect_rename =git_config_rename ("merge.renames" , value); +free (value); + }git_config (git_xmerge_config,NULL ); }
@@ -2244,10 +2253,11 @@ void init_merge_options(struct merge_options *o)memset (o,0 ,sizeof (struct merge_options )); o->verbosity =2 ; o->buffer_output =1 ; + o->diff_detect_rename =-1 ; + o->merge_detect_rename =-1 ; o->diff_rename_limit =-1 ; o->merge_rename_limit =-1 ; o->renormalize =0 ; - o->detect_rename =1 ;merge_recursive_config (o); merge_verbosity =getenv ("GIT_MERGE_VERBOSITY" );if (merge_verbosity)
@@ -2298,18 +2308,19 @@ int parse_merge_opt(struct merge_options *o, const char *s)else if (!strcmp (s,"no-renormalize" )) o->renormalize =0 ;else if (!strcmp (s,"no-renames" )) - o->detect_rename =0 ; + o->merge_detect_rename =0 ;else if (!strcmp (s,"find-renames" )) { - o->detect_rename =1 ; + o->merge_detect_rename =1 ; o->rename_score =0 ; }else if (skip_prefix (s,"find-renames=" , &arg) ||skip_prefix (s,"rename-threshold=" , &arg)) {if ((o->rename_score =parse_rename_score (&arg)) ==-1 || *arg !=0 )return -1 ; - o->detect_rename =1 ; + o->merge_detect_rename =1 ; }else return -1 ; +return 0 ; }
merge-recursive.h +7 -1
@@ -17,7 +17,8 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_rename; +int diff_detect_rename; +int merge_detect_rename;int diff_rename_limit;int merge_rename_limit;int rename_score;
@@ -28,6 +29,11 @@ struct merge_options {struct hashmap current_file_dir_set;struct string_list df_conflict_file_set; }; +inline int merge_detect_rename (struct merge_options *o) +{ +return o->merge_detect_rename >=0 ? o->merge_detect_rename : + o->diff_detect_rename >=0 ? o->diff_detect_rename :1 ; +}/* merge_trees() but with recursive ancestor consolidation */ int merge_recursive(struct merge_options *o,
t/t3034-merge-recursive-rename-options.sh +18 -0
@@ -309,4 +309,22 @@ test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' ' check_threshold_0 ' +test_expect_success 'merge.renames disables rename detection' ' + git read-tree --reset -u HEAD && + git -c merge.renames=false merge-recursive $tail && + check_no_renames +' + +test_expect_success 'merge.renames defaults to diff.renames' ' + git read-tree --reset -u HEAD && + git -c diff.renames=false merge-recursive $tail && + check_no_renames +' + +test_expect_success 'merge.renames overrides diff.renames' ' + git read-tree --reset -u HEAD && + test_must_fail git -c diff.renames=false -c merge.renames=true merge-recursive $tail && + $check_50 +' + test_done
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Documentation/merge-config.txt merge.renameLimit:: · line 46
Quoted 6 lines · Ben Peart
Documentation/merge-config.txt +6
-0+merge.renames:: + Whether and how Git detects renames. 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. Defaults to the value of diff.renames. +
We shouldn't allow users to force copy detection on for merges The diff side of the code will detect them correctly but the code in merge-recursive will mishandle the copy pairs. I think fixing it is somewhere between big can of worms and it's-a-configuration-that-doesn't-even-make-sense, but it's been a while since I thought about it.
Quoted 15 lines
merge-recursive.h +2
-1@@ -17,7 +17,8 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_rename; +int diff_detect_rename; +int merge_detect_rename;int diff_rename_limit;int merge_rename_limit;int rename_score;
merge-recursive.h struct merge_options { · line 36
Quoted 8 lines
merge-recursive.h +5
-0@@ -28,6 +29,11 @@ struct merge_options {struct hashmap current_file_dir_set;struct string_list df_conflict_file_set; }; +inline int merge_detect_rename (struct merge_options *o) +{ +return o->merge_detect_rename >=0 ? o->merge_detect_rename : + o->diff_detect_rename >=0 ? o->diff_detect_rename :1 ; +}
Why did you split o->detect_rename into two fields? You then recombine them in merge_detect_rename(), and after initial setup only ever access them through that function. Having two fields worries me that people will accidentally introduce bugs by using one of them instead of the merge_detect_rename() function. Is there a reason you decided against having the initial setup just set a single value and then use it directly?
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Quoted 15 lines · Elijah Newren
Documentation/merge-config.txt merge.renameLimit:: · line 46
Quoted 6 lines · Ben Peart
Documentation/merge-config.txt +6
-0+merge.renames:: + Whether and how Git detects renames. 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. Defaults to the value of diff.renames. +We shouldn't allow users to force copy detection on for merges The diff side of the code will detect them correctly but the code in merge-recursive will mishandle the copy pairs. I think fixing it is somewhere between big can of worms and it's-a-configuration-that-doesn't-even-make-sense, but it's been a while since I thought about it.
Color me puzzled. :) The consensus was that the default value for merge.renames come from diff.renames. diff.renames supports copy detection which means that merge.renames will inherit that value. My assumption was that is what was intended so when I reimplemented it, I fully implemented it that way.
Are you now requesting to only use diff.renames as the default if the value is true or false but not if it is copy? What should happen if diff.renames is actually set to copy? Should merge silently change that to true, display a warning, error out, or something else? Do you have some other behavior for how to handle copy being inherited from diff.renames you'd like to see?
Can you write the documentation that clearly explains the exact behavior you want? That would kill two birds with one stone... :)
Quoted 25 lines
Quoted 16 lines
merge-recursive.h +2
-1@@ -17,7 +17,8 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_rename; +int diff_detect_rename; +int merge_detect_rename;int diff_rename_limit;int merge_rename_limit;int rename_score;merge-recursive.h struct merge_options { · line 36
Quoted 8 lines
merge-recursive.h +5
-0@@ -28,6 +29,11 @@ struct merge_options {struct hashmap current_file_dir_set;struct string_list df_conflict_file_set; }; +inline int merge_detect_rename (struct merge_options *o) +{ +return o->merge_detect_rename >=0 ? o->merge_detect_rename : + o->diff_detect_rename >=0 ? o->diff_detect_rename :1 ; +}
Quoted 8 lines
Why did you split
o->detect_renameinto two fields? You then recombine them inmerge_detect_rename(), and after initial setup only ever access them through that function. Having two fields worries me that people will accidentally introduce bugs by using one of them instead of themerge_detect_rename()function. Is there a reason you decided against having the initial setup just set a single value and then use it directly?
The setup of this value is split into 3 places that may or may not all get called. The initial values, the values that come from the config settings and then any values passed on the command line.
Because the merge value can now inherit from the diff value, you only know the final value after you have received all possible inputs. That makes it necessary to be a calculated value.
If you look at diff_rename_limit/merge_rename_limit, detect_rename follow the same pattern for the same reasons. It turns out detect_rename was a little more complex because it is used in 3 different locations (vs just one) which is why I wrapped the inheritance logic into the helper function merge_detect_rename().
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Quoted 13 lines · Ben Peart
Color me puzzled. :) The consensus was that the default value for merge.renames come from diff.renames. diff.renames supports copy detection which means that merge.renames will inherit that value. My assumption was that is what was intended so when I reimplemented it, I fully implemented it that way.
Are you now requesting to only use diff.renames as the default if the value is true or false but not if it is copy? What should happen if diff.renames is actually set to copy? Should merge silently change that to true, display a warning, error out, or something else? Do you have some other behavior for how to handle copy being inherited from diff.renames you'd like to see?
Quoted 2 lines
Can you write the documentation that clearly explains the exact behavior you want? That would kill two birds with one stone... :)
I think demoting from copy to rename-only is a good idea, at least for now, because I do not believe we have figured out what we want to happen when we detect copied files are involved in a merge.
But I am not sure if we even want to fail merge.renames=copy as an invalid configuration. So my gut feeling of the best solution to the above is to do something like:
- whether the configuration comes from diff.renames or merge.renames, turn *.renames=copy to true inside the merge recursive machinery.
- document the fact in "git merge-recursive" documentation (or "git merge" documentation) to say "_currently_ asking for rename detection to find copies and renames will do the same thing---copies are ignored", impliying "this might change in the future", in the BUGS section.
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Quoted 30 lines · Junio C Hamano
Quoted 13 lines · Ben Peart
Color me puzzled. :) The consensus was that the default value for merge.renames come from diff.renames. diff.renames supports copy detection which means that merge.renames will inherit that value. My assumption was that is what was intended so when I reimplemented it, I fully implemented it that way.
Are you now requesting to only use diff.renames as the default if the value is true or false but not if it is copy? What should happen if diff.renames is actually set to copy? Should merge silently change that to true, display a warning, error out, or something else? Do you have some other behavior for how to handle copy being inherited from diff.renames you'd like to see?
Quoted 2 lines
Can you write the documentation that clearly explains the exact behavior you want? That would kill two birds with one stone... :)
I think demoting from copy to rename-only is a good idea, at least for now, because I do not believe we have figured out what we want to happen when we detect copied files are involved in a merge.
But I am not sure if we even want to fail merge.renames=copy as an invalid configuration. So my gut feeling of the best solution to the above is to do something like:
- whether the configuration comes from diff.renames or merge.renames, turn *.renames=copy to true inside the merge recursive machinery.
Quoted 5 lines
- document the fact in "git merge-recursive" documentation (or "git merge" documentation) to say "_currently_ asking for rename detection to find copies and renames will do the same thing---copies are ignored", impliying "this might change in the future", in the BUGS section.
Yes, I agree. One more thing:
- It may be best to avoid advertising "copies" as a vaild option for
merge.renames since it doesn't have any current practical use anywhere. (Remove the sentence 'If set to "copies" or "copy", Git will detect copies, as well.' from the documentation)
My rationale for translating "copy" to "true" is a little different than Junio's, though:
1) The reason we have configuration options around renames and copies is primarily because they are expensive to compute. So we let some users specify that they don't want them, other users are willing to pay for rename detection, and others are willing to pay for both rename and copy detection.
2) If rename/copy detection were cheap, every part of git would just compute whatever level of detection was relevant and use it.
3) The resolve and octopus merge strategies ignores diff.renames and merge.renames, because they don't have logic to use any rename information. diff and log can use both renames and copies. And the recursive merge machinery is code which can use renames but not copies.
4) Therefore, translating from "copy" to "true" inside the merge recursive machinery is fine and not an error because we are using as much detection information as is relevant to the algorithm and which the user is willing to pay for.
To throw one more wrinkle in here, merge.renames could actually be set to "copy" and make sense, because we compute diffs multiple times. Twice within the recursive merge machinery (for which we'd want to translate "copy" to "true"), and once for the diffstat at the end (which comes from builtin/merge.c, and for which it could make sense to detect copies).
(Kind of curious whether Junio agrees with my rationale or thinks I'm out in left field with it...)
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Hi,
Quoted 31 lines · Elijah Newren
Quoted 30 lines · Junio C Hamano
Quoted 13 lines · Ben Peart
Color me puzzled. :) The consensus was that the default value for merge.renames come from diff.renames. diff.renames supports copy detection which means that merge.renames will inherit that value. My assumption was that is what was intended so when I reimplemented it, I fully implemented it that way.
Are you now requesting to only use diff.renames as the default if the value is true or false but not if it is copy? What should happen if diff.renames is actually set to copy? Should merge silently change that to true, display a warning, error out, or something else? Do you have some other behavior for how to handle copy being inherited from diff.renames you'd like to see?
Quoted 2 lines
Can you write the documentation that clearly explains the exact behavior you want? That would kill two birds with one stone... :)
I think demoting from copy to rename-only is a good idea, at least for now, because I do not believe we have figured out what we want to happen when we detect copied files are involved in a merge.
But I am not sure if we even want to fail merge.renames=copy as an invalid configuration. So my gut feeling of the best solution to the above is to do something like:
- whether the configuration comes from diff.renames or merge.renames, turn *.renames=copy to true inside the merge recursive machinery.
Quoted 7 lines
Quoted 5 lines
- document the fact in "git merge-recursive" documentation (or "git merge" documentation) to say "_currently_ asking for rename detection to find copies and renames will do the same thing---copies are ignored", impliying "this might change in the future", in the BUGS section.
Yes, I agree.
Guys, you argued long and hard that one config setting (diff.renames) should magically imply another one (merge.renames), on the basis that they essentially do the same.
And now you are suggesting that they *cannot* be essentially the same? Are you agreeing on such a violation of the Law of Least Surprise?
Please, make up your mind. Inheriting merge.renames from diff.renames is "magic" enough to be puzzling. Introducing that auto-demoting is just simply creating a bad user experience. Please don't.
It is fine, of course, to admit at this stage that the whole magic idea of letting merge.renames inherit from diff.renames was only half thought through (we're in reviewing stage right now for the purpose of weighing alternative approaches and trying to come up with the best solution after all) and that we should go with Ben's original approach, which has the rather huge and dramatic advantage of being very, very clear and easy to understand to the user. We could even document that (and why) the 'copy' value in merge.renames is not allowed for the time being.
Ciao,
Dscho
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Hi Dsco,
Quoted 1 line · Johannes Schindelin
Hi,
<snip>
Quoted 4 lines
Guys, you argued long and hard that one config setting (diff.renames) should magically imply another one (merge.renames), on the basis that they essentially do the same.
I apologize for any frustration; I've probably caused a good deal of it by repeatedly catching or noticing things after one review and then bringing them up later. I just didn't catch it all at first.
Your restatement of the basis of the argument doesn't match my rationale, though. My reasoning was that (1) the configuration options exist to allow the user to specify how much of a performance cost they're willing to pay, (2) the two options are separate because some users might want to configure one more loosely or tightly than the other, but (3) many users will want to just specify once how much they are willing to pay without being forced to repeat themselves for different parts of git (diff, merge, possible future commands).
I'll add to my former basis by stating that I think the diffstat at the end of the merge should be controlled by these variables, even if that's not implemented as part of Ben's series. And both of those configuration options clearly feed into whether that diffstat should be doing rename or copy or no detection. While they could be kept separate options, forcing us to somehow decide which one overrides which, I think it's much more natural if we already have merge.renames inheriting from and overriding diff.renames.
Quoted 6 lines
And now you are suggesting that they *cannot* be essentially the same? Are you agreeing on such a violation of the Law of Least Surprise?
Please, make up your mind. Inheriting merge.renames from diff.renames is "magic" enough to be puzzling. Introducing that auto-demoting is just simply creating a bad user experience. Please don't.
From my view, resolve and octopus merges auto-demote diff.renames and merge.renames to false. In fact, they optimize the work involved in that demotion by not even checking the value. I think demoting "copy" to "true" in the recursive merge machinery is no more surprising than that is. You can find more details to this argument in the portion of my email that you responded to but snipped out.
But to add to that argument, if auto-demotion is a violation of the Law of Least Surprise, as you claim, then naming this option merge.renames is also a violation of the Law of Least Surprise. You would instead need to rename it to something like merge.recursive.renames. (And then when a new merge strategy comes along that also does renames, we'll need to add a merge.ort.renames.)
Quoted 8 lines
It is fine, of course, to admit at this stage that the whole magic idea of letting merge.renames inherit from diff.renames was only half thought through (we're in reviewing stage right now for the purpose of weighing alternative approaches and trying to come up with the best solution after all) and that we should go with Ben's original approach, which has the rather huge and dramatic advantage of being very, very clear and easy to understand to the user. We could even document that (and why) the 'copy' value in merge.renames is not allowed for the time being.
It was only half thought through, yes, at least by me. But the more I think through it, the more I like the inheritance personally. I see no problem with the demotion and think the inheritance has the advantage of being easier to understand, because I see your proposal as causing questions like:
- "Why does merge.renameLimit inherit from diff.renameLimit but
merge.renames doesn't from diff.renames?"
- "Why can't I just specify in one place that I {am, am not} willing
to pay for {full, both copy and} rename detection wherever it makes sense?"
- "How do I control the detection for the diffstat at the end of the merge?"
Hope that helps,
Elijah
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
In reply to Junio C HamanoQuoted 3 lines · Junio C Hamano
I think demoting from copy to rename-only is a good idea, at least for now, because I do not believe we have figured out what we want to happen when we detect copied files are involved in a merge.
Does anyone know some threads concerning that topic? I tried to search for it, but somehow "merge copy detection" did not find me useful threads so far. I would be interested in that topic anyway, so I would like to know what the ideas are that floated so far.
Greetings,
Eckhard
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Quoted 10 lines · Eckhard Maaß
Quoted 3 lines · Junio C Hamano
I think demoting from copy to rename-only is a good idea, at least for now, because I do not believe we have figured out what we want to happen when we detect copied files are involved in a merge.
Does anyone know some threads concerning that topic? I tried to search for it, but somehow "merge copy detection" did not find me useful threads so far. I would be interested in that topic anyway, so I would like to know what the ideas are that floated so far.
I doubt it has ever been discussed before this thread. But, if you're curious, I'll try to dump a few thoughts. Let's say we have branches A and B, and:
A: modifies file z
B: copies z to y
Should the modifications to z done in A propagate to both z and y? If not, what good is copy detection? If so, then there are several ramifications...
- If B not only copied z but also first modified it, then do we have potential conflicts with both z and y -- possibly the exact same conflicts, making the user resolve them repeatedly?
- What if A copied z to x? Do changes to z propagate to all three of z and x and y? Do changes to either x or y affect z? Do they affect each other?
- If A deleted z, does that give us a copy/delete conflict for y? Do we also have to worry about copy/add conflicts? copy/add/delete? rename/copy (multiple variants)? copy/copy?
- Extra degrees of freedom may mean new conflict types:
- The extra degrees of freedom from renames introduced multiple new conflict types (e.g. rename/add, rename/rename(1to2), rename/rename(2to1)).
- Directory rename detection added more (rename/rename/rename(1to3), rename/rename(Nto1), add/add/add, directory/file/file, n-fold transitive rename, etc.), -- and forced us to specify various rules to limit the possibility space so that conflicts could be representable in the index and understandable by the user.
- I suspect adding copies would add new types of conflicts we haven't thought of yet.
The more I think about it, the more I think that attempting to detect copies in a merge algorithm just doesn't make sense. Anything I can think of that someone might attempt to use detected copies for would just surprise users in a bad way...and it'd open up a big can of edge and corner case problems as well.
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Quoted 2 lines · Elijah Newren
I doubt it has ever been discussed before this thread. But, if you're curious, I'll try to dump a few thoughts.
Thank you, I try to dump some of mine, too. Maybe let me first stress that for me copy detection without --find-copies-harder is much more a "find content extracted" (like methods being factored out). In a way this is nearer to a rename than to a real copy.
Quoted 8 lines
[...] Let's say we have branches
A and B, and:
A: modifies file z
B: copies z to yShould the modifications to z done in A propagate to both z and y? If not, what good is copy detection? If so, then there are several ramifications...
If one just assumes the most likely outcome is that something from z wad factored out to y, it might just be sufficient to see whether the modifications of the two branches apply cleanly - if A touched the parts of B that have been factored out there would be a normal merge conflict (where one could be nice and give a hint that some content was copied to y on the B branch), if A did not touched the parts touched (or moved) by B, then there is no problem. If A exactly deleted the content moved by B, there will be no conflict - but this is seems to be strange anyway.
I admit that a "real" copy would get unnoticed that way. But the semantics of such a copy isn't too clear for me either - did I copy the other part to make it independent of the other or did I just employ a copy and paste tactic? The former does not want the changes, the later does. But I am happy catering to the former here.
To sum up:
- fail as before for conflicting merges, but give a hint that one has copied to quicken up resolution.
Quoted 3 lines
- If B not only copied z but also first modified it, then do we have potential conflicts with both z and y -- possibly the exact same conflicts, making the user resolve them repeatedly?
With the above suggestion, if there are conflicts, you fail and give a hint.
Quoted 3 lines
- What if A copied z to x? Do changes to z propagate to all three of z and x and y? Do changes to either x or y affect z? Do they affect each other?
A copy on branch to x and one another to y seems strange even if z merges cleanly. Did both sides try to factor the same thing out to different files? Or did they try to make something independent, but managed to make it to different files? For this I would be inclined to just suggest fail with a copy/copy(somewhere else). But this is a real corner case after all. Has anyone seen just thing in practice?
Quoted 3 lines
- If A deleted z, does that give us a copy/delete conflict for y? Do we also have to worry about copy/add conflicts? copy/add/delete? rename/copy (multiple variants)? copy/copy?
We do have the modified/deleted conflict where we could hint that content also has been copied and then not try to do more.
Quoted 5 lines
- Extra degrees of freedom may mean new conflict types:
- The extra degrees of freedom from renames introduced multiple new conflict types (e.g. rename/add, rename/rename(1to2), rename/rename(2to1)).
For renaming one side and coping the other, I would think doing the same as above is sensible enough: if there are conflicts one can give an additional hint of the one part having been copied, but not change the kind of conflicts much.
Quoted 4 lines
The more I think about it, the more I think that attempting to detect copies in a merge algorithm just doesn't make sense. Anything I can think of that someone might attempt to use detected copies for would just surprise users in a bad way...
Hm, it didn't sound like that. Would you think that users would be surprised by my suggestions? Or are they all too corner casey to be worth implementing anyway?
Greetings,
Eckhard
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
Hi Eckhard,
Quoted 8 lines · Eckhard Maaß
Quoted 2 lines · Elijah Newren
I doubt it has ever been discussed before this thread. But, if you're curious, I'll try to dump a few thoughts.
Thank you, I try to dump some of mine, too. Maybe let me first stress that for me copy detection without --find-copies-harder is much more a "find content extracted" (like methods being factored out). In a way this is nearer to a rename than to a real copy.
Ooh, if you wanted to detect movement of code between files (as blame does, I think searches for PICKAXE_BLAME_MOVE would point you in the right direction) and then try to use that during merge to allow applied changes to move with the code, that would be awesome. Expensive, and might be a lot of work to wire it all up, but it'd be very interesting. I was only discussing DIFF_DETECT_COPY in my previous email, which was all about duplicating entire files; that's something I don't see utility in right now for resolving merges.
<snip>
Quoted 5 lines
I admit that a "real" copy would get unnoticed that way. But the semantics of such a copy isn't too clear for me either - did I copy the other part to make it independent of the other or did I just employ a copy and paste tactic? The former does not want the changes, the later does. But I am happy catering to the former here.
Right, if you have to assume that the copy was made to make the code independent, then there's no value for merge resolution to having detected the copy in the first place. That has the advantage of side-stepping the possible new edge and corner cases I mentioned in the rest of my email, but it means we shouldn't even spend time detecting copies -- whether whole file (via DIFF_DETECT_COPY) or individual lines (via PICKAXE_BLAME_COPY and variants).
Elijah
Re: [PATCH v3 2/3] merge: Add merge.renames config setting
In reply to Ben PeartQuoted 45 lines · Ben Peart
Quoted 28 lines · Elijah Newren
Quoted 15 lines · Ben Peart
merge-recursive.h +2
-1@@ -17,7 +17,8 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_rename; +int diff_detect_rename; +int merge_detect_rename;int diff_rename_limit;int merge_rename_limit;int rename_score;merge-recursive.h struct merge_options { · line 36
Quoted 8 lines
merge-recursive.h +5
-0@@ -28,6 +29,11 @@ struct merge_options {struct hashmap current_file_dir_set;struct string_list df_conflict_file_set; }; +inline int merge_detect_rename (struct merge_options *o) +{ +return o->merge_detect_rename >=0 ? o->merge_detect_rename : + o->diff_detect_rename >=0 ? o->diff_detect_rename :1 ; +}Quoted 8 lines
Why did you split
o->detect_renameinto two fields? You then recombine them inmerge_detect_rename(), and after initial setup only ever access them through that function. Having two fields worries me that people will accidentally introduce bugs by using one of them instead of themerge_detect_rename()function. Is there a reason you decided against having the initial setup just set a single value and then use it directly?The setup of this value is split into 3 places that may or may not all get called. The initial values, the values that come from the config settings and then any values passed on the command line.
Because the merge value can now inherit from the diff value, you only know the final value after you have received all possible inputs. That makes it necessary to be a calculated value.
Quoted 5 lines
If you look at diff_rename_limit/merge_rename_limit,
detect_renamefollow the same pattern for the same reasons. It turns outdetect_renamewas a little more complex because it is used in 3 different locations (vs just one) which is why I wrapped the inheritance logic into the helper functionmerge_detect_rename().
Ah, you're following the precedent set by diff_rename_limit/merge_rename_limit; that makes sense. Thanks for the explanation. I believe another possibility here is that for both the {merge,diff}_rename_limit pair of variables and the {diff,merge}_renames pair of variables, since the code parses all inputs before ever using the result, we could calculate the result once and store it rather than storing the constituent pieces of the calculation. That would also prevent people from trying to use one of the pieces of the calculation instead of treating it as a coherent whole. However, while I would have preferred that the rename_limit pair of variables also went away in favor of just one field which is updated as it parses each input option, what you have is fine for this series.
From: Elijah Newren <[email protected]>
Quoted 2 lines · Ben Peart
Can you write the documentation that clearly explains the exact behavior you want? That would kill two birds with one stone... :)
Sure, something like the following is what I envision, and I've tried to include the suggestion from Junio to document the copy behavior in the merge-recursive documentation.
-- 8< --
Subject: [PATCH] fixup! merge: Add merge.renames config setting
--- Documentation/merge-config.txt | 3 +-- Documentation/merge-strategies.txt | 5 +++-- merge-recursive.c | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-)
Documentation/merge-config.txt +1 -2
@@ -41,8 +41,7 @@ merge.renameLimit:: merge.renames:: Whether and how Git detects renames. 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. Defaults to the value of diff.renames. + detection is enabled. Defaults to the value of diff.renames. merge.renormalize:: Tell Git that canonical representation of files in the
Documentation/merge-strategies.txt +3 -2
@@ -23,8 +23,9 @@ recursive:: causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history. Additionally this can detect and handle merges involving - renames. This is the default merge strategy when - pulling or merging one branch. + renames, but currently cannot make use of detected + copies. This is the default merge strategy when pulling + or merging one branch. + The 'recursive' strategy can take the following options:
merge-recursive.c +8 -0
@@ -564,6 +564,14 @@ static struct string_list *get_renames(struct merge_options *o, opts.flags.recursive =1 ; opts.flags.rename_empty =0 ; opts.detect_rename =merge_detect_rename (o); +/* +* We do not have logic to handle the detection of copies. In +* fact, it may not even make sense to add such logic: would we +* really want a change to a base file to be propagated through +* multiple other files by a merge? +*/ +if (opts.detect_rename >DIFF_DETECT_RENAME ) + opts.detect_rename =DIFF_DETECT_RENAME ; opts.rename_limit = o->merge_rename_limit >=0 ? o->merge_rename_limit : o->diff_rename_limit >=0 ? o->diff_rename_limit :1000 ;
Signature · 1 line
2.17.0.294.g8e3b83c0e3
Re:
Quoted 60 lines · Elijah Newren
From: Elijah Newren <[email protected]>
Quoted 2 lines · Ben Peart
Can you write the documentation that clearly explains the exact behavior you want? That would kill two birds with one stone... :)
Sure, something like the following is what I envision, and I've tried to include the suggestion from Junio to document the copy behavior in the merge-recursive documentation.
-- 8< --
Subject: [PATCH] fixup! merge: Add merge.renames config setting--- Documentation/merge-config.txt | 3 +-- Documentation/merge-strategies.txt | 5 +++-- merge-recursive.c | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-)Documentation/merge-config.txt +1
-2@@ -41,8 +41,7 @@ merge.renameLimit:: merge.renames:: Whether and how Git detects renames. 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. Defaults to the value of diff.renames. + detection is enabled. Defaults to the value of diff.renames. merge.renormalize:: Tell Git that canonical representation of files in theDocumentation/merge-strategies.txt +3
-2@@ -23,8 +23,9 @@ recursive:: causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history. Additionally this can detect and handle merges involving - renames. This is the default merge strategy when - pulling or merging one branch. + renames, but currently cannot make use of detected + copies. This is the default merge strategy when pulling + or merging one branch. + The 'recursive' strategy can take the following options:merge-recursive.c +1
-0@@ -564,6 +564,14 @@ static struct string_list *get_renames(struct merge_options *o, opts.flags.recursive =1 ; opts.flags.rename_empty =0 ; opts.detect_rename =merge_detect_rename (o); +/*
merge-recursive.c static struct string_list *get_renames(struct merge_options *o, · line 577
Quoted 10 lines
merge-recursive.c +7
-0+* We do not have logic to handle the detection of copies. In +* fact, it may not even make sense to add such logic: would we +* really want a change to a base file to be propagated through +* multiple other files by a merge? +*/ +if (opts.detect_rename >DIFF_DETECT_RENAME ) + opts.detect_rename =DIFF_DETECT_RENAME ; opts.rename_limit = o->merge_rename_limit >=0 ? o->merge_rename_limit : o->diff_rename_limit >=0 ? o->diff_rename_limit :1000 ;
Thanks Elijah. I've applied this patch and reviewed and tested it. It works and addresses the concerns around the settings inheritance from diff.renames. I still _prefer_ the simpler model that doesn't do the partial inheritance but I can use this model as well.
I'm unsure on the protocol here. Should I incorporate this patch and submit a reroll or can it just be applied as is?
Re:
Quoted 15 lines · Ben Peart
Quoted 14 lines · Elijah Newren
From: Elijah Newren <[email protected]>
Quoted 3 lines · Ben Peart
Can you write the documentation that clearly explains the exact behavior you
want? That would kill two birds with one stone... :)Sure, something like the following is what I envision, and I've tried to include the suggestion from Junio to document the copy behavior in the merge-recursive documentation.
<snip>
Quoted 8 lines
Thanks Elijah. I've applied this patch and reviewed and tested it. It works and addresses the concerns around the settings inheritance from diff.renames. I still _prefer_ the simpler model that doesn't do the partial inheritance but I can use this model as well.
I'm unsure on the protocol here. Should I incorporate this patch and submit a reroll or can it just be applied as is?
I suspect you'll want to re-roll anyway, to base your series on en/rename-directory-detection-reboot instead of on master. (Junio plans to merge it down to next, and your series has four different merge conflicts with it.)
There are two other loose ends with this series that Junio will need to weigh in on:
- I'm obviously a strong proponent of the inherited setting, but Junio may change his mind after reading Dscho's arguments against it (or after reading my arguments for it).
- I like the setting as-is, and think we could allow a "copy" setting for merge.renames to specify that the post-merge diffstat should detect copies (not part of your series, but a useful addition I'd like to tackle afterwards). However, Junio had comments in [email protected] about merge.renames handling the scoring as well, like -Xfind-renames. Those sound incompatible to me for a single setting, and I'm unsure if Junio would resolve them the way I do or still feels strongly about the scoring.
Re:
Quoted 38 lines · Elijah Newren
Quoted 15 lines · Ben Peart
Quoted 14 lines · Elijah Newren
From: Elijah Newren <[email protected]>
Quoted 3 lines · Ben Peart
Can you write the documentation that clearly explains the exact behavior you
want? That would kill two birds with one stone... :)Sure, something like the following is what I envision, and I've tried to include the suggestion from Junio to document the copy behavior in the merge-recursive documentation.
<snip>
Quoted 8 lines
Thanks Elijah. I've applied this patch and reviewed and tested it. It works and addresses the concerns around the settings inheritance from diff.renames. I still _prefer_ the simpler model that doesn't do the partial inheritance but I can use this model as well.
I'm unsure on the protocol here. Should I incorporate this patch and submit a reroll or can it just be applied as is?
I suspect you'll want to re-roll anyway, to base your series on en/rename-directory-detection-reboot instead of on master. (Junio plans to merge it down to next, and your series has four different merge conflicts with it.)
There are two other loose ends with this series that Junio will need to weigh in on:
- I'm obviously a strong proponent of the inherited setting, but Junio may change his mind after reading Dscho's arguments against it (or after reading my arguments for it).
Quoted 9 lines
- I like the setting as-is, and think we could allow a "copy" setting for merge.renames to specify that the post-merge diffstat should detect copies (not part of your series, but a useful addition I'd like to tackle afterwards). However, Junio had comments in [email protected] about merge.renames handling the scoring as well, like -Xfind-renames. Those sound incompatible to me for a single setting, and I'm unsure if Junio would resolve them the way I do or still feels strongly about the scoring.
I think this patch series (including Elijah's fixup!) improves the situation from where we were and it provides the necessary functionality to solve the problem I started out to solve. While there are other changes that could be made, I think they should be done in separate follow up patches.
I'm happy to reroll this incorporating the fixup! so that we can make progress. Junio, would you prefer I reroll this based on en/rename-directory-detection-reboot or master?
PATCH v3 3/3 merge: pass aggressive when rename detection is turned off
In reply to Ben PeartSet aggressive flag in git_merge_trees() when rename detection is turned off. This allows read_tree() to auto resolve more cases that would have otherwise been handled by the rename detection.
--- merge-recursive.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
merge-recursive.c +3 -1
@@ -276,6 +276,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) }static int git_merge_trees (int index_only, +int aggressive,struct tree *common,struct tree *head,struct tree *merge)
@@ -294,6 +295,7 @@ static int git_merge_trees(int index_only, opts.fn = threeway_merge; opts.src_index = &the_index; opts.dst_index = &the_index; + opts.aggressive = aggressive;setup_unpack_trees_porcelain (&opts,"merge" );init_tree_desc_from_tree (t+0 , common);
@@ -1993,7 +1995,7 @@ int merge_trees(struct merge_options *o,return 1 ; } - code =git_merge_trees (o->call_depth, common, head, merge); + code =git_merge_trees (o->call_depth, !merge_detect_rename (o), common, head, merge);if (code !=0 ) {if (show (o,4 ) || o->call_depth)
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v3 3/3] merge: pass aggressive when rename detection is turned off
Quoted 40 lines · Ben Peart
Set aggressive flag in
git_merge_trees()when rename detection is turned off. This allowsread_tree()to auto resolve more cases that would have otherwise been handled by the rename detection.Reviewed-by Johannes Schindelin <[email protected]>Signed-off-by Ben Peart <[email protected]>--- merge-recursive.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)merge-recursive.c +3
-2@@ -276,6 +276,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) }static int git_merge_trees (int index_only, +int aggressive,struct tree *common,struct tree *head,struct tree *merge)@@ -294,6 +295,7 @@ static int git_merge_trees(int index_only, opts.fn = threeway_merge; opts.src_index = &the_index; opts.dst_index = &the_index; + opts.aggressive = aggressive;setup_unpack_trees_porcelain (&opts,"merge" );init_tree_desc_from_tree (t+0 , common);@@ -1993,7 +1995,7 @@ int merge_trees(struct merge_options *o,return 1 ; } - code =git_merge_trees (o->call_depth, common, head, merge); + code =git_merge_trees (o->call_depth, !merge_detect_rename (o), common, head, merge);if (code !=0 ) {if (show (o,4 ) || o->call_depth) --
Quoted 1 line
2.17.0.windows.1
Patch looks fine but as a heads up -- since merge_options is a parameter in git_merge_trees after the en/rename-directory-detection-reboot lands, we'll be able to switch this patch to set opts.aggressive directly instead of needing to pass it in as a parameter.
Re: [PATCH v3 0/3] add merge.renames config setting
In reply to Ben PeartHi Ben,
Quoted 1 line · Ben Peart
This is a complete rewrite based on the feedback from earlier patches.
Thanks for pushing forward on this.
Quoted 10 lines
Update the documentation to better indicate command line options that override various config settings related to merge.
Add a new config merge.renames setting to to control the rename detection behavior of merge. This setting will default to the value of diff.renames.
Also adds logic so that when rename detection is turned off, the aggressive flag is passed to
read_tree()so that it can auto resolve more cases that would have been handled by rename detection.
Quoted 8 lines
For the repro that I have been using this drops the merge time from ~1 hour to ~5 minutes and the unmerged entries goes down from ~40,000 to 1.
Helped-by Kevin Willford <[email protected]>Reviewed-by Johannes Schindelin <[email protected]>Signed-off-by Ben Peart <[email protected]>Base Ref: master
We may need to figure out how to coordinate amongst a few topics. Looking over your patches, there are going to be a few conflicts with en/rename-directory-detection-reboot, so this won't apply to pu. Martin's series to introduce clear_unpack_trees_porcelain()[1], which he was waiting to submit until mine went through, will also conflict with this, if he uses the changes I suggested for the handling in merge-recursive[2]. These aren't major conflicts, but I'm just flagging it.
[1] https://public-inbox.org/git/[email protected]/
[2] https://public-inbox.org/git/[email protected]/
v4 cover add additional config settings for merge
In reply to Ben PeartThis version incorporates Elijah's fixup patch and is now based on en/rename-directory-detection in next.
Base Ref: en/rename-directory-detection
Web-Diff: https://github.com/benpeart/git/commit/16b175c25f
Checkout: git fetch https://github.com/benpeart/git merge-renames-v4 && git checkout 16b175c25f
### Patches
Ben Peart (3):
merge: update documentation for {merge,diff}.renameLimit
merge: Add merge.renames config setting
merge: pass aggressive when rename detection is turned off
Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 8 +++++- Documentation/merge-strategies.txt | 11 +++++--- diff.c | 2 +- diff.h | 1 + merge-recursive.c | 31 ++++++++++++++++++----- merge-recursive.h | 8 +++++- t/t3034-merge-recursive-rename-options.sh | 18 +++++++++++++ 8 files changed, 68 insertions(+), 14 deletions(-)
base-commit: c5b761fb2711542073cf1906c0e86a34616b79ae
Signature · 1 line
2.17.0.windows.1
PATCH v4 1/3 merge: update documentation for {merge,diff}.renameLimit
Update the documentation to better indicate that the renameLimit setting is ignored if rename detection is turned off via command line options or config settings.
--- Documentation/diff-config.txt | 3 ++- Documentation/merge-config.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-)
Documentation/diff-config.txt +2 -1
@@ -112,7 +112,8 @@ diff.orderFile:: diff.renameLimit:: The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off. diff.renames:: Whether and how Git detects renames. If set to "false",
Documentation/merge-config.txt +2 -1
@@ -35,7 +35,8 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off. merge.renormalize:: Tell Git that canonical representation of files in the
Signature · 1 line
2.17.0.windows.1
PATCH v4 2/3 merge: Add merge.renames config setting
In reply to Ben PeartAdd the ability to control rename detection for merge via a config setting. This setting behaves the same and defaults to the value of diff.renames but only applies to merge.
--- Documentation/merge-config.txt | 5 ++++ Documentation/merge-strategies.txt | 11 ++++++--- diff.c | 2 +- diff.h | 1 + merge-recursive.c | 30 ++++++++++++++++++----- merge-recursive.h | 8 +++++- t/t3034-merge-recursive-rename-options.sh | 18 ++++++++++++++ 7 files changed, 63 insertions(+), 12 deletions(-)
Documentation/merge-config.txt +5 -0
@@ -38,6 +38,11 @@ merge.renameLimit:: diff.renameLimit. This setting has no effect if rename detection is turned off. +merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. Defaults to the value of diff.renames. + merge.renormalize:: Tell Git that canonical representation of files in the repository has changed over time (e.g. earlier commits record
Documentation/merge-strategies.txt +7 -4
@@ -23,8 +23,9 @@ recursive:: causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history. Additionally this can detect and handle merges involving - renames. This is the default merge strategy when - pulling or merging one branch. + renames, but currently cannot make use of detected + copies. This is the default merge strategy when pulling + or merging one branch. + The 'recursive' strategy can take the following options:
@@ -84,12 +85,14 @@ no-renormalize;; `merge.renormalize` configuration variable. no-renames;; - Turn off rename detection. + Turn off rename detection. This overrides the `merge.renames` + configuration variable. See also linkgit:git-diff[1] `--no-renames`. find-renames[=<n>];; Turn on rename detection, optionally setting the similarity - threshold. This is the default. + threshold. This is the default. This overrides the + 'merge.renames' configuration variable. See also linkgit:git-diff[1] `--find-renames`. rename-threshold=<n>;;
diff.c +1 -1
@@ -177,7 +177,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valureturn 0 ; } -static int git_config_rename (const char *var,const char *value) +int git_config_rename (const char *var,const char *value) {if (!value)return DIFF_DETECT_RENAME ;
diff.h +1 -0
@@ -321,6 +321,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb);extern void diff_setup (struct diff_options *);extern int diff_opt_parse (struct diff_options *,const char **,int ,const char *);extern void diff_setup_done (struct diff_options *); +extern int git_config_rename (const char *var,const char *value);#define DIFF_DETECT_RENAME 1#define DIFF_DETECT_COPY 2
merge-recursive.c +24 -6
@@ -1524,7 +1524,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,diff_setup (&opts); opts.flags.recursive =1 ; opts.flags.rename_empty =0 ; - opts.detect_rename =DIFF_DETECT_RENAME ; + opts.detect_rename =merge_detect_rename (o); +/* +* We do not have logic to handle the detection of copies. In +* fact, it may not even make sense to add such logic: would we +* really want a change to a base file to be propagated through +* multiple other files by a merge? +*/ +if (opts.detect_rename >DIFF_DETECT_RENAME ) + opts.detect_rename =DIFF_DETECT_RENAME ; opts.rename_limit = o->merge_rename_limit >=0 ? o->merge_rename_limit : o->diff_rename_limit >=0 ? o->diff_rename_limit :1000 ;
@@ -2564,7 +2572,7 @@ static int handle_renames(struct merge_options *o, ri->head_renames =NULL ; ri->merge_renames =NULL ; -if (!o->detect_rename) +if (!merge_detect_rename (o))return 1 ; head_pairs =get_diffpairs (o, common, head);
@@ -3241,9 +3249,18 @@ int merge_recursive_generic(struct merge_options *o,static void merge_recursive_config (struct merge_options *o) { +char *value =NULL ;git_config_get_int ("merge.verbosity" , &o->verbosity);git_config_get_int ("diff.renamelimit" , &o->diff_rename_limit);git_config_get_int ("merge.renamelimit" , &o->merge_rename_limit); +if (!git_config_get_string ("diff.renames" , &value)) { + o->diff_detect_rename =git_config_rename ("diff.renames" , value); +free (value); + } +if (!git_config_get_string ("merge.renames" , &value)) { + o->merge_detect_rename =git_config_rename ("merge.renames" , value); +free (value); + }git_config (git_xmerge_config,NULL ); }
@@ -3256,7 +3273,8 @@ void init_merge_options(struct merge_options *o) o->diff_rename_limit =-1 ; o->merge_rename_limit =-1 ; o->renormalize =0 ; - o->detect_rename =1 ; + o->diff_detect_rename =-1 ; + o->merge_detect_rename =-1 ;merge_recursive_config (o); merge_verbosity =getenv ("GIT_MERGE_VERBOSITY" );if (merge_verbosity)
@@ -3307,16 +3325,16 @@ int parse_merge_opt(struct merge_options *o, const char *s)else if (!strcmp (s,"no-renormalize" )) o->renormalize =0 ;else if (!strcmp (s,"no-renames" )) - o->detect_rename =0 ; + o->merge_detect_rename =0 ;else if (!strcmp (s,"find-renames" )) { - o->detect_rename =1 ; + o->merge_detect_rename =1 ; o->rename_score =0 ; }else if (skip_prefix (s,"find-renames=" , &arg) ||skip_prefix (s,"rename-threshold=" , &arg)) {if ((o->rename_score =parse_rename_score (&arg)) ==-1 || *arg !=0 )return -1 ; - o->detect_rename =1 ; + o->merge_detect_rename =1 ; }else return -1 ;
merge-recursive.h +7 -1
@@ -18,7 +18,8 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_rename; +int diff_detect_rename; +int merge_detect_rename;int diff_rename_limit;int merge_rename_limit;int rename_score;
@@ -55,6 +56,11 @@ struct collision_entry {struct string_list source_files; unsigned reported_already:1; }; +inline int merge_detect_rename (struct merge_options *o) +{ +return o->merge_detect_rename >=0 ? o->merge_detect_rename : + o->diff_detect_rename >=0 ? o->diff_detect_rename :1 ; +}/* merge_trees() but with recursive ancestor consolidation */ int merge_recursive(struct merge_options *o,
t/t3034-merge-recursive-rename-options.sh +18 -0
@@ -309,4 +309,22 @@ test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' ' check_threshold_0 ' +test_expect_success 'merge.renames disables rename detection' ' + git read-tree --reset -u HEAD && + git -c merge.renames=false merge-recursive $tail && + check_no_renames +' + +test_expect_success 'merge.renames defaults to diff.renames' ' + git read-tree --reset -u HEAD && + git -c diff.renames=false merge-recursive $tail && + check_no_renames +' + +test_expect_success 'merge.renames overrides diff.renames' ' + git read-tree --reset -u HEAD && + test_must_fail git -c diff.renames=false -c merge.renames=true merge-recursive $tail && + $check_50 +' + test_done
Signature · 1 line
2.17.0.windows.1
Re: [PATCH v4 2/3] merge: Add merge.renames config setting
Ben Peart <[email protected]> writes:
I'd downcase the verb on the subject.
Quoted 13 lines
Add the ability to control rename detection for merge via a config setting. This setting behaves the same and defaults to the value of diff.renames but only applies to merge.
Reviewed-by Johannes Schindelin <[email protected]>Helped-by Elijah Newren <[email protected]>Signed-off-by Ben Peart <[email protected]>...
merge-recursive.h +0
-0
merge-recursive.h struct collision_entry { · line 63
Quoted 8 lines
merge-recursive.h +5
-0@@ -55,6 +56,11 @@ struct collision_entry {struct string_list source_files; unsigned reported_already:1; }; +inline int merge_detect_rename (struct merge_options *o) +{ +return o->merge_detect_rename >=0 ? o->merge_detect_rename : + o->diff_detect_rename >=0 ? o->diff_detect_rename :1 ; +}
I'll tweak the above to leave a blank before the function, and make it "static inline", to ensure that the output from
$ git grep -e '\<inline\>' --and --not -e 'static inline' -- \*.h
is empty.
PATCH v4 3/3 merge: pass aggressive when rename detection is turned off
In reply to Ben PeartSet aggressive flag in git_merge_trees() when rename detection is turned off. This allows read_tree() to auto resolve more cases that would have otherwise been handled by the rename detection.
--- merge-recursive.c | 1 + 1 file changed, 1 insertion(+)
merge-recursive.c +1 -0
@@ -355,6 +355,7 @@ static int git_merge_trees(struct merge_options *o, o->unpack_opts.fn = threeway_merge; o->unpack_opts.src_index = &the_index; o->unpack_opts.dst_index = &the_index; + o->unpack_opts.aggressive = !merge_detect_rename (o);setup_unpack_trees_porcelain (&o->unpack_opts,"merge" );init_tree_desc_from_tree (t+0 , common);
Signature · 1 line
2.17.0.windows.1
Hi Ben,
Thanks for your persistence.
Quoted 2 lines · Ben Peart
This version incorporates Elijah's fixup patch and is now based on en/rename-directory-detection in next.
en/rename-directory-detection was in master but reverted. en/rename-directory-detection-reboot is the new series, and isn't quite in next yet; Junio just said in the last "What's cooking" that he intends to merge it down. However, the two series are obviously similar, so your rebasing even onto the old one does cut down on the conflicts considerably. Rebasing this latest series onto en/rename-directory-detection-reboot leaves just one minor conflict.
Anyway, I've looked over this latest re-roll and it looks good to me: