[PATCH RFC 06/11] review: add backward discovery of older series revisions
Christian Brauner <[email protected]> Sat, 18 Jul 2026 00:37:42 +0200
| Newsgroups | org.kernel.linux.tools |
|---|---|
| Message-ID | <[email protected]> |
Auto-discovery only looks forward, so versions posted before a series was tracked never enter the revisions catalog unless they shared the seed thread. Add discover_older_revisions(): fetch the tracked thread and run the same get_extra_series() machinery b4 am/mbox uses to pull other revisions -- but with an explicit wantvers covering every previous version, since the backward search defaults to fetching only latest-1. Newly recorded revisions are polled immediately so they arrive with counts and cached thread blobs. Assisted-by: LLM Signed-off-by: Christian Brauner (Amutable) <[email protected]> --- src/b4/review/tracking.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/b4/review/tracking.py b/src/b4/review/tracking.py index c176c46..d61304d 100644 --- a/src/b4/review/tracking.py +++ b/src/b4/review/tracking.py @@ -2774,6 +2774,76 @@ def update_revision_message_counts( return {'updated': updated, 'errors': errors} +def discover_older_revisions( + identifier: str, + series: Dict[str, Any], + linkmask: str, + topdir: Optional[str] = None, +) -> Dict[str, Any]: + """Search lore for revisions older than the tracked one and record them. + + Auto-discovery only looks forward, so versions posted before a series + was tracked never enter the catalog unless they happened to share the + seed thread. This fetches the tracked revision's thread, runs the + backward lore search (change-id query when the cover carries one, + subject+sender otherwise, capped roughly a year back), records every + previously unknown revision, and immediately polls the series so the + new revisions get counts and cached thread blobs. + + Returns ``{'found': n, 'revisions': [..], 'error': str-or-None}`` + where *found* counts genuinely new catalog entries and *revisions* + lists their version numbers. + """ + change_id = series.get('change_id', '') + message_id = series.get('message_id', '') + if not change_id or not message_id: + return {'found': 0, 'revisions': [], 'error': 'no message-id for this series'} + tracked_rev = int(series.get('revision') or 1) + if tracked_rev <= 1: + return {'found': 0, 'revisions': [], 'error': None} + if not b4.can_network: + return {'found': 0, 'revisions': [], 'error': 'offline'} + + mbox_bytes = _fetch_thread_mbox_bytes(str(message_id)) + if mbox_bytes is None: + return { + 'found': 0, + 'revisions': [], + 'error': f'could not fetch thread for {message_id}', + } + msgs = b4.split_and_dedupe_pi_results(mbox_bytes) + # Same machinery b4 am/mbox uses to pull other revisions of a series, + # except we ask for every previous version at once — without an + # explicit wantvers the backward search only fetches latest-1. + wantvers = list(range(1, tracked_rev)) + try: + msgs = b4.mbox.get_extra_series( + msgs, direction=-1, wantvers=wantvers, nocache=True + ) + except liblore.OperationCancelledError: + raise + except Exception as ex: + return {'found': 0, 'revisions': [], 'error': str(ex)} + + lmbx = b4.LoreMailbox() + for msg in msgs: + lmbx.add_message(msg) + if not lmbx.series: + return {'found': 0, 'revisions': [], 'error': None} + + conn = get_db(identifier) + new_revs = _record_discovered_revisions(conn, change_id, lmbx, linkmask) + conn.close() + + if new_revs: + update_revision_message_counts(identifier, [series], topdir=topdir) + return { + 'found': len(new_revs), + 'revisions': sorted(new_revs), + 'error': None, + } + + def mark_all_messages_seen( conn: sqlite3.Connection, change_id: str, revision: int ) -> None: -- 2.53.0