Skip to content

Commit 29dfff8

Browse files
committed
feat: 将原来使用git rebase的方式修改成git-filter-repo
1 parent 2ecfb59 commit 29dfff8

File tree

7 files changed

+146
-103
lines changed

7 files changed

+146
-103
lines changed

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@
1010
1111

1212

13-
### 安装的前置条件
13+
### 前置条件
1414

1515
- 本地需要安装python 并配置了环境变量
1616

1717
> 不低于python3.8
1818
1919
- 本地安装了Git
2020

21+
- 安装git-filter-repo
22+
23+
> [git-filter-repo](https://github.com/newren/git-filter-repo)
24+
25+
26+
2127
### 打包
2228

2329
- 安装依赖包
@@ -40,10 +46,10 @@
4046
```json
4147
{
4248
"authors": [
43-
"chenlei04(陈磊) <chenlei04@genscigroup.com>",
44-
"wangwenjie(王文杰) <wangwenjie@genscigroup.com>",
45-
"wushiguang(吴世光) <wushiguang@genscigroup.com>",
46-
"zhangwei(张伟) <zhangwei@genscigroup.com>"
49+
"zhangsan <zhangsan@XXx.com>",
50+
"lisi <lisi@XXx.com>",
51+
"wangwu <wangwu@XXx.com>",
52+
"zhaoliu <zhaoliu@XXx.com>"
4753
]
4854
}
4955
```

callback_builder.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import os
2+
from datetime import datetime, timezone, timedelta
3+
4+
5+
class CallbackScriptBuilder:
6+
@staticmethod
7+
def build_bulk_commit_callback(filepath: str, commit_changes: dict) -> bool:
8+
"""
9+
生成 callback 脚本,针对多次提交,每个提交设置独立的 author/email/date/message
10+
"""
11+
try:
12+
import json
13+
changes_json = json.dumps(commit_changes, indent=2, ensure_ascii=False)
14+
15+
content = f'''
16+
from datetime import datetime, timezone, timedelta
17+
18+
commit_changes = {changes_json}
19+
commit_id = commit.original_id.decode()[:7]
20+
if commit_id in commit_changes:
21+
change = commit_changes[commit_id]
22+
commit.author_name = change["name"].encode("utf-8")
23+
commit.author_email = change["email"].encode("utf-8")
24+
commit.committer_name = change["name"].encode("utf-8")
25+
commit.committer_email = change["email"].encode("utf-8")
26+
27+
dt = datetime.strptime(change["date"], "%Y-%m-%dT%H:%M:%S")
28+
dt = dt.replace(tzinfo=timezone(timedelta(hours=8)))
29+
timestamp = int(dt.timestamp())
30+
commit.author_date = f"{{timestamp}} +0800".encode("utf-8")
31+
commit.committer_date = f"{{timestamp}} +0800".encode("utf-8")
32+
commit.message = change["message"].encode("utf-8")
33+
'''
34+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
35+
with open(filepath, "w", encoding="utf-8", newline="\n") as f:
36+
f.write(content.lstrip())
37+
return True
38+
except Exception as e:
39+
print(f"[CallbackScriptBuilder] 错误: {e}")
40+
return False
41+
42+
@staticmethod
43+
def build_single_commit_callback(
44+
filepath: str,
45+
target_hash: str,
46+
author_name: str,
47+
author_email: str,
48+
commit_message: str,
49+
date_str: str
50+
):
51+
"""
52+
生成 callback 脚本(用于 git-filter-repo),只修改一个指定提交
53+
"""
54+
try:
55+
dt = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S")
56+
safe_msg = commit_message.replace('"""', r'\"\"\"')
57+
58+
new_dt = datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
59+
tzinfo=timezone(timedelta(hours=8)))
60+
timestamp = int(new_dt.timestamp())
61+
62+
def encode_line(key: str, value: str) -> str:
63+
return f'commit.{key} = "{value}".encode("utf-8")'
64+
65+
content = f'''
66+
if commit.original_id.decode()[:7] == "{target_hash}":
67+
{encode_line("author_name", author_name)}
68+
{encode_line("author_email", author_email)}
69+
{encode_line("committer_name", author_name)}
70+
{encode_line("committer_email", author_email)}
71+
commit.message = "{safe_msg}".encode("utf-8")
72+
commit.author_date = f"{timestamp} +0800".encode("utf-8") # 格式化为字节
73+
commit.committer_date = f"{timestamp} +0800".encode("utf-8") # 格式化为字节
74+
'''
75+
76+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
77+
with open(filepath, "w", encoding="utf-8", newline="\n") as f:
78+
f.write(content)
79+
return True
80+
except Exception as e:
81+
print(f"CallbackScriptBuilder 错误: {e}")
82+
return False

config.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
{
2-
"authors": [
3-
"chenlei04(\u9648\u78ca) <chenlei04@genscigroup.com>",
4-
"wangwenjie(\u738b\u6587\u6770) <wangwenjie@genscigroup.com>",
5-
"wushiguang(\u5434\u4e16\u5149) <wushiguang@genscigroup.com>",
6-
"zhangwei(\u5f20\u4f1f) <zhangwei@genscigroup.com>"
7-
]
8-
}
1+
{"authors": ["chenlei04(\u9648\u78ca) <chenlei04@genscigroup.com>", "wangwenjie(\u738b\u6587\u6770) <wangwenjie@genscigroup.com>", "wushiguang(\u5434\u4e16\u5149) <wushiguang@genscigroup.com>", "zhangwei(\u5f20\u4f1f) <zhangwei@genscigroup.com>"], "last_path": "C:/envirment/code/adhd/test/adhd-jfinal-project"}

dist/config.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

dist/main.exe

-34.2 MB
Binary file not shown.

