添加gitdiff.py 2041/2
author孙春阳 <snickersun@163.com>
Mon, 4 Aug 2025 03:48:51 +0000 (11:48 +0800)
committer孙春阳 <snickersun@163.com>
Mon, 4 Aug 2025 03:48:51 +0000 (11:48 +0800)
gitdiff.py [new file with mode: 0644]

diff --git a/gitdiff.py b/gitdiff.py
new file mode 100644 (file)
index 0000000..48529ea
--- /dev/null
@@ -0,0 +1,212 @@
+import os
+import subprocess
+import datetime
+import sys
+from pathlib import Path
+
+def run_git_command(command):
+    try:
+        result = subprocess.run(
+            command,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            encoding='utf-8',
+            errors='replace',
+            check=True
+        )
+        return result.stdout.strip()
+    except subprocess.CalledProcessError as e:
+        print(f"Error executing git command: {e.stderr}")
+        sys.exit(1)
+    except UnicodeDecodeError as e:
+        print(f"Encoding error: {e}")
+        sys.exit(1)
+
+def main():
+    if len(sys.argv) > 3:
+        print("Usage: python git_changes_extractor.py [commit1 [commit2]]")
+        print("       If no arguments are provided, uncommitted changes will be extracted")
+        print("       If only commit1 is provided, it defaults to comparing with HEAD")
+        sys.exit(1)
+
+    # 处理不同参数情况
+    if len(sys.argv) == 1:
+        commit1 = None
+        commit2 = None
+        mode = "uncommitted"
+    elif len(sys.argv) == 2:
+        commit1 = sys.argv[1]
+        commit2 = "HEAD"
+        mode = "commits"
+    else:
+        commit1 = sys.argv[1]
+        commit2 = sys.argv[2]
+        mode = "commits"
+
+    # 获取当前仓库路径和名称
+    repo_path = Path(os.getcwd())
+    repo_name = repo_path.name
+
+    # 创建输出目录 (格式: 仓库名_时间戳)
+    timestamp = datetime.datetime.now().strftime("%y%m%d%H%M%S")
+    output_dir_name = f"{repo_name}_{timestamp}"
+    patches_dir = repo_path.parent / "PATCHES"
+    output_dir = patches_dir / output_dir_name
+    output_dir.mkdir(parents=True, exist_ok=True)
+    
+    # 创建diff子目录
+    diff_dir = output_dir / "diff"
+    diff_dir.mkdir(exist_ok=True)
+
+    # 获取变更文件列表
+    if mode == "uncommitted":
+        # 获取暂存区变更
+        staged_changes = run_git_command(
+            ["git", "diff", "--name-status", "--cached", "--diff-filter=ACDMRTUXB"]
+        ).splitlines()
+        
+        # 获取工作区变更
+        unstaged_changes = run_git_command(
+            ["git", "diff", "--name-status", "--diff-filter=ACDMRTUXB"]
+        ).splitlines()
+        
+        # 获取未跟踪文件 (新增但未git add的文件)
+        untracked_files = run_git_command(
+            ["git", "ls-files", "--others", "--exclude-standard"]
+        ).splitlines()
+        untracked_changes = [f"A\t{file}" for file in untracked_files]
+        
+        # 合并变更 (暂存区优先,然后工作区,最后未跟踪)
+        change_list = staged_changes.copy()
+        
+        # 添加工作区变更中不在暂存区的文件
+        staged_files = {line.split('\t', 1)[1] for line in staged_changes}
+        for line in unstaged_changes:
+            file_path = line.split('\t', 1)[1]
+            if file_path not in staged_files:
+                change_list.append(line)
+        
+        # 添加未跟踪文件
+        all_existing_files = {line.split('\t', 1)[1] for line in change_list}
+        for line in untracked_changes:
+            file_path = line.split('\t', 1)[1]
+            if file_path not in all_existing_files:
+                change_list.append(line)
+        
+        # 生成完整的未提交变更diff
+        full_diff = run_git_command(
+            ["git", "diff", "--patch", "--cached"]
+        )
+        with open(diff_dir / "uncommitted_changes.diff", "w", encoding='utf-8') as f:
+            f.write(full_diff)
+        
+        # 生成工作区与暂存区差异
+        unstaged_diff = run_git_command(
+            ["git", "diff", "--patch"]
+        )
+        with open(diff_dir / "unstaged_changes.diff", "w", encoding='utf-8') as f:
+            f.write(unstaged_diff)
+    else:
+        # 获取两个提交之间的变更
+        change_list = run_git_command(
+            ["git", "diff", "--name-status", commit1, commit2]
+        ).splitlines()
+
+        # 生成每个commit的详细diff
+        commit_hashes = run_git_command(
+            ["git", "rev-list", "--reverse", f"{commit1}..{commit2}"]
+        ).splitlines()
+        
+        if not commit_hashes:
+            commit_diff = run_git_command(
+                ["git", "diff", "--patch", commit1, commit2]
+            )
+            diff_file = diff_dir / f"{commit1[:8]}_{commit2[:8]}.diff"
+            with open(diff_file, "w", encoding='utf-8') as f:
+                f.write(commit_diff)
+        else:
+            all_commits = [commit1] + commit_hashes
+            for i in range(len(all_commits) - 1):
+                parent = all_commits[i]
+                child = all_commits[i + 1]
+                
+                commit_diff = run_git_command(
+                    ["git", "diff", "--patch", parent, child]
+                )
+                diff_file = diff_dir / f"{parent[:8]}_{child[:8]}.diff"
+                with open(diff_file, "w", encoding='utf-8') as f:
+                    f.write(commit_diff)
+
+    # 分类变更文件
+    added_files = []
+    modified_files = []
+    deleted_files = []
+
+    for line in change_list:
+        parts = line.split('\t')
+        if len(parts) < 2:
+            continue
+        status, file_path = parts[0], '\t'.join(parts[1:])
+        
+        if status == 'A':
+            added_files.append(file_path)
+        elif status == 'M':
+            modified_files.append(file_path)
+        elif status == 'D':
+            deleted_files.append(file_path)
+
+    # 写入变更列表文件
+    with open(output_dir / "gitchangelist.txt", "w", encoding='utf-8') as f:
+        f.write("Added Files:\n")
+        for file in added_files:
+            f.write(f"  {file}\n")
+        
+        f.write("\nModified Files:\n")
+        for file in modified_files:
+            f.write(f"  {file}\n")
+        
+        f.write("\nDeleted Files:\n")
+        for file in deleted_files:
+            f.write(f"  {file}\n")
+
+    # 写入git log(如果有提交范围)
+    if mode == "commits":
+        git_log = run_git_command(
+            ["git", "log", "--name-status", f"{commit1}..{commit2}"]
+        )
+        with open(output_dir / "gitlog.txt", "w", encoding='utf-8') as f:
+            f.write(git_log)
+    else:
+        # 未提交变更的简短信息
+        with open(output_dir / "gitlog.txt", "w", encoding='utf-8') as f:
+            f.write("Summary of uncommitted changes\n")
+            f.write("================================\n\n")
+            f.write("Changes are relative to HEAD:\n")
+            f.write(run_git_command(["git", "status", "--short"]))
+
+    # 复制变更文件(不包括已删除的)
+    for file_path in added_files + modified_files:
+        source_file = repo_path / file_path
+        target_file = output_dir / file_path
+        
+        target_file.parent.mkdir(parents=True, exist_ok=True)
+        
+        try:
+            # 直接从工作区复制,因为未跟踪文件在工作区中存在
+            if os.path.exists(source_file):
+                with open(source_file, "rb") as src, open(target_file, "wb") as dst:
+                    dst.write(src.read())
+            else:
+                # 从暂存区提取文件内容
+                blob_content = run_git_command(
+                    ["git", "show", f":{file_path}"]
+                )
+                with open(target_file, "w", encoding='utf-8') as dst:
+                    dst.write(blob_content)
+        except Exception as e:
+            print(f"Error copying file {file_path}: {e}")
+
+    print(f"Changes saved to: {output_dir}")
+
+if __name__ == "__main__":
+    main()    
\ No newline at end of file