@@ -398,21 +398,43 @@ def apply_patch(self, checkout: Checkout, diff_text: str) -> None:
398398 ``subprocess.CalledProcessError`` if the patch does not apply
399399 cleanly. ``--index`` keeps the index in sync so newly added files
400400 show up in :meth:`collect_changes`. The patch is written to a temp
401- file (not passed on the command line) so its size is unbounded."""
401+ file (not passed on the command line) so its size is unbounded.
402+
403+ Strict ``git apply`` requires the hunk's context *and* its ``@@``
404+ line numbers to match exactly. LLM-authored diffs routinely have
405+ slightly-off line numbers or whitespace, so we retry with
406+ ``--recount`` (recompute line numbers from the hunk body) and
407+ ``--whitespace=fix``. The retry still requires the context lines to
408+ match, so it tolerates bad geometry without applying to the wrong
409+ place — ``git apply --index`` is all-or-nothing, so a failed attempt
410+ leaves the tree untouched for the next one."""
402411 with tempfile .NamedTemporaryFile (
403412 "w" , suffix = ".patch" , delete = False , encoding = "utf-8"
404413 ) as fh :
405414 patch_path = fh .name
406415 # git apply requires the diff to end with a newline.
407416 fh .write (diff_text if diff_text .endswith ("\n " ) else diff_text + "\n " )
417+ attempts = (
418+ ("strict" , ["apply" , "--index" , "--whitespace=nowarn" , patch_path ]),
419+ (
420+ "recount" ,
421+ ["apply" , "--index" , "--whitespace=fix" , "--recount" , patch_path ],
422+ ),
423+ )
408424 try :
409- self ._git (
410- checkout .path ,
411- "apply" ,
412- "--index" ,
413- "--whitespace=nowarn" ,
414- patch_path ,
415- timeout = 120 ,
425+ last_stderr = ""
426+ for label , args in attempts :
427+ proc = self ._git (checkout .path , * args , timeout = 120 , check = False )
428+ if proc .returncode == 0 :
429+ if label != "strict" :
430+ log .info (
431+ "patch applied via '%s' fallback (strict git apply failed)" ,
432+ label ,
433+ )
434+ return
435+ last_stderr = proc .stderr .decode ("utf-8" , errors = "replace" )
436+ raise subprocess .CalledProcessError (
437+ 1 , ["git" , "apply" ], stderr = last_stderr .encode ()
416438 )
417439 finally :
418440 os .unlink (patch_path )
0 commit comments