main.py

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import random
55
import subprocess
66
import sys
7-
import tempfile
87
from datetime import timedelta, datetime
98

109
from PyQt5.QtCore import QDateTime
@@ -14,6 +13,8 @@
1413
QFormLayout, QDateTimeEdit, QDialogButtonBox, QListWidgetItem, QTextEdit
1514
)
1615

16+
from callback_builder import CallbackScriptBuilder
17+
1718
CONFIG_PATH = "config.json"
1819

1920

@@ -341,32 +342,45 @@ def rewrite_commits_randomly(self):
341342
new_commits = [commit[:7] for commit in commits]
342343
if base_commit != "" and base_commit in new_commits:
343344
index = new_commits.index(base_commit)
344-
index = index+1 if index<(len(commits)-1) else -1
345+
index = index + 1 if index < (len(commits) - 1) else -1
345346
commits = commits[:index]
346347

347348
total = len(commits)
348349
seconds_range = int((end - start).total_seconds())
349350
time_steps = sorted([random.randint(0, seconds_range) for _ in range(total)], reverse=True)
350-
351+
commit_changes = {}
351352
# 忽略第一次提交,因为无法修改
352353
for i, commit in enumerate(commits):
353354
rand_time = start + timedelta(seconds=time_steps[i])
354355
formatted_date = rand_time.strftime("%Y-%m-%dT%H:%M:%S")
356+
355357
rand_author = random.choice(authors)
356358
name = rand_author.split("<")[0].strip()
357359
email = rand_author.split("<")[1].strip(" >")
358360
_, _, message = self.get_commit_info(commit)
359-
script_path = os.path.join(tempfile.gettempdir(), f"editor_{commit}.py")
360-
generate_rebase_editor_script(script_path)
361-
env = build_env(name, email, formatted_date, script_path)
362-
363-
rebase_interactive(repo, commit, env, self.root_commit_log in commit)
364-
amend_commit(repo, env, message)
365-
rebase_continue(repo, env)
366-
delete_temp_file(script_path)
367361

368-
self.load_commits()
369-
QMessageBox.information(self, "完成", "批量重写完成")
362+
commit_changes[commit[:7]] = {
363+
"name": name,
364+
"email": email,
365+
"date": formatted_date,
366+
"message": message,
367+
}
368+
callback_path = os.path.join(self.repo_path.text(), "rewrite_callback.py")
369+
CallbackScriptBuilder.build_bulk_commit_callback(callback_path, commit_changes)
370+
try:
371+
result = subprocess.run([
372+
"git-filter-repo",
373+
"--commit-callback", callback_path
374+
, "--force"
375+
], cwd=self.repo_path.text(), capture_output=True, text=True)
376+
377+
if result.returncode == 0:
378+
self.load_commits()
379+
QMessageBox.information(self, "成功", "提交修改完成(使用 filter-repo)")
380+
else:
381+
QMessageBox.critical(self, "失败", result.stderr)
382+
finally:
383+
os.remove(callback_path)
370384

371385
def push_force(self):
372386
repo = self.repo_path.text()
@@ -396,42 +410,36 @@ def edit_commit(self, item):
396410
return
397411

398412
repo_path = self.repo_path.text()
413+
file_name = "edit_commit_callback.py"
414+
script_path = os.path.join(repo_path, file_name)
399415
try:
400416
author_name = new_author.split("<")[0].strip()
401417
author_email = new_author.split("<")[1].strip(" >")
402-
403-
full_log = self.run_git_command(
404-
["git", "rev-list", self.branch_selector.currentText()],
405-
cwd=repo_path
406-
).splitlines()
407-
full_log = [v[:7] for v in full_log]
408-
if selected_commit not in full_log:
409-
QMessageBox.critical(self, "错误", "未能在分支中找到该提交")
418+
ok = CallbackScriptBuilder.build_single_commit_callback(
419+
filepath=script_path,
420+
target_hash=selected_commit,
421+
author_name=author_name,
422+
author_email=author_email,
423+
commit_message=new_msg,
424+
date_str=new_date # 格式:2024-01-01T10:00:00
425+
)
426+
427+
if not ok:
428+
QMessageBox.critical(self, "失败", "生成 callback 脚本失败")
410429
return
411-
412-
index = full_log.index(selected_commit)
413-
# if index == len(full_log) - 1:
414-
# QMessageBox.warning(self, "提示", "无法 rebase 第一个提交")
415-
# return
416-
417-
rebase_start = full_log[index]
418-
419-
editor_script = os.path.join(tempfile.gettempdir(), "rebase_editor.py")
420-
generate_rebase_editor_script(editor_script)
421-
env = build_env(author_name, author_email, new_date, editor_script)
422-
423-
# 根提交记录的修改
424-
rebase_interactive(repo_path, rebase_start, env, self.root_commit_log in rebase_start)
425-
426-
amend_commit(repo_path, env, new_msg)
427-
428-
rebase_continue(repo_path, env)
429-
430-
os.remove(editor_script)
431-
self.load_commits()
432-
QMessageBox.information(self, "成功", "提交修改成功!")
433-
430+
result = subprocess.run([
431+
"git-filter-repo",
432+
"--commit-callback", script_path
433+
, "--force"
434+
], cwd=repo_path, capture_output=True, text=True)
435+
436+
if result.returncode == 0:
437+
self.load_commits()
438+
QMessageBox.information(self, "成功", "提交修改完成(使用 filter-repo)")
439+
else:
440+
QMessageBox.critical(self, "失败", result.stderr)
434441
except Exception as e:
442+
os.remove(script_path)
435443
QMessageBox.critical(self, "错误", str(e))
436444

437445
def get_commit_info(self, selected_commit):

main.spec

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)