@@ -33,13 +33,33 @@ def get_cherry_pick_sha(message):
3333 m = re .search (r'\(cherry picked from commit ([a-fA-F0-9]+)\)' , message )
3434 return m .group (1 ) if m else None
3535
36+ def get_backported_commit_sha (message ):
37+ m = re .search (r'\(backported from commit ([a-fA-F0-9]+)\)' , message )
38+ return m .group (1 ) if m else None
39+
40+ def get_upstream_commit_sha (message ):
41+ for getter in (get_cherry_pick_sha , get_backported_commit_sha ):
42+ sha = getter (message )
43+ if sha :
44+ return sha
45+ m = re .search (r'^[Uu]pstream commit ([a-fA-F0-9]+)' , message , re .MULTILINE )
46+ return m .group (1 ) if m else None
47+
3648def get_backport_url (message ):
3749 m = re .search (r'\(backported from (https?://[^\)]+)\)' , message )
3850 return m .group (1 ) if m else None
3951
4052def is_sauce (subject ):
4153 return bool (re .match (r'^NVIDIA:.*SAUCE:' , subject ))
4254
55+ def is_ubuntu_local (subject ):
56+ """Ubuntu-local commits with no upstream equivalent.
57+
58+ Catches 'UBUNTU: SAUCE: ...', 'UBUNTU: [Config] ...',
59+ 'UBUNTU: [Packaging] ...', 'UBUNTU: Ubuntu-X.X.X-...', etc.
60+ """
61+ return bool (re .match (r'^UBUNTU:\s' , subject ))
62+
4363def is_revert (subject ):
4464 return subject .startswith ('Revert "' )
4565
@@ -129,7 +149,7 @@ def describe_sob_chain_backport(message):
129149 return "ok, backporter: {}" .format (' ' .join (names ))
130150
131151
132- COL_WIDTHS = [12 , 45 , 10 , 7 , 25 ]
152+ COL_WIDTHS = [12 , 64 , 10 , 7 , 25 ]
133153HEADERS = ['Local' , 'Referenced upstream / Patch subject' , 'Patch-ID' , 'Subject' , 'SoB chain' ]
134154
135155def _hline (left , sep , right ):
@@ -159,6 +179,11 @@ def print_digest_table(rows):
159179 print (_hline ('└' , '┴' , '┘' ))
160180
161181
182+ def format_upstream_ref (sha12 , subject ):
183+ """Return compact digest text for a referenced upstream commit."""
184+ return "{} {}" .format (sha12 , subject )
185+
186+
162187def _shorten_lkml_url (url ):
163188 """Shorten a lore.kernel.org URL to a compact display form."""
164189 m = re .search (r'lore\.kernel\.org/[^/]+/([^/]+)' , url )
@@ -322,7 +347,7 @@ def build_digest(commits, repo, upstream_remote=None):
322347 errors = []
323348
324349 for commit in commits :
325- src_sha = get_cherry_pick_sha (commit .message )
350+ src_sha = get_upstream_commit_sha (commit .message )
326351 bp_url = get_backport_url (commit .message )
327352
328353 # --- SAUCE / Revert: no upstream reference, show as informational row ---
@@ -336,6 +361,9 @@ def build_digest(commits, repo, upstream_remote=None):
336361 # Strip outer Revert "..." wrapper and SAUCE prefix for display
337362 inner = re .sub (r'^Revert\s+"' , '' , subj ).rstrip ('"' )
338363 display = _norm_sauce_subject (inner ) or inner
364+ elif is_ubuntu_local (subj ):
365+ kind = 'UBUNTU'
366+ display = re .sub (r'^UBUNTU:\s*' , '' , subj )
339367 else :
340368 kind = 'SAUCE'
341369 display = _norm_sauce_subject (subj ) or subj
@@ -411,6 +439,7 @@ def build_digest(commits, repo, upstream_remote=None):
411439 # Subject
412440 local_subj = subject_of (commit )
413441 upstream_subj = subject_of (upstream )
442+ upstream_ref = format_upstream_ref (upstream_sha12 , upstream_subj )
414443 if local_subj == upstream_subj :
415444 subj_status = 'match'
416445 subj_error = False
@@ -427,7 +456,7 @@ def build_digest(commits, repo, upstream_remote=None):
427456 local_sha12 , subject_of (commit )[:40 ], sob_status ))
428457
429458 has_error = pid_error or subj_error or sob_error
430- rows .append (dict (local = local_sha12 , upstream = upstream_sha12 ,
459+ rows .append (dict (local = local_sha12 , upstream = upstream_ref ,
431460 patch_id = pid_status , subject = subj_status ,
432461 sob = sob_status , error = has_error ))
433462
@@ -460,20 +489,25 @@ def lint_commits(commits):
460489
461490 # Classification for R6/R7/R9/R10
462491 sauce = is_sauce (subject )
492+ ubuntu = is_ubuntu_local (subject )
463493 revert = is_revert (subject )
464494
465- # R9: subject length — exempt SAUCE and Reverts of SAUCE; the mandatory
466- # "NVIDIA: [VR: ]SAUCE:" prefix already consumes 15–20 characters.
495+ # R9: subject length — exempt SAUCE, UBUNTU local and Reverts of SAUCE;
496+ # the mandatory "NVIDIA: [VR: ]SAUCE:" / "UBUNTU: [Config]" prefix
497+ # already consumes 12–20 characters.
467498 revert_of_sauce = revert and bool (re .match (r'^Revert "NVIDIA:.*SAUCE:' , subject ))
468- if len (subject ) > 72 and not sauce and not revert_of_sauce :
499+ if len (subject ) > 72 and not sauce and not ubuntu and not revert_of_sauce :
469500 warnings .append ("W: {}: subject {} chars (>72)" .format (label , len (subject )))
470- cp_sha = get_cherry_pick_sha (commit .message )
501+ upstream_sha = get_upstream_commit_sha (commit .message )
502+ cp_sha = get_cherry_pick_sha (commit .message )
471503 bp_url = get_backport_url (commit .message )
472504
473- # R6: non-SAUCE, non-Revert commits must have an upstream reference trailer
474- if not sauce and not revert and cp_sha is None and bp_url is None :
505+ # R6: non-SAUCE, non-UBUNTU, non-Revert commits must have an upstream
506+ # reference trailer. UBUNTU local commits (e.g. UBUNTU: [Config]) have
507+ # no upstream equivalent.
508+ if not sauce and not ubuntu and not revert and upstream_sha is None and bp_url is None :
475509 errors .append (
476- "E: {}: not SAUCE/Revert but has no upstream reference trailer"
510+ "E: {}: not SAUCE/UBUNTU/ Revert but has no upstream reference trailer"
477511 " (cherry picked from commit ... or backported from ...)" .format (label ))
478512
479513 # R7: detect wrong trailer for LKML in-review backports
@@ -508,19 +542,18 @@ def check_pr_metadata(pr_title, pr_body_path, base_branch):
508542 warnings .append (
509543 "W: PR title missing [<branch>] prefix: \" {}\" " .format (pr_title [:80 ]))
510544
511- # R3: BugLink required for tracked branches
545+ # R3: Launchpad bug link required for tracked branches
512546 if pr_body_path and base_branch and TRACKED_BRANCH_RE .match (base_branch ):
513547 try :
514548 body = open (pr_body_path ).read ()
515549 except OSError as e :
516550 errors .append ("E: cannot read --pr-body {}: {}" .format (pr_body_path , e ))
517551 body = ''
518- buglink_re = re .compile (
519- r'^(?:BugLink:|LP:)\s+https://bugs\.launchpad\.net/' , re .MULTILINE )
552+ buglink_re = re .compile (r'https://bugs\.launchpad\.net/' )
520553 if not buglink_re .search (body ):
521554 errors .append (
522- "E: PR targets {} but body has no 'BugLink:' or 'LP:' "
523- " https://bugs.launchpad.net/... line " .format (base_branch ))
555+ "E: PR targets {} but body has no"
556+ " https://bugs.launchpad.net/... link " .format (base_branch ))
524557
525558 return warnings , errors
526559
0 commit comments