Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/git_diff_status_show.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#
# Copyright (c) 2006-2023, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2025-05-15 Supper Thomas show the PR size
#

name: PR status

# 这个ci主要用来显示当前的PR对于master节点上的增加的code size有多大
on:
pull_request:
branches:
- master

permissions:
contents: read # to fetch code (actions/checkout)

jobs:
build:
runs-on: ubuntu-24.04
name: PR status show

steps:
- uses: actions/checkout@main
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@main
with:
python-version: 3.8

- name: fetch origin
shell: bash
run: |
git config --global http.postBuffer 524288000
git remote -v
git fetch origin
git branch
git branch -a

- name: PR status show
if: ${{ success() }}
shell: bash
run: |
python tools/ci/git_diff_status_show.py origin/master

201 changes: 201 additions & 0 deletions tools/ci/git_diff_status_show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#
# 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
#
#!/usr/bin/env python3
import subprocess
import sys
import os
import re
import argparse
import locale
from typing import List, Dict

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} "
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 < 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"

def main():
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)



def generate_report(file_diffs: List[FileDiff], target_branch: str):
"""生成差异报告"""
print(f"\n=== Comparison between {target_branch} and current branch ===\n")

# 分类统计
added_files = [f for f in file_diffs if f.status == 'A']
removed_files = [f for f in file_diffs if f.status == 'D']
modified_files = [f for f in file_diffs if f.status == 'M']
renamed_files = [f for f in file_diffs if f.status == 'R']

# 计算总变化量
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(abs(f.size_change) for f in modified_files)
# 计算总的大小变化
total_size_change = sum(f.size_change for f in file_diffs)

print(f"Total changes: {len(file_diffs)} files")
print(f"Added: {len(added_files)} files ({format_size(total_added)})")
print(f"Removed: {len(removed_files)} files ({format_size(total_removed)})")
print(f"Modified: {len(modified_files)} files ({format_size(total_modified)})")
print(f"Renamed: {len(renamed_files)} files")
print(f"\nTotal size change: {format_size(total_size_change)}")
print("\n" + "="*60 + "\n")

# 显示详细差异
for diff in file_diffs:
print(diff)

# 详细展示修改和新增的文件大小
if diff.status == 'A':
print(f" Size: {format_size(diff.new_size)}")
elif diff.status == 'M':
print(f" Original size: {format_size(diff.old_size)}")
print(f" New size: {format_size(diff.new_size)}")
print(f" Size change: {format_size(abs(diff.size_change))}")
elif diff.status == 'R':
print(f" Original size: {format_size(diff.old_size)}")
print(f" New size: {format_size(diff.new_size)}")
print(f" Size change: {format_size(abs(diff.size_change))}")
elif diff.status == 'D':
print(f" Original size: {format_size(diff.old_size)}")

print("-" * 50)

if __name__ == "__main__":
main()