- 新增Archive相关资源和预制体 - 新增SalesSystem销售系统基础 - 添加火山美术资源文件夹 Co-Authored-By: Claude Code <noreply@anthropic.com>
347 lines
9.9 KiB
Python
347 lines
9.9 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Split Commit Tool - 交互式拆分未提交变更为多个原子提交
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
# 配置
|
|
PROTECTED_BRANCHES = ['main', 'master', 'release/', 'hotfix/']
|
|
REPO_ROOT = Path(__file__).parent.absolute()
|
|
os.chdir(REPO_ROOT)
|
|
|
|
def run_command(cmd, capture_output=True):
|
|
"""执行Git命令"""
|
|
try:
|
|
result = subprocess.run(
|
|
cmd,
|
|
shell=True,
|
|
capture_output=capture_output,
|
|
text=True,
|
|
encoding='utf-8'
|
|
)
|
|
return result.returncode == 0, result.stdout, result.stderr
|
|
except Exception as e:
|
|
return False, "", str(e)
|
|
|
|
def is_protected_branch(branch_name):
|
|
"""检查是否为保护分支"""
|
|
for protected in PROTECTED_BRANCHES:
|
|
if protected.endswith('/') and branch_name.startswith(protected):
|
|
return True
|
|
if branch_name == protected:
|
|
return True
|
|
return False
|
|
|
|
def get_current_branch():
|
|
"""获取当前分支"""
|
|
success, stdout, stderr = run_command("git branch --show-current")
|
|
return stdout.strip() if success else ""
|
|
|
|
def analyze_changes():
|
|
"""分析变更"""
|
|
print("=" * 60)
|
|
print("步骤 1: 分析变更")
|
|
print("=" * 60)
|
|
|
|
# git status
|
|
print("\n[git status]")
|
|
success, stdout, stderr = run_command("git status")
|
|
print(stdout or stderr)
|
|
|
|
# git diff --stat
|
|
print("\n[git diff --stat]")
|
|
success, stdout, stderr = run_command("git diff --stat")
|
|
print(stdout or stderr)
|
|
|
|
return True
|
|
|
|
def create_commit(msg):
|
|
"""使用msg.txt创建提交(避免中文乱码)"""
|
|
msg_file = REPO_ROOT / "msg.txt"
|
|
try:
|
|
with open(msg_file, 'w', encoding='utf-8') as f:
|
|
f.write(msg)
|
|
f.write("\n\nCo-Authored-By: Claude Code <noreply@anthropic.com>\n")
|
|
|
|
success, stdout, stderr = run_command("git commit -F msg.txt")
|
|
if not success:
|
|
print(f"❌ 提交失败: {stderr}")
|
|
return False
|
|
|
|
print(f"✅ 提交成功: {msg}")
|
|
return True
|
|
finally:
|
|
if msg_file.exists():
|
|
msg_file.unlink()
|
|
|
|
def commit_all_changes():
|
|
"""一次性提交所有变更"""
|
|
print("\n" + "=" * 60)
|
|
print("一次性提交所有变更")
|
|
print("=" * 60)
|
|
|
|
# 处理删除的文件
|
|
run_command("git ls-files --deleted -z | xargs -0 git rm", capture_output=False)
|
|
|
|
# 添加所有变更
|
|
run_command("git add -A", capture_output=False)
|
|
|
|
# 创建提交
|
|
create_commit("chore: 批量提交变更")
|
|
|
|
# 显示提交历史
|
|
print("\n[提交历史]")
|
|
run_command("git log --oneline -5", capture_output=False)
|
|
|
|
def interactive_split():
|
|
"""交互式拆分提交"""
|
|
print("\n" + "=" * 60)
|
|
print("步骤 2: 分组变更")
|
|
print("=" * 60)
|
|
|
|
# 获取所有变更
|
|
success, status_output, _ = run_command("git status --short")
|
|
if not success:
|
|
print("❌ 无法获取变更状态")
|
|
return False
|
|
|
|
# 分析变更类型
|
|
staged_files = []
|
|
modified_files = []
|
|
deleted_files = []
|
|
untracked_files = []
|
|
|
|
for line in status_output.strip().split('\n'):
|
|
if not line:
|
|
continue
|
|
|
|
status = line[:2]
|
|
file_path = line[3:]
|
|
|
|
if status == 'M ':
|
|
staged_files.append(file_path)
|
|
elif status == ' M':
|
|
modified_files.append(file_path)
|
|
elif status == 'D ':
|
|
deleted_files.append(file_path)
|
|
elif status == ' D':
|
|
deleted_files.append(file_path)
|
|
elif status == '??':
|
|
untracked_files.append(file_path)
|
|
elif status == 'MM':
|
|
staged_files.append(file_path)
|
|
modified_files.append(file_path)
|
|
|
|
# 显示分组建议
|
|
groups = []
|
|
print("\n[变更分组方案]")
|
|
|
|
order = 1
|
|
|
|
# 删除文件
|
|
if deleted_files:
|
|
groups.append({
|
|
'order': order,
|
|
'files': deleted_files,
|
|
'message': '删除旧文件',
|
|
'type': 'delete'
|
|
})
|
|
print(f"{order}. 删除 {len(deleted_files)} 个文件")
|
|
for f in deleted_files[:3]:
|
|
print(f" - {f}")
|
|
if len(deleted_files) > 3:
|
|
print(f" ... 还有 {len(deleted_files) - 3} 个文件")
|
|
order += 1
|
|
|
|
# 修改的meta文件
|
|
meta_files = [f for f in modified_files if f.endswith('.meta')]
|
|
if meta_files:
|
|
groups.append({
|
|
'order': order,
|
|
'files': meta_files,
|
|
'message': '更新.meta文件',
|
|
'type': 'meta'
|
|
})
|
|
print(f"{order}. 修改 {len(meta_files)} 个.meta文件")
|
|
for f in meta_files[:3]:
|
|
print(f" - {f}")
|
|
if len(meta_files) > 3:
|
|
print(f" ... 还有 {len(meta_files) - 3} 个文件")
|
|
order += 1
|
|
|
|
# 场景文件
|
|
scene_files = [f for f in modified_files if f.endswith('.unity')]
|
|
if scene_files:
|
|
groups.append({
|
|
'order': order,
|
|
'files': scene_files,
|
|
'message': '更新场景',
|
|
'type': 'scene'
|
|
})
|
|
print(f"{order}. 修改 {len(scene_files)} 个场景文件")
|
|
for f in scene_files:
|
|
print(f" - {f}")
|
|
order += 1
|
|
|
|
# 脚本文件
|
|
script_files = [f for f in modified_files if f.endswith('.cs')]
|
|
if script_files:
|
|
for script_file in script_files:
|
|
groups.append({
|
|
'order': order,
|
|
'files': [script_file],
|
|
'message': f'更新脚本: {Path(script_file).name}',
|
|
'type': 'script'
|
|
})
|
|
print(f"{order}. 修改脚本: {script_file}")
|
|
order += 1
|
|
|
|
# 未跟踪文件
|
|
if untracked_files:
|
|
groups.append({
|
|
'order': order,
|
|
'files': untracked_files,
|
|
'message': '添加新文件',
|
|
'type': 'untracked'
|
|
})
|
|
print(f"{order}. 添加 {len(untracked_files)} 个新文件")
|
|
for f in untracked_files[:3]:
|
|
print(f" - {f}")
|
|
if len(untracked_files) > 3:
|
|
print(f" ... 还有 {len(untracked_files) - 3} 个文件")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("步骤 3: 确认拆分方案")
|
|
print("=" * 60)
|
|
|
|
choice = input("\n是否开始拆分? [1] 开始拆分 [2] 取消拆分 [3] 自定义分组: ")
|
|
|
|
if choice == '2':
|
|
print("\n已取消拆分")
|
|
return False
|
|
|
|
if choice == '3':
|
|
print("\n自定义分组功能暂未实现,请使用自动分组")
|
|
return False
|
|
|
|
# 开始拆分
|
|
print("\n" + "=" * 60)
|
|
print("步骤 4: 执行拆分")
|
|
print("=" * 60)
|
|
|
|
for group in groups:
|
|
print(f"\n>>> 处理第 {group['order']} 组")
|
|
|
|
# 根据类型执行不同的git操作
|
|
if group['type'] == 'delete':
|
|
# 删除文件
|
|
for file_path in group['files']:
|
|
run_command(f'git rm "{file_path}"', capture_output=False)
|
|
elif group['type'] == 'untracked':
|
|
# 添加新文件
|
|
run_command('git add -A', capture_output=False)
|
|
else:
|
|
# 修改文件
|
|
for file_path in group['files']:
|
|
run_command(f'git add "{file_path}"', capture_output=False)
|
|
|
|
# 创建提交
|
|
success = create_commit(group['message'])
|
|
if not success:
|
|
print(f"❌ 第 {group['order']} 组提交失败,拆分中止")
|
|
return False
|
|
|
|
input("\n按回车继续下一组...")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("步骤 5: 完成")
|
|
print("=" * 60)
|
|
|
|
print("\n[提交历史]")
|
|
success, stdout, _ = run_command("git log --oneline -" + str(len(groups)))
|
|
print(stdout)
|
|
|
|
print(f"\n✅ 拆分完成!共创建 {len(groups)} 个原子提交")
|
|
return True
|
|
|
|
def main():
|
|
"""主函数"""
|
|
print("=" * 60)
|
|
print("Split Commit - 交互式拆分提交工具")
|
|
print("=" * 60)
|
|
|
|
# 检查保护分支
|
|
current_branch = get_current_branch()
|
|
print(f"\n当前分支: {current_branch}")
|
|
|
|
if is_protected_branch(current_branch):
|
|
print("\n❌ 当前为保护分支,无法直接拆分")
|
|
print("\n选项:")
|
|
print("[1] 创建新分支再拆分 (推荐)")
|
|
print("[2] 压成单commit并移到新分支")
|
|
print("[3] 取消")
|
|
|
|
choice = input("\n请选择: ")
|
|
|
|
if choice == '1':
|
|
branch_name = input("请输入新分支名: ")
|
|
if not branch_name:
|
|
print("❌ 分支名不能为空")
|
|
return
|
|
|
|
print("\n执行:")
|
|
print("1. git stash push --include-untracked")
|
|
print(f"2. git checkout -b {branch_name}")
|
|
print("3. git stash pop")
|
|
|
|
confirm = input("\n确认执行?(y/n): ")
|
|
if confirm.lower() == 'y':
|
|
run_command("git stash push --include-untracked", capture_output=False)
|
|
run_command(f"git checkout -b {branch_name}", capture_output=False)
|
|
run_command("git stash pop", capture_output=False)
|
|
print(f"\n✅ 已切换到新分支: {branch_name}")
|
|
print("请重新运行此工具进行拆分")
|
|
|
|
elif choice == '2':
|
|
print("\n功能暂未实现")
|
|
|
|
return
|
|
|
|
# 非保护分支,继续拆分流程
|
|
print("\n✅ 非保护分支,可以进行拆分")
|
|
|
|
# 分析变更
|
|
if not analyze_changes():
|
|
return
|
|
|
|
# 选择模式
|
|
print("\n" + "=" * 60)
|
|
print("选择拆分模式:")
|
|
print("=" * 60)
|
|
print("[1] 交互式拆分 (推荐)")
|
|
print("[2] 一次性提交所有变更")
|
|
|
|
mode = input("\n请选择: ")
|
|
|
|
if mode == '1':
|
|
interactive_split()
|
|
elif mode == '2':
|
|
commit_all_changes()
|
|
else:
|
|
print("❌ 无效选择")
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print("\n\n已取消操作")
|
|
sys.exit(0)
|
|
except Exception as e:
|
|
print(f"\n❌ 发生错误: {e}")
|
|
sys.exit(1)
|