11#!/usr/bin/env python3
22
33import argparse
4- import subprocess
4+ import os
55import re
6+ import subprocess
67import sys
7- import os
88import tempfile
99
10+
1011def run_git (repo , args ):
1112 """Run a git command in the given repository and return its output as a string."""
12- result = subprocess .run ([' git' , '-C' , repo ] + args , text = True , capture_output = True , check = False )
13+ result = subprocess .run ([" git" , "-C" , repo ] + args , text = True , capture_output = True , check = False )
1314 if result .returncode != 0 :
1415 raise RuntimeError (f"Git command failed: { ' ' .join (args )} \n { result .stderr } " )
1516 return result .stdout
1617
18+
1719def ref_exists (repo , ref ):
1820 """Return True if the given ref exists in the repository, False otherwise."""
1921 try :
20- run_git (repo , [' rev-parse' , ' --verify' , ' --quiet' , ref ])
22+ run_git (repo , [" rev-parse" , " --verify" , " --quiet" , ref ])
2123 return True
2224 except RuntimeError :
2325 return False
2426
27+
2528def get_pr_commits (repo , pr_branch , base_branch ):
2629 """Get a list of commit SHAs that are in the PR branch but not in the base branch."""
2730 try :
28- output = run_git (repo , [' rev-list' , f' { base_branch } ..{ pr_branch } ' ])
31+ output = run_git (repo , [" rev-list" , f" { base_branch } ..{ pr_branch } " ])
2932 return output .strip ().splitlines ()
3033 except RuntimeError as e :
3134 raise RuntimeError (f"Failed to get commits from { base_branch } ..{ pr_branch } : { e } " )
3235
36+
3337def get_commit_message (repo , sha ):
3438 """Get the commit message for a given commit SHA."""
3539 try :
36- return run_git (repo , [' log' , '-n' , '1' , ' --format=%B' , sha ])
40+ return run_git (repo , [" log" , "-n" , "1" , " --format=%B" , sha ])
3741 except RuntimeError as e :
3842 raise RuntimeError (f"Failed to get commit message for { sha } : { e } " )
3943
44+
4045def get_short_hash_and_subject (repo , sha ):
4146 """Get the abbreviated commit hash and subject for a given commit SHA."""
4247 try :
43- output = run_git (repo , [' log' , '-n' , '1' , ' --format=%h%x00%s' , sha ]).strip ()
44- short_hash , subject = output .split (' \x00 ' , 1 )
48+ output = run_git (repo , [" log" , "-n" , "1" , " --format=%h%x00%s" , sha ]).strip ()
49+ short_hash , subject = output .split (" \x00 " , 1 )
4550 return short_hash , subject
4651 except RuntimeError as e :
4752 raise RuntimeError (f"Failed to get short hash and subject for { sha } : { e } " )
4853
54+
4955def extract_upstream_hash (msg ):
5056 """Extract the upstream commit hash from a commit message.
5157 Looks for lines like 'commit <hash>' in the commit message."""
52- match = re .search (r' ^commit\s+([0-9a-fA-F]{12,40})' , msg , re .MULTILINE )
58+ match = re .search (r" ^commit\s+([0-9a-fA-F]{12,40})" , msg , re .MULTILINE )
5359 if match :
5460 return match .group (1 )
5561 return None
5662
63+
5764def run_interdiff (repo , backport_sha , upstream_sha , interdiff_path ):
5865 """Run interdiff comparing the backport commit with the upstream commit.
5966 Returns (success, output) tuple."""
6067 # Generate format-patch for backport commit
6168 try :
62- backport_patch = run_git (repo , [' format-patch' , '-1' , ' --stdout' , backport_sha ])
69+ backport_patch = run_git (repo , [" format-patch" , "-1" , " --stdout" , backport_sha ])
6370 except RuntimeError as e :
6471 return False , f"Failed to generate patch for backport commit: { e } "
6572
6673 # Generate format-patch for upstream commit
6774 try :
68- upstream_patch = run_git (repo , [' format-patch' , '-1' , ' --stdout' , upstream_sha ])
75+ upstream_patch = run_git (repo , [" format-patch" , "-1" , " --stdout" , upstream_sha ])
6976 except RuntimeError as e :
7077 return False , f"Failed to generate patch for upstream commit: { e } "
7178
7279 # Write patches to temp files
7380 bp_path = None
7481 up_path = None
7582 try :
76- with tempfile .NamedTemporaryFile (mode = 'w' , suffix = ' .patch' , delete = False ) as bp :
83+ with tempfile .NamedTemporaryFile (mode = "w" , suffix = " .patch" , delete = False ) as bp :
7784 bp .write (backport_patch )
7885 bp_path = bp .name
7986
80- with tempfile .NamedTemporaryFile (mode = 'w' , suffix = ' .patch' , delete = False ) as up :
87+ with tempfile .NamedTemporaryFile (mode = "w" , suffix = " .patch" , delete = False ) as up :
8188 up .write (upstream_patch )
8289 up_path = up .name
8390
8491 interdiff_result = subprocess .run (
85- [interdiff_path , '--fuzzy=3' , bp_path , up_path ],
86- text = True ,
87- capture_output = True ,
88- check = False
92+ [interdiff_path , "--fuzzy=3" , bp_path , up_path ], text = True , capture_output = True , check = False
8993 )
9094
9195 # Check for interdiff errors (non-zero return code other than 1)
@@ -107,21 +111,21 @@ def run_interdiff(repo, backport_sha, upstream_sha, interdiff_path):
107111 if up_path and os .path .exists (up_path ):
108112 os .unlink (up_path )
109113
114+
110115def find_interdiff ():
111116 """Find interdiff in system PATH. Returns path if found, None otherwise."""
112- result = subprocess .run ([' which' , ' interdiff' ], capture_output = True , text = True , check = False )
117+ result = subprocess .run ([" which" , " interdiff" ], capture_output = True , text = True , check = False )
113118 if result .returncode == 0 :
114119 return result .stdout .strip ()
115120 return None
116121
122+
117123def main ():
118- parser = argparse .ArgumentParser (
119- description = "Run interdiff on backported kernel commits to compare with upstream."
120- )
124+ parser = argparse .ArgumentParser (description = "Run interdiff on backported kernel commits to compare with upstream." )
121125 parser .add_argument ("--repo" , help = "Path to the Linux kernel git repo" , required = True )
122126 parser .add_argument ("--pr_branch" , help = "Git reference to the feature branch" , required = True )
123127 parser .add_argument ("--base_branch" , help = "Branch the feature branch is based off of" , required = True )
124- parser .add_argument ("--markdown" , action = ' store_true' , help = "Format output with markdown" )
128+ parser .add_argument ("--markdown" , action = " store_true" , help = "Format output with markdown" )
125129 parser .add_argument ("--interdiff" , help = "Path to interdiff executable (default: system interdiff)" , default = None )
126130 args = parser .parse_args ()
127131
@@ -145,8 +149,7 @@ def main():
145149
146150 # Validate that all required refs exist
147151 missing_refs = []
148- for refname , refval in [('PR branch' , args .pr_branch ),
149- ('base branch' , args .base_branch )]:
152+ for refname , refval in [("PR branch" , args .pr_branch ), ("base branch" , args .base_branch )]:
150153 if not ref_exists (args .repo , refval ):
151154 missing_refs .append ((refname , refval ))
152155
@@ -210,7 +213,7 @@ def main():
210213 any_differences = True
211214 if args .markdown :
212215 out_lines .append (f"- ⚠️ PR commit `{ pr_commit_desc } ` → upstream `{ upstream_hash [:12 ]} `" )
213- out_lines .append (f " **Differences found:**\n " )
216+ out_lines .append (" **Differences found:**\n " )
214217 out_lines .append ("```diff" )
215218 out_lines .append (output )
216219 out_lines .append ("```\n " )
@@ -226,15 +229,16 @@ def main():
226229 if any_differences :
227230 if args .markdown :
228231 print ("## :mag: Interdiff Analysis\n " )
229- print (' \n ' .join (out_lines ))
232+ print (" \n " .join (out_lines ))
230233 print ("*This is an automated interdiff check for backported commits.*" )
231234 else :
232- print (' \n ' .join (out_lines ))
235+ print (" \n " .join (out_lines ))
233236 else :
234237 if args .markdown :
235238 print ("> ✅ **All backported commits match their upstream counterparts.**" )
236239 else :
237240 print ("All backported commits match their upstream counterparts." )
238241
242+
239243if __name__ == "__main__" :
240244 main ()
0 commit comments