-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Expand file tree
/
Copy pathgit_diff_show.py
More file actions
372 lines (308 loc) · 14.3 KB
/
Copy pathgit_diff_show.py
File metadata and controls
372 lines (308 loc) · 14.3 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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
#
# Copyright (c) 2025, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2025-05-15 supperthomas add PR status show
# 2025-05-22 hydevcode 替换bsp_building.yml的判断文件修改机制,并将PR status show合并进bsp_building.yml
import subprocess
import sys
import os
import re
import argparse
import locale
from typing import List, Dict
import json
from typing import List
class FileDiff:
def __init__(self, path: str, status: str, size_change: int = 0, old_size: int = 0, new_size: int = 0):
self.path = path
self.status = status # A (added), M (modified), D (deleted), R (renamed)
self.size_change = size_change
self.old_size = old_size
self.new_size = new_size
def __str__(self):
if self.status == 'A':
return f"Added {self.path}: {self.size_change} bytes"
elif self.status == 'D':
return f"Deleted {self.path}: was {self.old_size} bytes"
elif self.status == 'M' or self.status == 'R':
return f"Modified {self.path}: {self.size_change} bytes change"
else:
return f"{self.status} {self.path}"
class GitDiffAnalyzer:
def __init__(self, target_branch: str):
self.target_branch = target_branch
self.encoding = locale.getpreferredencoding()
def get_diff_files(self) -> List[FileDiff]:
"""获取当前分支与目标分支之间的差异文件"""
# 找到当前分支和目标分支的最近共同祖先
merge_base = self.get_merge_base()
if not merge_base:
print("No common ancestor found between current branch and target branch")
sys.exit(1)
# 获取差异文件列表
diff_cmd = f"git diff --name-status {merge_base} HEAD"
print(diff_cmd)
try:
output = subprocess.check_output(diff_cmd.split(), stderr=subprocess.STDOUT)
output = output.decode(self.encoding).strip()
print(output)
except subprocess.CalledProcessError as e:
print(f"Error executing git diff: {e.output.decode(self.encoding)}")
sys.exit(1)
if not output:
print("No differences between current branch and target branch")
sys.exit(0)
# 处理可能的换行符问题
output = output.replace('\r\n', '\n')
lines = output.split('\n')
file_diffs = []
for line in lines:
line = line.strip()
if not line:
continue
parts = line.split('\t')
if len(parts) < 2:
# 可能是重命名文件,格式为 "RXXX\told_path\tnew_path"
match = re.match(r'R(\d+)\t(.+)\t(.+)', line)
if match:
status = 'R'
old_path = match.group(2)
new_path = match.group(3)
# 计算重命名文件的修改大小
old_size = self.get_file_size(old_path, self.target_branch)
new_size = self.get_file_size(new_path, 'HEAD')
size_change = new_size - old_size if old_size > 0 else new_size
file_diffs.append(FileDiff(new_path, status, size_change, old_size, new_size))
else:
status = parts[0][0] # 取状态码的第一个字符
path = parts[1]
if status == 'A':
# 新增文件,计算大小
size = self.get_file_size(path, 'HEAD')
file_diffs.append(FileDiff(path, status, size, 0, size))
elif status == 'D':
# 删除文件,计算原大小
size = self.get_file_size(path, self.target_branch)
file_diffs.append(FileDiff(path, status, 0, size, 0))
elif status == 'M':
# 修改文件,计算大小变化
old_size = self.get_file_size(path, self.target_branch)
new_size = self.get_file_size(path, 'HEAD')
size_change = new_size - old_size
file_diffs.append(FileDiff(path, status, size_change, old_size, new_size))
return file_diffs
def get_merge_base(self) -> str:
"""获取当前分支和目标分支的最近共同祖先"""
try:
cmd = f"git merge-base {self.target_branch} HEAD"
print(cmd)
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
return output.decode(self.encoding).strip()
except subprocess.CalledProcessError as e:
print(f"Error executing git merge-base: {e.output.decode(self.encoding)}")
return None
def get_file_size(self, path: str, ref: str) -> int:
"""获取指定分支上文件的大小"""
try:
# 使用 git cat-file 来获取文件内容,然后计算其大小
cmd = f"git cat-file blob {ref}:{path}"
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
return len(output)
except subprocess.CalledProcessError:
# 如果文件不存在或无法获取,返回0
return 0
def format_size(size: int) -> str:
"""将字节大小转换为人类可读的格式"""
if size >= 0:
if size < 1024:
return f"{size} bytes"
elif size < 1024 * 1024:
return f"{size / 1024:.1f} KB"
elif size < 1024 * 1024 * 1024:
return f"{size / (1024 * 1024):.1f} MB"
else:
return f"{size / (1024 * 1024 * 1024):.1f} GB"
else:
temp_size=abs(size)
if temp_size < 1024:
return f"-{temp_size} bytes"
elif temp_size < 1024 * 1024:
return f"-{temp_size / 1024:.1f} KB"
elif temp_size < 1024 * 1024 * 1024:
return f"-{temp_size / (1024 * 1024):.1f} MB"
else:
return f"-{temp_size / (1024 * 1024 * 1024):.1f} GB"
def is_bsp(path):
return os.path.isfile(os.path.join(path, "rtconfig.h"))
def filter_bsp_config(file_diffs: List[FileDiff], config_path: str):
# 读取原始JSON配置
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
# 获取所有修改的文件路径(统一使用Linux风格路径)
modified_paths = [diff.path.replace('\\', '/') for diff in file_diffs]
print(modified_paths)
if not modified_paths:
print("master分支运行")
return
bsp_paths = set()
bsp_in_but_not_bsp_paths = set()
all_print_paths = set()
for modified_path in modified_paths:
parts = modified_path.strip(os.sep).split('/')
if not parts:
continue
first_level = parts[0]
first_level_path = os.path.join(os.getcwd(), first_level)
#处理bsp路径的逻辑
if first_level == "bsp":
temp_path=os.path.join(os.getcwd(), modified_path)
# 判断是否是文件
if not os.path.isdir(modified_path):
temp_path = os.path.dirname(temp_path)
#循环搜索每一级是否有rtconfig.h
while temp_path !=first_level_path:
if is_bsp(temp_path):
bsp_paths.add(temp_path)
break
temp_path=os.path.dirname(temp_path)
if temp_path ==first_level_path:
bsp_in_but_not_bsp_paths.add(parts[1])
else:
#非bsp路径逻辑
all_print_paths.add(first_level_path)
# 变成相对路径
bsp_list = set()
for path in sorted(bsp_paths):
current_working_dir = os.path.join(os.getcwd(), "bsp/")
if path.startswith(current_working_dir):
bsp_list.add(path[len(current_working_dir):].lstrip(os.sep))
else:
bsp_list.add(path) # 如果 first_level_path 不以 current_working_dir 开头,则保持不变
# 处理修改了bsp外的文件的情况
filtered_bsp = [
path for path in bsp_list
if path.split('/')[0] not in bsp_in_but_not_bsp_paths
]
merged_result = filtered_bsp + list(bsp_in_but_not_bsp_paths)
filtered_legs = []
for leg in config["legs"]:
matched_paths = [
path for path in leg.get("SUB_RTT_BSP", [])
if any(keyword in path for keyword in merged_result)
]
if matched_paths:
filtered_legs.append({**leg, "SUB_RTT_BSP": matched_paths})
# 生成新的配置
new_config = {"legs": filtered_legs}
# 判断有没有修改到bsp外的文件,有的话则编译全部
if not all_print_paths:
print(new_config)
file_name = ".github/ALL_BSP_COMPILE_TEMP.json"
# 将 new_config 写入文件
with open(file_name, "w", encoding="utf-8") as file:
json.dump(new_config, file, ensure_ascii=False, indent=4)
def main():
# 源文件路径
source_file = ".github/ALL_BSP_COMPILE.json" # 替换为你的文件路径
# 目标文件路径
target_file = ".github/ALL_BSP_COMPILE_TEMP.json" # 替换为你的目标文件路径
# 读取源文件并过滤掉带有 // 的行
with open(source_file, "r", encoding="utf-8") as infile, open(target_file, "w", encoding="utf-8") as outfile:
for line in infile:
if not line.lstrip().startswith("//"):
outfile.write(line)
parser = argparse.ArgumentParser(description='Compare current branch with target branch and show file differences.')
parser.add_argument('target_branch', help='Target branch to compare against (e.g., master)')
args = parser.parse_args()
analyzer = GitDiffAnalyzer(args.target_branch)
file_diffs = analyzer.get_diff_files()
# 生成报告
generate_report(file_diffs, args.target_branch)
filter_bsp_config(file_diffs,".github/ALL_BSP_COMPILE_TEMP.json")
def add_summary(text):
"""
add summary to github action.
"""
if "GITHUB_STEP_SUMMARY" in os.environ:
summary_path = os.environ["GITHUB_STEP_SUMMARY"] # 获取摘要文件路径
# 将 text 写入摘要文件(追加模式)
with open(summary_path, "a") as f: # "a" 表示追加模式
f.write(text + "\n") # 写入文本并换行
else:
print("Environment variable $GITHUB_STEP_SUMMARY is not set.")
def summarize_diff(label, count, size=None):
"""格式化输出变更摘要"""
line = f" {label:<12} {count:>3} files"
if size is not None:
line += f" ({format_size(size)})"
add_summary(line)
def generate_report(file_diffs: List[FileDiff], target_branch: str):
"""生成差异报告"""
add_summary(f"# 📊 **Comparison between {target_branch} and Current Branch**\n")
# 分类统计
added_files = [f for f in file_diffs if f.status.startswith('A')]
removed_files = [f for f in file_diffs if f.status.startswith('D')]
modified_files = [f for f in file_diffs if f.status.startswith('M')]
renamed_files = [f for f in file_diffs if f.status.startswith('R')]
copied_files = [f for f in file_diffs if f.status.startswith('C')]
unmerged_files = [f for f in file_diffs if f.status.startswith('U')]
type_changed_files = [f for f in file_diffs if f.status.startswith('T')]
# 计算总变化量
total_added = sum(f.size_change for f in added_files)
total_removed = sum(f.old_size for f in removed_files)
total_modified = sum(f.size_change for f in modified_files)
total_copied = sum(f.size_change for f in copied_files)
total_renamed = sum(f.old_size for f in renamed_files)
total_type_changed = sum(f.size_change for f in type_changed_files)
total_size_change = sum(f.size_change for f in file_diffs)
# === 汇总输出 ===
summarize_diff("Total:", len(file_diffs))
summarize_diff("Added:", len(added_files), total_added)
summarize_diff("Removed:", len(removed_files), total_removed)
summarize_diff("Modified:", len(modified_files), total_modified)
summarize_diff("Renamed:", len(renamed_files), total_renamed)
summarize_diff("Copied:", len(copied_files), total_copied)
summarize_diff("Type Changed:", len(type_changed_files), total_type_changed)
summarize_diff("Unmerged:", len(unmerged_files))
if total_size_change > 0:
change_desc = f"📈 **Increase of {format_size(total_size_change)}**"
elif total_size_change < 0:
change_desc = f"📉 **Reduction of {format_size(abs(total_size_change))}**"
else:
change_desc = "➖ **No net size change**"
add_summary(f"\n### 📦 **Total Size Change:** {change_desc} (Excluding removed files)")
# 显示详细差异文件内容
add_summary("\n## 📂 **Detailed File Changes**\n")
for diff in file_diffs:
add_summary(f"📄 {diff.path} — **[{diff.status}]**")
# 文件状态处理
if diff.status.startswith('A'):
add_summary(f"📦 Size: {format_size(diff.new_size)}")
elif diff.status.startswith(('M', 'R')): # 修改或重命名
add_summary(f"📏 Original size: {format_size(diff.old_size)}")
add_summary(f"📐 New size: {format_size(diff.new_size)}")
delta = diff.size_change
if delta > 0:
change_str = f"📈 Increased by {format_size(delta)}"
elif delta < 0:
change_str = f"📉 Reduced by {format_size(abs(delta))}"
else:
change_str = "➖ No size change"
add_summary(f"📊 Size change: {change_str}")
elif diff.status.startswith('D'):
add_summary(f"🗑️ Original size: {format_size(diff.old_size)}")
elif diff.status.startswith('C'):
add_summary(f"📎 Copied from size: {format_size(diff.old_size)} → {format_size(diff.new_size)}")
elif diff.status.startswith('T'):
add_summary("⚙️ File type changed")
elif diff.status.startswith('U'):
add_summary("⚠️ Unmerged conflict detected")
else:
add_summary("❓ Unknown change type")
add_summary("\n\n")
if __name__ == "__main__":
main()