User Tools

Site Tools


python:gpt-patcher

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
python:gpt-patcher [2025/11/06 14:17] Wuffpython:gpt-patcher [2025/11/07 16:26] (current) Wuff
Line 4: Line 4:
  
 The script below can help apply these patches. The script below can help apply these patches.
-It accepts file, stdin, clipboard or interactive manual paste as source for the patch, parses it for the target filename, checks if it can apply the patch and outputs the newly patched file.+It accepts file, stdin, clipboard or interactive manual paste as source for the patch, parses it for the target filename or prompts if none found, checks if it can apply the patch and outputs the newly patched file.
 Command line option -i can replace the original file immediately, but creates backup files. Command line option -i can replace the original file immediately, but creates backup files.
 Command line option -t can be used to patch a different filename. Command line option -t can be used to patch a different filename.
Line 37: Line 37:
     else:     else:
         clip = pyperclip.paste().strip() if pyperclip else ""         clip = pyperclip.paste().strip() if pyperclip else ""
-        if clip and PATCH_BEGIN in clip and PATCH_END in clip:+        if clip:
             print("Using patch from clipboard.")             print("Using patch from clipboard.")
             data = clip             data = clip
Line 51: Line 51:
  
  
-def validate_patch_format(data: str): +def detect_patch_format(data: str): 
-    """Ensure patch contains Begin/End Patch and Update File headers.""" +    """Return (has_header, has_update_line) flags.""" 
-    if PATCH_BEGIN not in data or PATCH_END not in data+    has_header = PATCH_BEGIN in data and PATCH_END in data 
-        sys.exit("Error: Patch must contain '*** Begin Patch' and '*** End Patch'.") +    has_update_line = PATCH_UPDATE_FILE in data 
-    if PATCH_UPDATE_FILE not in data+    return has_header, has_update_line
-        sys.exit("Error: Patch must contain '*** Update File:' line.") +
-    return True+
  
  
-def parse_patch(data: str):+def parse_patch(data: str, filename_override=None):
     """Extract the target filename and list of hunks."""     """Extract the target filename and list of hunks."""
     match = re.search(r"\*\*\* Update File:\s*(.+)", data)     match = re.search(r"\*\*\* Update File:\s*(.+)", data)
-    if not match: +    filename = match.group(1).strip() if match else filename_override 
-        sys.exit("Error: Could not find file in patch header.") + 
-    filename = match.group(1).strip()+    if not filename
 +        filename = input("No target file specifiedPlease enter the filename to patch: ").strip() 
 +        if not filename: 
 +            sys.exit("Error: Target filename required.")
  
 +    # Extract hunks (between @@ markers)
     hunks = []     hunks = []
     current_hunk = []     current_hunk = []
     in_hunk = False     in_hunk = False
- 
     for line in data.splitlines():     for line in data.splitlines():
         if line.startswith("@@"):         if line.startswith("@@"):
Line 77: Line 78:
             current_hunk = []             current_hunk = []
             in_hunk = True             in_hunk = True
-        elif line.strip() == PATCH_END: 
-            if current_hunk: 
-                hunks.append(current_hunk) 
-            break 
         elif in_hunk:         elif in_hunk:
             current_hunk.append(line)             current_hunk.append(line)
 +    if current_hunk: 
 +        hunks.append(current_hunk)
     return filename, hunks     return filename, hunks
  
Line 115: Line 113:
     match = re.search(pattern, content)     match = re.search(pattern, content)
     if not match:     if not match:
-        # try without requiring both contexts (looser match)+        # Try looser match
         match = re.search(re.escape(old_block), content)         match = re.search(re.escape(old_block), content)
         if not match:         if not match:
Line 140: Line 138:
     old_block = "\n".join(minus_lines)     old_block = "\n".join(minus_lines)
     new_block = "\n".join(plus_lines)     new_block = "\n".join(plus_lines)
- 
-    # extract leading and trailing context if available 
     context_before = "\n".join(context_lines[:3]) if context_lines else None     context_before = "\n".join(context_lines[:3]) if context_lines else None
     context_after = "\n".join(context_lines[-3:]) if context_lines else None     context_after = "\n".join(context_lines[-3:]) if context_lines else None
Line 160: Line 156:
  
 def main(): def main():
-    parser = argparse.ArgumentParser(description="Apply ChatGPT unified diffs to files.")+    parser = argparse.ArgumentParser(description="Apply ChatGPT or raw unified diffs to files.")
     parser.add_argument("patchfile", nargs="?", help="File containing the patch (optional)")     parser.add_argument("patchfile", nargs="?", help="File containing the patch (optional)")
     parser.add_argument("-i", "--in-place", action="store_true", help="Replace original file (create backup)")     parser.add_argument("-i", "--in-place", action="store_true", help="Replace original file (create backup)")
Line 168: Line 164:
  
     data = read_patch_input(args.patchfile)     data = read_patch_input(args.patchfile)
-    validate_patch_format(data) +    has_headerhas_update_line detect_patch_format(data)
-    patch_filehunks parse_patch(data)+
  
-    target_file = args.target or patch_file+    # If header present but update line missing, prompt for file 
 +    patch_file, hunks = parse_patch(data, filename_override=args.target)
  
-    if not os.path.exists(target_file): +    if not os.path.exists(patch_file): 
-        sys.exit(f"Error: Target file '{target_file}' not found.")+        sys.exit(f"Error: Target file '{patch_file}' not found.")
  
-    result = apply_patch_to_file(target_file, hunks)+    result = apply_patch_to_file(patch_file, hunks)
  
     if args.in_place:     if args.in_place:
-        backup = make_backup(target_file+        backup = make_backup(patch_file
-        with open(target_file, "w", encoding="utf-8") as f:+        with open(patch_file, "w", encoding="utf-8") as f:
             f.write(result)             f.write(result)
-        print(f"Patched {target_file} (backup: {backup})")+        print(f"Patched {patch_file} (backup: {backup})")
     else:     else:
         sys.stdout.write(result)         sys.stdout.write(result)
Line 189: Line 185:
 if __name__ == "__main__": if __name__ == "__main__":
     main()     main()
- 
 </code> </code>
python/gpt-patcher.1762438675.txt.gz · Last modified: by Wuff