-
Notifications
You must be signed in to change notification settings - Fork 157
Fix another crazy rename assertion #1992
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
base: master
Are you sure you want to change the base?
Fix another crazy rename assertion #1992
Conversation
A comment at the top of t6429 mentions why the test doesn't exercise git rebase or git cherry-pick. However, it claims that it uses `test-tool fast-rebase`. That was true when the comment was written, but commit f920b02 (replay: introduce new builtin, 2023-11-24) changed it to use git replay without updating this comment. We could potentially just strike this second comment, since git replay is a bonified built-in, but perhaps the explanation about why it focuses on git replay is still useful. Update the comment to make it accurate again. Signed-off-by: Elijah Newren <newren@gmail.com>
While developing commit a16e8ef (merge-ort: fix merge.directoryRenames=false, 2025-03-13), I was testing things out and had an extra condition on one of the if-blocks that I occasionally swapped between '&& 0' and '&& 1' to see the effects of the changes. I forgot to remove it before submitting and it wasn't caught in review. Remove it now. Signed-off-by: Elijah Newren <newren@gmail.com>
At GitHub, we had a repository that was triggering git: merge-ort.c:3032: process_renames: Assertion `newinfo && !newinfo->merged.clean` failed. during git replay. This sounds similar to the somewhat recent f6ecb60 (merge-ort: fix directory rename on top of source of other rename/delete, 2025-08-06), but the cause is different. Unlike that case, there are no rename-to-self situations arising in this case, and new to this case it can only be triggered during a replay operation on the 2nd or later commit being replayed, never on the first merge in the sequence. To trigger, the repository needs: * an upstream which: * renames a file to a different directory, e.g. old/file -> new/file * leaves other files remaining in the original directory (so that e.g. "old/" still exists upstream even though file has been removed from it and placed elsewhere) * a topic branch being rebased where: * a commit in the sequence: * modifies old/file * a subsequent commit in the sequence being replayed: * does NOT touch *anything* under new/ * does NOT touch old/file * DOES modify other paths under old/ * does NOT have any relevant renames that we need to detect _anywhere_ elsewhere in the tree (meaning this interacts interestingly with both directory renames and cached renames) In such a case, the assertion will trigger. The fix turns out to be surprisingly simple. I have a very vague recollection that I actually considered whether to add such an if-check years ago when I added the very similar one for oldinfo in 1b6b902 (merge-ort: process_renames() now needs more defensiveness, 2021-01-19), but I think I couldn't figure out a possible way to trigger it and was worried at the time that if I didn't know how to trigger it then I wasn't so sure that simply skipping it was correct. Waiting did give me a chance to put more thorough tests and checks into place for the rename-to-self cases a few months back, which I might not have found as easily otherwise. Anyway, put the check in place now and add a test that demonstrates the fix. Note that this bug, as demonstrated by the conditions listed above, runs at the intersection of relevant renames, trivial directory resolutions, and cached renames. All three of those optimizations are ones that unfortunately make the code (and testcases!) a bit more complex, and threading all three makes it a bit more so. However, the testcase isn't crazy enough that I'd expect no one to ever hit it in practice, and was confused why we didn't see it before. After some digging, I discovered that merge.directoryRenames=false is a workaround to this bug, and GitHub used that setting until recently (it was a "temporary" match-what-libgit2-does piece of code that lasted years longer than intended). Since the conditions I gave above for triggering this bug rule out the possibility of there being directory renames, one might assume that it shouldn't matter whether you try to detect such renames if there aren't any. However, due to commit a16e8ef (merge-ort: fix merge.directoryRenames=false, 2025-03-13), the heavy hammer used there means that merge.directoryRenames=false ALSO turns off rename caching, which is critical to triggering the bug. This becomes a bit more than an aside since... Re-reading that old commit, a16e8ef (merge-ort: fix merge.directoryRenames=false, 2025-03-13), it appears that the solution to this latest bug might have been at least a partial alternative solution to that old commit. And it may have been an improved alternative (or at least help implement one), since it may be able to avoid the heavy-handed disabling of rename cache. That might be an interesting future thing to investigate, but is not critical for the current fix. However, since I spent time digging it all up, at least leave a small comment tweak breadcrumb to help some future reader (myself or others) who wants to dig further to connect the dots a little quicker. Signed-off-by: Elijah Newren <newren@gmail.com>
9fc91d9 to
9095098
Compare
|
/submit |
|
Submitted as pull.1992.git.1762192908.gitgitgadget@gmail.com To fetch this version into To fetch this version to local tag |
|
This patch series was integrated into seen via git@835f3cc. |
|
This branch is now known as |
|
This patch series was integrated into seen via git@fea4b28. |
|
This patch series was integrated into seen via git@88d12f9. |
…nput-error-message CIHelper.parsePRURLInput: fix misleading error message
|
There was a status update in the "New Topics" section about the branch Yet another corner case fix around renames in the "ort" merge strategy. Will merge to 'next'? source: <pull.1992.git.1762192908.gitgitgadget@gmail.com> |
|
This patch series was integrated into seen via git@057e273. |
|
This patch series was integrated into seen via git@80b838a. |
|
This patch series was integrated into seen via git@65b3639. |
|
This patch series was integrated into seen via git@779547d. |
| # NOTE 1: this testfile tends to not only rename files, but modify on both | ||
| # sides; without modifying on both sides, optimizations can kick in | ||
| # which make rename detection irrelevant or trivial. We want to make | ||
| # sure that we are triggering rename caching rather than rename |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, "Kristoffer Haugsbakk" wrote (reply to this):
On Mon, Nov 3, 2025, at 19:01, Elijah Newren via GitGitGadget wrote:
> From: Elijah Newren <newren@gmail.com>
>
> A comment at the top of t6429 mentions why the test doesn't exercise git
> rebase or git cherry-pick. However, it claims that it uses `test-tool
> fast-rebase`. That was true when the comment was written, but commit
> f920b0289ba3 (replay: introduce new builtin, 2023-11-24) changed it to
> use git replay without updating this comment.
>
> We could potentially just strike this second comment, since git replay
> is a bonified built-in, but perhaps the explanation about why it focuses
s/bonified/bona fide/ ?
> on git replay is still useful. Update the comment to make it accurate
> again.
>
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---|
User |
This fixes another special corner case that was being triggered at GitHub; the error being triggered is the same as what I submitted a fix for a few months ago, but the way it was triggered and the fix needed are different in this case. See the final commit message for details. The first two patches are just tiny cleanups I noticed while investigating the problem.
I will also note that I first came up with an alternative fix -- checking in use_cached_pairs() whether
new_namewas contained in theopt->priv->pathsstrmap, and if not, skipping to the next cached rename instead of adding it topairs. That would also work, but it would mean that if a yet-subsequent commit after that did modify the old/file path, I think we'd have to re-detect the rename, which would hurt the effectiveness of the cached renames optimization. Simply avoiding using it in process_renames() allows it to avoid being forgotten (and since old/file is NOT modified, the upstream rename remains valid). Besides, this fix is nicely symmetrical to the check on !oldinfo, so it seems more aesthetic to me as well as helping us preserve performance.cc: "Kristoffer Haugsbakk" kristofferhaugsbakk@fastmail.com