@@ -368,91 +368,42 @@ def generate_patch(
368368) -> str :
369369 """Generate a git patch covering all detected drift.
370370
371- - Modified files: standard git diff
372- - Extra (untracked) files: added via git diff --no-index /dev/null <file>
373- - Missing ( deleted) files: removal diffs via diff header with empty content
371+ Uses `git add -N` to mark untracked (extra) files as intent-to-add,
372+ then runs `git diff` on the specific affected files to capture
373+ modified, new, and deleted files in one clean patch.
374374 """
375- parts : list [str ] = []
375+ paths = [d ["path" ] for d in content_diffs ] + extra_files + missing_files
376+ if not paths :
377+ return ""
376378
377- # Modified files
378- if content_diffs :
379- paths = [d ["path" ] for d in content_diffs ]
379+ # Mark untracked files as intent-to-add so git diff includes them
380+ if extra_files :
380381 try :
381- parts .append (_git ("diff" , "--" , * paths ))
382+ subprocess .run (
383+ ["git" , "add" , "-N" , "--" , * extra_files ],
384+ check = True ,
385+ capture_output = True ,
386+ )
382387 except subprocess .CalledProcessError :
383388 pass
384389
385- # Extra files — generate "new file" diffs
386- for path_str in extra_files :
387- file_path = Path (path_str )
388- try :
389- content = file_path .read_bytes ()
390- except (FileNotFoundError , OSError ):
391- continue
392- # Check if binary
393- try :
394- text = content .decode ("utf-8" )
395- except UnicodeDecodeError :
396- parts .append (
397- f"diff --git a/{ path_str } b/{ path_str } \n "
398- f"new file mode 100644\n "
399- f"Binary files /dev/null and b/{ path_str } differ\n "
400- )
401- continue
402- lines = text .splitlines (keepends = True )
403- n = len (lines )
404- diff_body = "" .join (
405- f"+{ line } "
406- if line .endswith ("\n " )
407- else f"+{ line } \n \\ No newline at end of file\n "
408- for line in lines
409- )
410- parts .append (
411- f"diff --git a/{ path_str } b/{ path_str } \n "
412- f"new file mode 100644\n "
413- f"--- /dev/null\n "
414- f"+++ b/{ path_str } \n "
415- f"@@ -0,0 +1,{ n } @@\n "
416- f"{ diff_body } "
417- )
390+ try :
391+ patch = _git ("diff" , "--" , * paths )
392+ except subprocess .CalledProcessError :
393+ patch = ""
418394
419- # Missing files — generate deletion diffs
420- for path_str in missing_files :
395+ # Undo the intent-to-add so we don't leave index dirty
396+ if extra_files :
421397 try :
422- committed_bytes = subprocess .run (
423- ["git" , "show" , f"HEAD:{ path_str } " ],
424- capture_output = True ,
398+ subprocess .run (
399+ ["git" , "reset" , "--" , * extra_files ],
425400 check = True ,
426- ).stdout
427- except subprocess .CalledProcessError :
428- continue
429- try :
430- text = committed_bytes .decode ("utf-8" )
431- except UnicodeDecodeError :
432- parts .append (
433- f"diff --git a/{ path_str } b/{ path_str } \n "
434- f"deleted file mode 100644\n "
435- f"Binary files a/{ path_str } and /dev/null differ\n "
401+ capture_output = True ,
436402 )
437- continue
438- lines = text .splitlines (keepends = True )
439- n = len (lines )
440- diff_body = "" .join (
441- f"-{ line } "
442- if line .endswith ("\n " )
443- else f"-{ line } \n \\ No newline at end of file\n "
444- for line in lines
445- )
446- parts .append (
447- f"diff --git a/{ path_str } b/{ path_str } \n "
448- f"deleted file mode 100644\n "
449- f"--- a/{ path_str } \n "
450- f"+++ /dev/null\n "
451- f"@@ -1,{ n } +0,0 @@\n "
452- f"{ diff_body } "
453- )
403+ except subprocess .CalledProcessError :
404+ pass
454405
455- return "" . join ( parts )
406+ return patch
456407
457408
458409# ---------------------------------------------------------------------------
0 commit comments