1+ #
2+ # Copyright (c) 2025, RT-Thread Development Team
3+ #
4+ # SPDX-License-Identifier: Apache-2.0
5+ #
6+ # Change Logs:
7+ # Date Author Notes
8+ # 2025-05-15 supperthomas add PR status show
9+ #
10+ #!/usr/bin/env python3
11+ import subprocess
12+ import sys
13+ import os
14+ import re
15+ import argparse
16+ import locale
17+ from typing import List , Dict
18+
19+ class FileDiff :
20+ def __init__ (self , path : str , status : str , size_change : int = 0 , old_size : int = 0 , new_size : int = 0 ):
21+ self .path = path
22+ self .status = status # A (added), M (modified), D (deleted), R (renamed)
23+ self .size_change = size_change
24+ self .old_size = old_size
25+ self .new_size = new_size
26+
27+ def __str__ (self ):
28+ if self .status == 'A' :
29+ return f"Added { self .path } : { self .size_change } bytes"
30+ elif self .status == 'D' :
31+ return f"Deleted { self .path } : was { self .old_size } bytes"
32+ elif self .status == 'M' or self .status == 'R' :
33+ return f"Modified { self .path } : { self .size_change } bytes change"
34+ else :
35+ return f"{ self .status } { self .path } "
36+
37+ class GitDiffAnalyzer :
38+ def __init__ (self , target_branch : str ):
39+ self .target_branch = target_branch
40+ self .encoding = locale .getpreferredencoding ()
41+
42+ def get_diff_files (self ) -> List [FileDiff ]:
43+ """获取当前分支与目标分支之间的差异文件"""
44+ # 获取差异文件列表
45+ diff_cmd = f"git diff --name-status { self .target_branch } "
46+ print (diff_cmd )
47+ try :
48+ output = subprocess .check_output (diff_cmd .split (), stderr = subprocess .STDOUT )
49+ output = output .decode (self .encoding ).strip ()
50+ print (output )
51+ except subprocess .CalledProcessError as e :
52+ print (f"Error executing git diff: { e .output .decode (self .encoding )} " )
53+ sys .exit (1 )
54+
55+ if not output :
56+ print ("No differences between current branch and target branch" )
57+ sys .exit (0 )
58+
59+ # 处理可能的换行符问题
60+ output = output .replace ('\r \n ' , '\n ' )
61+ lines = output .split ('\n ' )
62+
63+ file_diffs = []
64+ for line in lines :
65+ line = line .strip ()
66+ if not line :
67+ continue
68+
69+ parts = line .split ('\t ' )
70+ if len (parts ) < 2 :
71+ # 可能是重命名文件,格式为 "RXXX\told_path\tnew_path"
72+ match = re .match (r'R(\d+)\t(.+)\t(.+)' , line )
73+ if match :
74+ status = 'R'
75+ old_path = match .group (2 )
76+ new_path = match .group (3 )
77+ # 计算重命名文件的修改大小
78+ old_size = self .get_file_size (old_path , self .target_branch )
79+ new_size = self .get_file_size (new_path , 'HEAD' )
80+ size_change = new_size - old_size if old_size > 0 else new_size
81+ file_diffs .append (FileDiff (new_path , status , size_change , old_size , new_size ))
82+ else :
83+ status = parts [0 ][0 ] # 取状态码的第一个字符
84+ path = parts [1 ]
85+
86+ if status == 'A' :
87+ # 新增文件,计算大小
88+ size = self .get_file_size (path , 'HEAD' )
89+ file_diffs .append (FileDiff (path , status , size , 0 , size ))
90+ elif status == 'D' :
91+ # 删除文件,计算原大小
92+ size = self .get_file_size (path , self .target_branch )
93+ file_diffs .append (FileDiff (path , status , 0 , size , 0 ))
94+ elif status == 'M' :
95+ # 修改文件,计算大小变化
96+ old_size = self .get_file_size (path , self .target_branch )
97+ new_size = self .get_file_size (path , 'HEAD' )
98+ size_change = new_size - old_size
99+ file_diffs .append (FileDiff (path , status , size_change , old_size , new_size ))
100+
101+ return file_diffs
102+
103+ def get_file_size (self , path : str , ref : str ) -> int :
104+ """获取指定分支上文件的大小"""
105+ try :
106+ # 使用 git cat-file 来获取文件内容,然后计算其大小
107+ cmd = f"git cat-file blob { ref } :{ path } "
108+ output = subprocess .check_output (cmd .split (), stderr = subprocess .STDOUT )
109+ return len (output )
110+ except subprocess .CalledProcessError :
111+ # 如果文件不存在或无法获取,返回0
112+ return 0
113+
114+ def format_size (size : int ) -> str :
115+ """将字节大小转换为人类可读的格式"""
116+ if size < 1024 :
117+ return f"{ size } bytes"
118+ elif size < 1024 * 1024 :
119+ return f"{ size / 1024 :.1f} KB"
120+ elif size < 1024 * 1024 * 1024 :
121+ return f"{ size / (1024 * 1024 ):.1f} MB"
122+ else :
123+ return f"{ size / (1024 * 1024 * 1024 ):.1f} GB"
124+
125+ def main ():
126+ parser = argparse .ArgumentParser (description = 'Compare current branch with target branch and show file differences.' )
127+ parser .add_argument ('target_branch' , help = 'Target branch to compare against (e.g., master)' )
128+ args = parser .parse_args ()
129+
130+ analyzer = GitDiffAnalyzer (args .target_branch )
131+ file_diffs = analyzer .get_diff_files ()
132+
133+ # 生成报告
134+ generate_report (file_diffs , args .target_branch )
135+
136+ def generate_report (file_diffs : List [FileDiff ], target_branch : str ):
137+ """生成差异报告"""
138+ print (f"\n === Comparison between { target_branch } and current branch ===\n " )
139+
140+ # 分类统计
141+ added_files = [f for f in file_diffs if f .status == 'A' ]
142+ removed_files = [f for f in file_diffs if f .status == 'D' ]
143+ modified_files = [f for f in file_diffs if f .status == 'M' ]
144+ renamed_files = [f for f in file_diffs if f .status == 'R' ]
145+
146+ # 计算总变化量
147+ total_added = sum (f .size_change for f in added_files )
148+ total_removed = sum (f .old_size for f in removed_files )
149+ total_modified = sum (abs (f .size_change ) for f in modified_files )
150+ # 计算总的大小变化
151+ total_size_change = sum (f .size_change for f in file_diffs )
152+
153+ print (f"Total changes: { len (file_diffs )} files" )
154+ print (f"Added: { len (added_files )} files ({ format_size (total_added )} )" )
155+ print (f"Removed: { len (removed_files )} files ({ format_size (total_removed )} )" )
156+ print (f"Modified: { len (modified_files )} files ({ format_size (total_modified )} )" )
157+ print (f"Renamed: { len (renamed_files )} files" )
158+ print (f"\n Total size change: { format_size (total_size_change )} " )
159+ print ("\n " + "=" * 60 + "\n " )
160+
161+ # 显示详细差异
162+ for diff in file_diffs :
163+ print (diff )
164+
165+ # 详细展示修改和新增的文件大小
166+ if diff .status == 'A' :
167+ print (f" Size: { format_size (diff .new_size )} " )
168+ elif diff .status == 'M' :
169+ print (f" Original size: { format_size (diff .old_size )} " )
170+ print (f" New size: { format_size (diff .new_size )} " )
171+ print (f" Size change: { format_size (abs (diff .size_change ))} " )
172+ elif diff .status == 'R' :
173+ print (f" Original size: { format_size (diff .old_size )} " )
174+ print (f" New size: { format_size (diff .new_size )} " )
175+ print (f" Size change: { format_size (abs (diff .size_change ))} " )
176+ elif diff .status == 'D' :
177+ print (f" Original size: { format_size (diff .old_size )} " )
178+
179+ print ("-" * 50 )
180+
181+ if __name__ == "__main__" :
182+ main ()
0 commit comments