Skip to content

Commit e16bc2e

Browse files
committed
refactor:将耗时的业务逻辑调整为异步线程执行,解决主界面卡顿的问题
1 parent a1ac020 commit e16bc2e

File tree

6 files changed

+989
-433
lines changed

6 files changed

+989
-433
lines changed

src/callback/callback_builder.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,15 @@ def build_single_commit_callback(
4646
author_name: str,
4747
author_email: str,
4848
commit_message: str,
49-
date_str: str
49+
commit_time: datetime
5050
):
5151
"""
5252
生成 callback 脚本(用于 git-filter-repo),只修改一个指定提交
5353
"""
5454
try:
55-
dt = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S")
5655
safe_msg = commit_message.replace('"""', r'\"\"\"')
5756

58-
new_dt = datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
57+
new_dt = datetime(commit_time.year, commit_time.month, commit_time.day, commit_time.hour, commit_time.minute, commit_time.second,
5958
tzinfo=timezone(timedelta(hours=8)))
6059
timestamp = int(new_dt.timestamp())
6160

src/command/command.py

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
import os
2+
import subprocess
3+
import traceback
4+
from abc import ABC, abstractmethod
5+
from datetime import datetime
6+
from typing import Tuple
7+
8+
from PyQt5.QtCore import pyqtSignal, QThreadPool, QObject, QRunnable
9+
10+
from src.callback import CallbackScriptBuilder
11+
12+
13+
class Command(ABC):
14+
def __init__(self, repo_path: str):
15+
super().__init__()
16+
self.repo_path = repo_path
17+
18+
@abstractmethod
19+
def execute(self, ):
20+
"""执行命令"""
21+
pass
22+
23+
24+
class WorkerSignals(QObject):
25+
finished = pyqtSignal(bool, str) # success, output
26+
27+
28+
class CommandWorker(QRunnable):
29+
def __init__(self, command, callback=None):
30+
super().__init__()
31+
self.command = command
32+
self.callback = callback
33+
self.signals = WorkerSignals()
34+
35+
if callback:
36+
self.signals.finished.connect(callback)
37+
38+
def run(self):
39+
"""在工作线程中执行"""
40+
try:
41+
print(f"执行命令: {self.command.__class__.__name__}")
42+
success, output = self.command.execute()
43+
self.signals.finished.emit(success, output)
44+
except Exception as e:
45+
error_msg = f"命令执行异常: {str(e)}\n{traceback.format_exc()}"
46+
print(error_msg)
47+
self.signals.finished.emit(False, error_msg)
48+
49+
50+
# 获取指定分支的提交信息
51+
class GetBranchCommitsCommand(Command):
52+
def __init__(self, repo_path: str, branch_name: str):
53+
"""
54+
:param repo_path:仓库路径
55+
:param branch_name:分支名称
56+
"""
57+
super().__init__(repo_path)
58+
self.branch_name = branch_name
59+
60+
def execute(self):
61+
try:
62+
print("获取指定分支的提交信息")
63+
result = subprocess.run([
64+
"git",
65+
"log",
66+
"--pretty=format:%h %an <%ae> %ad %s %d",
67+
"--date=iso",
68+
self.branch_name
69+
], cwd=self.repo_path, encoding='utf-8', errors='replace', capture_output=True, text=True)
70+
if result.returncode == 0:
71+
return True, result.stdout
72+
else:
73+
return False, result.stderr
74+
except Exception as e:
75+
return False, str(e)
76+
77+
78+
# 编辑单条提交信息
79+
class EditSingleCommitCommand(Command):
80+
def __init__(self, repo_path: str, commit_id: str, new_author: str, new_email: str,
81+
new_commit_message: str, new_commit_time: datetime):
82+
"""
83+
:param repo_path:仓库路径
84+
:param commit_id:提交ID
85+
:param new_author:新的作者
86+
:param new_email:新的邮箱
87+
:param new_commit_message:新的提交信息
88+
:param new_commit_time:新的提交时间
89+
"""
90+
super().__init__(repo_path)
91+
self.commit_id = commit_id
92+
self.new_commit_message = new_commit_message
93+
self.new_commit_time = new_commit_time
94+
self.new_author = new_author
95+
self.new_email = new_email
96+
97+
def execute(self):
98+
# 创建文件
99+
file_name = "edit_commit_callback.py"
100+
target_file_path = os.path.join(self.repo_path, file_name)
101+
try:
102+
ok = CallbackScriptBuilder.build_single_commit_callback(
103+
filepath=target_file_path,
104+
target_hash=self.commit_id,
105+
author_name=self.new_author,
106+
author_email=self.new_email,
107+
commit_message=self.new_commit_message,
108+
commit_time=self.new_commit_time # 格式:2024-01-01T10:00:00
109+
)
110+
if not ok:
111+
return False, "生成文件失败"
112+
113+
result = subprocess.run([
114+
"git-filter-repo",
115+
"--commit-callback", file_name
116+
, "--force"
117+
], cwd=self.repo_path, encoding='utf-8',
118+
errors='replace', capture_output=True, text=True)
119+
120+
if result.returncode == 0:
121+
return True, "提交信息修改成功"
122+
else:
123+
return False, result.stderr
124+
except Exception as e:
125+
return False, str(e)
126+
finally:
127+
if os.path.exists(target_file_path):
128+
os.remove(target_file_path)
129+
130+
131+
# 编辑单条提交信息
132+
class EditBulkCommitCommand(Command):
133+
def __init__(self, repo_path: str, commit_changes: dict):
134+
"""
135+
:param repo_path:仓库路径
136+
:param commit_changes:提交信息
137+
"""
138+
super().__init__(repo_path)
139+
self.commit_changes = commit_changes
140+
141+
def execute(self):
142+
# 创建文件
143+
file_name = "rewrite_callback.py"
144+
target_file_path = os.path.join(self.repo_path, file_name)
145+
try:
146+
ok = CallbackScriptBuilder.build_bulk_commit_callback(target_file_path, self.commit_changes)
147+
if not ok:
148+
return False, "生成文件失败"
149+
result = subprocess.run([
150+
"git-filter-repo",
151+
"--commit-callback", file_name
152+
, "--force"
153+
], cwd=self.repo_path, encoding='utf-8',
154+
errors='replace', capture_output=True, text=True)
155+
156+
if result.returncode == 0:
157+
return True, "批量修改作者、邮箱及时间信息成功"
158+
else:
159+
return False, result.stderr
160+
except Exception as e:
161+
return False, str(e)
162+
finally:
163+
if os.path.exists(target_file_path):
164+
os.remove(target_file_path)
165+
166+
167+
# 切换分支
168+
class CheckoutCommand(Command):
169+
def __init__(self, repo_path: str, branch_name: str):
170+
"""
171+
:param repo_path:仓库路径
172+
:param branch_name:分支名称
173+
"""
174+
super().__init__(repo_path)
175+
self.branch_name = branch_name
176+
177+
def execute(self):
178+
try:
179+
result = subprocess.run([
180+
"git",
181+
"checkout",
182+
self.branch_name
183+
], cwd=self.repo_path, encoding='utf-8',
184+
errors='replace', capture_output=True, text=True)
185+
186+
if result.returncode == 0:
187+
return True, "切换分支成功"
188+
else:
189+
return False, result.stderr
190+
except Exception as e:
191+
return False, str(e)
192+
193+
194+
# 获取所有分支
195+
class GetAllBranchesCommand(Command):
196+
def __init__(self, repo_path: str):
197+
"""
198+
:param repo_path:仓库路径
199+
"""
200+
super().__init__(repo_path)
201+
202+
def execute(self):
203+
try:
204+
result = subprocess.run([
205+
"git",
206+
"branch"
207+
, "-a"
208+
], cwd=self.repo_path, encoding='utf-8',
209+
errors='replace', capture_output=True, text=True)
210+
211+
if result.returncode == 0:
212+
branches = [line.strip() for line in result.stdout.split("\n") if line.strip() != '']
213+
return True, branches
214+
else:
215+
return False, result.stderr
216+
except Exception as e:
217+
return False, str(e)
218+
219+
220+
# 获取远程仓库地址
221+
class GetRemoteRepoUrlCommand(Command):
222+
def __init__(self, repo_path: str):
223+
"""
224+
:param repo_path:仓库路径
225+
"""
226+
super().__init__(repo_path)
227+
228+
def execute(self):
229+
try:
230+
result = subprocess.run([
231+
"git",
232+
"remote",
233+
"get-url",
234+
"origin"
235+
], cwd=self.repo_path, encoding='utf-8',
236+
errors='replace', capture_output=True, text=True)
237+
238+
if result.returncode == 0:
239+
return True, result.stdout.strip()
240+
else:
241+
return False, result.stderr
242+
except Exception as e:
243+
return False, str(e)
244+
245+
246+
# 设置远程仓库
247+
class SetRemoteUrlCommand(Command):
248+
def __init__(self, repo_path: str, remote_url: str):
249+
super().__init__(repo_path)
250+
self.remote_url = remote_url
251+
252+
def execute(self) -> Tuple[bool, str]:
253+
try:
254+
result = subprocess.run(["git", "remote", "add", "origin", self.remote_url], cwd=self.repo_path,
255+
capture_output=True, text=True)
256+
if result.returncode == 0:
257+
return True, result.stdout.strip()
258+
else:
259+
return False, result.stderr
260+
except Exception as e:
261+
return False, str(e)
262+
263+
264+
class Executor:
265+
@staticmethod
266+
def executeAsync(command: Command, callback=None):
267+
try:
268+
worker = CommandWorker(command, callback)
269+
# 设置自动删除
270+
worker.setAutoDelete(True)
271+
# 使用全局线程池
272+
QThreadPool.globalInstance().start(worker)
273+
except Exception as e:
274+
print(f"提交异步任务失败: {e}")
275+
if callback:
276+
callback(False, str(e))
277+
278+
@staticmethod
279+
def execute(command: Command):
280+
return command.execute()

src/config.json

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

src/git_utils/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from .git_filter_repo import (
2+
Blob, Commit, Tag, RepoFilter, FilteringOptions,
3+
string_to_date, date_to_string,RepoAnalyze
4+
)
5+
6+
__all__ = [
7+
"Blob", "Commit", "Tag", "RepoFilter", "FilteringOptions",
8+
"string_to_date", "date_to_string", "RepoAnalyze"
9+
]

0 commit comments

Comments
 (0)