Instead of using msysgit 1.9.5, try the latest Git for Windows:
Since Git 2.18, git status
does a better job detecting renamed (of files and folders)
With Git 2.33 (Q3 2021), it will go even further, managing content based rename detection and directory rename detection.
But only with the new merge strategy ORT ("Ostensibly Recursive's Twin").
See commit 3585d0e, commit a492d53, commit 806f832 (30 Jun 2021) by Elijah Newren (newren
).
(Merged by Junio C Hamano -- gitster
-- in commit d3b88be, 16 Jul 2021)
Reported-by: Anders Kaseorg
Signed-off-by: Elijah Newren
Directory rename detection can cause transitive renames, e.g. if the two different sides of history each do one half of:
A/file -> B/file
B/ -> C/
then directory rename detection transitively renames to give us
A/file -> C/file
However, when C/ == A/, note that this gives us
A/file -> A/file.
merge-recursive assumed that any rename D -> E
would have D != E
.
While that is almost always true, the above is a special case where it is not.
So we cannot do things like delete the rename source, we cannot assume that a file existing at path E implies a rename/add conflict and we have to be careful about what stages end up in the output.
This change feels a bit hackish.
It took me surprisingly many hours to find, and given merge-recursive's design causing it to attempt to enumerate all combinations of edge and corner cases with special code for each combination, I'm worried there are other similar fixes needed elsewhere if we can just come up with the right special testcase.
Perhaps an audit would rule it out, but I have not the energy.
merge-recursive deserves to die, and since it is on its way out anyway, fixing this particular bug narrowly will have to be good enough.
A corner case bug in the ort merge strategy has been corrected with Git 2.35 (Q1 2022).
See commit d30126c (28 Dec 2021) by Elijah Newren (newren
).
(Merged by Junio C Hamano -- gitster
-- in commit 2c54104, 10 Jan 2022)
merge-ort
: fix bug with renormalization and rename/delete conflicts
Reported-by: Ralf Thielow
Signed-off-by: Elijah Newren
Reviewed-by: Derrick Stolee
Ever since commit a492d53 (merge-ort
: ensure we consult df_conflict and path_conflicts, 2021-06-30, Git v2.33.0-rc0 -- merge listed in batch #5) ("merge-ort: ensure we consult df_conflict
and path_conflicts",
2021-06-30), when renormalization is active AND a file is involved in a rename/delete conflict BUT the file is unmodified (either before or after renormalization), merge-ort was running into an assertion failure.
Prior to that commit (or if assertions were compiled out), merge-ort would mis-merge instead, ignoring the rename/delete conflict and just deleting the file.
Remove the assertions, fix the code appropriately, leave some good comments in the code, and add a testcase for this situation.
Git 2.38 (Q3 2022) fixes a long-standing corner case bug around directory renames in the merge-ort strategy.
See commit 751e165, commit 3ffbe5a, commit 6dd1f0e, commit 51e41e4, commit 0565cee (05 Jul 2022) by Elijah Newren (newren
).
(Merged by Junio C Hamano -- gitster
-- in commit e3349f2, 18 Jul 2022)
merge-ort
: fix issue with dual rename and add/add conflict
Signed-off-by: Elijah Newren
There is code in both merge-recursive and merge-ort for avoiding doubly transitive renames (i.e.
one side renames directory A/ -> B/
, and the other side renames directory B/ -> C/
), because this combination would otherwise make a mess for new files added to A/
on the first side and wondering which directory they end up in -- especially if there were even more renames such as the first side renaming C/ -> D/
.
In such cases, it just turns "off" directory rename detection for the higher order transitive cases.
Another way to look at this is that if the source name involved in a directory rename on one side is the target name of a directory rename operation for a file from the other side, then we avoid the doubly transitive rename.
(More concretely, if a directory rename on side D
wants to rename a file on side E
from OLD_NAME
-> NEW_NAME,
and side D
already had a file named NEW_NAME,
and a directory rename on side E
wants to rename side D's NEW_NAME
-> NEWER_NAME,
then we turn off the directory rename detection for NEW_NAME
to prevent the NEW_NAME
-> NEWER_NAME
rename, and instead end up with an add/add conflict on NEW_NAME
.)