-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathgit_manager.py
More file actions
347 lines (287 loc) · 10.9 KB
/
git_manager.py
File metadata and controls
347 lines (287 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Git管理模块
负责检查Git状态、获取commit信息等
"""
import subprocess
import os
from lib_logger import logger
import sys
from datetime import datetime
from typing import Tuple, Optional, List
class GitManager:
"""Git操作管理类"""
@staticmethod
def _get_subprocess_kwargs():
"""获取subprocess调用的通用参数,用于隐藏命令行窗口"""
kwargs = {
'capture_output': True,
'text': True,
'encoding': 'utf-8',
'errors': 'replace'
}
# 在Windows上隐藏命令行窗口
if sys.platform == 'win32' and hasattr(subprocess, 'CREATE_NO_WINDOW'):
kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
return kwargs
def __init__(self, repo_path: str = "."):
"""
初始化Git管理器
Args:
repo_path: Git仓库路径,默认为当前目录
"""
self.repo_path = os.path.abspath(repo_path)
def is_git_repo(self) -> bool:
"""检查是否为Git仓库"""
try:
git_dir = os.path.join(self.repo_path, '.git')
return os.path.exists(git_dir) or os.path.exists(git_dir + '.git')
except Exception as e:
logger.error(f"检查Git仓库失败: {e}")
return False
def has_uncommitted_changes(self) -> bool:
"""
检查是否有未提交的更改
Returns:
bool: True表示有未提交的更改,False表示工作区干净
"""
try:
# 检查工作区状态
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 30
result = subprocess.run(
['git', 'status', '--porcelain'],
**kwargs
)
if result.returncode != 0:
logger.error(f"Git status命令失败: {result.stderr}")
return True # 出错时假设有未提交更改
# 如果有输出,说明有未提交的更改
return len(result.stdout.strip()) > 0
except subprocess.TimeoutExpired:
logger.error("Git status命令超时")
return True
except Exception as e:
logger.error(f"检查未提交更改失败: {e}")
return True
def get_current_commit_id(self) -> Optional[str]:
"""
获取当前HEAD的commit ID
Returns:
str: 完整的commit hash,失败时返回None
"""
try:
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 30
result = subprocess.run(
['git', 'rev-parse', 'HEAD'],
**kwargs
)
if result.returncode == 0 and result.stdout:
commit_id = result.stdout.strip()
logger.info(f"当前commit ID: {commit_id}")
return commit_id
else:
logger.error(f"获取commit ID失败: {result.stderr}")
return None
except subprocess.TimeoutExpired:
logger.error("获取commit ID超时")
return None
except Exception as e:
logger.error(f"获取commit ID异常: {e}")
return None
def get_short_commit_id(self, length: int = 8) -> Optional[str]:
"""
获取短commit ID
Args:
length: commit ID长度,默认8位
Returns:
str: 短commit hash,失败时返回None
"""
full_commit_id = self.get_current_commit_id()
if full_commit_id:
return full_commit_id[:length]
return None
def get_commit_info(self) -> dict:
"""
获取详细的commit信息
Returns:
dict: 包含commit信息的字典
"""
info = {
'commit_id': None,
'short_commit_id': None,
'author': None,
'date': None,
'message': None,
'branch': None
}
try:
# 获取commit ID
info['commit_id'] = self.get_current_commit_id()
info['short_commit_id'] = self.get_short_commit_id()
# 获取作者信息
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 30
result = subprocess.run(
['git', 'log', '-1', '--pretty=format:%an'],
**kwargs
)
if result.returncode == 0 and result.stdout:
info['author'] = result.stdout.strip()
# 获取提交日期
result = subprocess.run(
['git', 'log', '-1', '--pretty=format:%ad', '--date=iso'],
**kwargs
)
if result.returncode == 0 and result.stdout:
info['date'] = result.stdout.strip()
# 获取提交信息
result = subprocess.run(
['git', 'log', '-1', '--pretty=format:%s'],
**kwargs
)
if result.returncode == 0 and result.stdout:
info['message'] = result.stdout.strip()
# 获取当前分支
result = subprocess.run(
['git', 'branch', '--show-current'],
**kwargs
)
if result.returncode == 0 and result.stdout:
branch = result.stdout.strip()
info['branch'] = branch
logger.info(f"Git分支检测成功: {branch}")
else:
logger.warning(f"Git分支检测失败: returncode={result.returncode}, stderr={result.stderr}")
# 尝试备用方法
result2 = subprocess.run(
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
**kwargs
)
if result2.returncode == 0 and result2.stdout:
branch = result2.stdout.strip()
info['branch'] = branch
logger.info(f"使用备用方法检测到Git分支: {branch}")
else:
logger.error(f"备用Git分支检测也失败: returncode={result2.returncode}, stderr={result2.stderr}")
except Exception as e:
logger.error(f"获取commit信息失败: {e}")
return info
def commit_changes(self, message: str = None) -> bool:
"""
提交当前更改
Args:
message: 提交信息,如果为None则使用默认信息
Returns:
bool: 提交是否成功
"""
try:
if message is None:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
message = f"Auto build: {timestamp}"
# 添加所有更改
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 30
kwargs['check'] = True
subprocess.run(
['git', 'add', '.'],
**kwargs
)
# 提交更改
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 30
result = subprocess.run(
['git', 'commit', '-m', message],
**kwargs
)
if result.returncode == 0:
logger.info(f"提交成功: {message}")
return True
else:
logger.error(f"提交失败: {result.stderr}")
return False
except subprocess.TimeoutExpired:
logger.error("Git提交超时")
return False
except Exception as e:
logger.error(f"Git提交异常: {e}")
return False
def get_recent_commits(self, count: int = 5) -> List[dict]:
"""
获取最近的提交记录
Args:
count: 获取的提交数量
Returns:
List[dict]: 提交记录列表
"""
commits = []
try:
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 30
result = subprocess.run(
['git', 'log', f'-{count}', '--pretty=format:%H|%an|%ad|%s', '--date=iso'],
**kwargs
)
if result.returncode == 0:
for line in result.stdout.strip().split('\n'):
if line:
parts = line.split('|', 3)
if len(parts) >= 4:
commits.append({
'commit_id': parts[0],
'author': parts[1],
'date': parts[2],
'message': parts[3]
})
except Exception as e:
logger.error(f"获取提交记录失败: {e}")
return commits
def push_changes(self) -> bool:
"""
推送当前分支到远程仓库(依赖用户已设置好默认上游分支)
Returns:
bool: 推送是否成功
"""
try:
kwargs = self._get_subprocess_kwargs()
kwargs['cwd'] = self.repo_path
kwargs['timeout'] = 60
result = subprocess.run(
['git', 'push'],
**kwargs
)
if result.returncode == 0:
logger.info("Git推送成功")
return True
logger.error(f"Git推送失败: {result.stderr}")
return False
except subprocess.TimeoutExpired:
logger.error("Git推送超时")
return False
except Exception as e:
logger.error(f"Git推送异常: {e}")
return False
def test_git_manager():
"""测试Git管理器功能"""
git_mgr = GitManager()
print(f"是否为Git仓库: {git_mgr.is_git_repo()}")
print(f"是否有未提交更改: {git_mgr.has_uncommitted_changes()}")
print(f"当前commit ID: {git_mgr.get_current_commit_id()}")
print(f"短commit ID: {git_mgr.get_short_commit_id()}")
info = git_mgr.get_commit_info()
print(f"Commit信息: {info}")
if __name__ == "__main__":
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
test_git_manager()