From: Simon Glass <sjg@chromium.org>
Add a 'patman series info' command that displays detailed information
about a series including description, upstream, and for each version:
the patchwork link, per-version description, cover letter name, patch
list with state, archive tag and review notes.
This helps diagnose autolink issues by showing what description each
version has stored, which is what patchwork searches use.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
tools/patman/cmdline.py | 1 +
tools/patman/control.py | 2 ++
tools/patman/cseries.py | 47 ++++++++++++++++++++++++++++++++++++
tools/patman/test_cseries.py | 42 ++++++++++++++++++++++++++++++++
4 files changed, 92 insertions(+)
@@ -294,6 +294,7 @@ def add_series_subparser(subparsers):
series_subparsers.add_parser('get-link')
series_subparsers.add_parser('inc')
+ series_subparsers.add_parser('info')
ls = series_subparsers.add_parser('ls', aliases=['list'])
_add_archived(ls)
@@ -199,6 +199,8 @@ def do_series(args, test_db=None, pwork=None, cser=None):
dry_run=args.dry_run, show_summary=True)
elif args.subcmd == 'dec':
cser.decrement(args.series, args.dry_run)
+ elif args.subcmd == 'info':
+ cser.show_info(args.series)
elif args.subcmd == 'gather':
cser.gather(pwork, args.series, args.version, args.show_comments,
args.show_cover_comments, args.gather_tags,
@@ -861,6 +861,53 @@ class Cseries(cser_helper.CseriesHelper):
if dry_run:
tout.info('Dry run completed')
+ def show_info(self, series):
+ """Show detailed information about a series and all its versions
+
+ Args:
+ series (str): Series name, or None for current branch
+ """
+ ser, _ = self._parse_series_and_version(series, None)
+ if not ser.idnum:
+ raise ValueError(f"Series '{ser.name}' not found in database")
+
+ print(f"Series: {ser.name}")
+ print(f" Description: {ser.desc}")
+ print(f" Upstream: {ser.upstream or '(none)'}")
+
+ versions = self.db.ser_ver_get_for_series(ser.idnum)
+ if not isinstance(versions, list):
+ versions = [versions]
+
+ for sv in versions:
+ link_str = sv.link or '(none)'
+ print(f"\n Version {sv.version}:")
+ print(f" Link: {link_str}")
+ print(f" Description: {sv.desc or '(none)'}")
+ if sv.name:
+ print(f" Cover: {sv.name}")
+ if sv.archive_tag:
+ print(f" Archive tag: {sv.archive_tag}")
+
+ # Show patches
+ try:
+ pclist = self.db.pcommit_get_list(sv.idnum)
+ print(f" Patches: {len(pclist)}")
+ for pc in pclist:
+ state = f' [{pc.state}]' if pc.state else ''
+ print(f" {pc.seq + 1}: {pc.subject}{state}")
+ except (ValueError, AttributeError):
+ pass
+
+ # Show notes if any
+ if sv.notes:
+ lines = sv.notes.strip().splitlines()
+ print(f" Notes: {lines[0]}")
+ for line in lines[1:3]:
+ print(f" {line}")
+ if len(lines) > 3:
+ print(f" ... ({len(lines)} lines)")
+
def set_upstream(self, series, ups, dry_run=False):
"""Set the upstream for a series
@@ -4438,3 +4438,45 @@ Date: .*
# Future 3 weeks
when = datetime(2025, 3, 31, 10, 0, 0)
self.assertEqual('in 3w', wf.friendly_time(now, when))
+
+ def test_series_info(self):
+ """Test the series info command"""
+ cser = self.get_database()
+
+ # Create a series with upstream and two versions
+ cser.db.upstream_add('us', 'https://us.example.com')
+ series_id = cser.db.series_add('test-info', 'My test series', ups='us')
+ svid1 = cser.db.ser_ver_add(series_id, 1, link='12345',
+ desc='First version desc')
+ svid2 = cser.db.ser_ver_add(series_id, 2, desc='Second version desc')
+
+ # Add patches to v1
+ from patman.database import Pcommit
+ cser.db.pcommit_add_list(svid1, [
+ Pcommit(idnum=None, seq=0, subject='Fix the widget',
+ svid=svid1, change_id=None, state=None,
+ patch_id=None, num_comments=0),
+ Pcommit(idnum=None, seq=1, subject='Add widget tests',
+ svid=svid1, change_id=None, state=None,
+ patch_id=None, num_comments=0)])
+
+ # Add notes to v2
+ cser.db.ser_ver_set_notes(svid2, 'Fixed review feedback')
+ cser.commit()
+
+ with terminal.capture() as (out, _):
+ cser.show_info('test-info')
+
+ output = out.getvalue()
+ self.assertIn('Series: test-info', output)
+ self.assertIn('Description: My test series', output)
+ self.assertIn('Upstream: us', output)
+ self.assertIn('Version 1:', output)
+ self.assertIn('Link: 12345', output)
+ self.assertIn('First version desc', output)
+ self.assertIn('Patches: 2', output)
+ self.assertIn('Fix the widget', output)
+ self.assertIn('Add widget tests', output)
+ self.assertIn('Version 2:', output)
+ self.assertIn('Second version desc', output)
+ self.assertIn('Notes: Fixed review feedback', output)