Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs from interesting renaming pairs: one side renames A/file -> B/file, the other B/ -> A/ #1039

Closed
wants to merge 3 commits into from

Conversation

newren
Copy link
Contributor

@newren newren commented Jun 26, 2021

Anders Kaseorg recently reported a few issues in an interesting rename case[1]. I was able to duplicate and find multiple bugs from it; two in merge-recursive, and one in merge-ort. This series has some fixes.

Changes since v1:

  • Added a third testcase

[1] https://lore.kernel.org/git/CABPp-BGDfucqae=HNES_QmmsjpDbdHrR6CG=H3gtiDygHzquVg@mail.gmail.com/

cc: Derrick Stolee stolee@gmail.com
cc: Elijah Newren newren@gmail.com

@newren
Copy link
Contributor Author

newren commented Jun 26, 2021

/submit

@gitgitgadget-git
Copy link

Submitted as pull.1039.git.git.1624727121.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git pr-git-1039/newren/rename-plus-dir-rename-cancel-v1

To fetch this version to local tag pr-git-1039/newren/rename-plus-dir-rename-cancel-v1:

git fetch --no-tags https://github.com/gitgitgadget/git tag pr-git-1039/newren/rename-plus-dir-rename-cancel-v1

@gitgitgadget-git
Copy link

This branch is now known as en/merge-dir-rename-corner-case-fix.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via be673f5.

@gitgitgadget-git
Copy link

User Derrick Stolee <stolee@gmail.com> has been added to the cc: list.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 5e78701.

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 C/file.
Since the default for merge.directoryRenames is conflict, this results
in an error message saying it is unclear whether the file should be
placed at B/file or C/file.

What if C/ is A/, though?  In such a case, the transitive rename would
give us A/file, the original name we started with.  Logically, having
an error message with B/file vs. A/file should be fine, as should
leaving the file where it started.  But the logic in both
merge-recursive and merge-ort did not handle a case of a filename being
renamed to itself correctly; merge-recursive had two bugs, and merge-ort
had one.  Add some testcases covering such a scenario.

Based-on-testcase-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Elijah Newren <newren@gmail.com>
Path conflicts (typically rename path conflicts, e.g.
rename/rename(1to2) or rename/add/delete), and directory/file conflicts
should obviously result in files not being marked as clean in the merge.
We had a codepath where we missed consulting the path_conflict and
df_conflict flags, based on match_mask.  Granted, it requires an unusual
setup to trigger this codepath (directory rename causing rename-to-self
is the only case I can think of), but we still need to handle it.  To
make it clear that we have audited the other codepaths that do not
explicitly mention these flags, add some assertions that the flags are
not set.

Reported-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Elijah Newren <newren@gmail.com>
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.

Reported-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Elijah Newren <newren@gmail.com>
@newren newren force-pushed the rename-plus-dir-rename-cancel branch from dea97c2 to 7c96d6c Compare June 29, 2021 23:30
@gitgitgadget-git
Copy link

User Elijah Newren <newren@gmail.com> has been added to the cc: list.

@newren
Copy link
Contributor Author

newren commented Jun 30, 2021

/submit

@gitgitgadget-git
Copy link

Submitted as pull.1039.v2.git.git.1625074200.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git pr-git-1039/newren/rename-plus-dir-rename-cancel-v2

To fetch this version to local tag pr-git-1039/newren/rename-plus-dir-rename-cancel-v2:

git fetch --no-tags https://github.com/gitgitgadget/git tag pr-git-1039/newren/rename-plus-dir-rename-cancel-v2

@gitgitgadget-git
Copy link

This patch series was integrated into seen via d7ef8a8.

@gitgitgadget-git
Copy link

The branch en/merge-dir-rename-corner-case-fix was mentioned in the "New Topics" section of the status updates on the Git mailing list.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 7b60e3b.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 026f7d0.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 149c6e6.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via c549cf9.

@gitgitgadget-git
Copy link

There was a status update in the "Cooking" section about the branch en/merge-dir-rename-corner-case-fix on the Git mailing list:

The merge code had funny interactions between content based rename
detection and directory rename detection.

Will merge to 'next'.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 11bd56e.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 9f1136e.

@gitgitgadget-git
Copy link

This patch series was integrated into next via cf70875.

@gitgitgadget-git gitgitgadget-git bot added the next label Jul 8, 2021
@gitgitgadget-git
Copy link

There was a status update in the "Cooking" section about the branch en/merge-dir-rename-corner-case-fix on the Git mailing list:

The merge code had funny interactions between content based rename
detection and directory rename detection.

Will merge to 'master'.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via 0c118dc.

@gitgitgadget-git
Copy link

There was a status update in the "Cooking" section about the branch en/merge-dir-rename-corner-case-fix on the Git mailing list:

The merge code had funny interactions between content based rename
detection and directory rename detection.

Will merge to 'master'.

@gitgitgadget-git
Copy link

This patch series was integrated into seen via d3b88be.

@gitgitgadget-git
Copy link

This patch series was integrated into next via d3b88be.

@gitgitgadget-git
Copy link

This patch series was integrated into master via d3b88be.

@gitgitgadget-git
Copy link

Closed via d3b88be.

@newren newren deleted the rename-plus-dir-rename-cancel branch July 19, 2021 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant