public inbox for tools@linux.kernel.org
 help / color / mirror / Atom feed
* [PATCH b4] b4: Add option to b4-send to include git-notes
@ 2026-02-25  1:01 Vishal Verma
  2026-02-25  2:54 ` Konstantin Ryabitsev
  0 siblings, 1 reply; 4+ messages in thread
From: Vishal Verma @ 2026-02-25  1:01 UTC (permalink / raw)
  To: Kernel.org Tools; +Cc: Konstantin Ryabitsev, Vishal Verma

Add an option (and a config entry) for b4-send to include any git-notes
in the formatted patch after the '---' break.

Note that git-show with the notes option adds notes with a 'Notes:'
prefix, and with an indent. The convention for mailing lists is
typically to just include the notes after the break, without either of
the above. Do some regex munging to remove it.

Assisted by Claude Code.

Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
I've only tested this using b4 send --dry-run until now. This patch
should be the first time actually sending anything with the
modifications. This paragraph should be coming from git-notes, not from
the b4-prep cover letter as it can for a single patch series - but I've
left the cover letter empty.

 docs/config.rst    | 22 ++++++++++++++++++++++
 src/b4/__init__.py | 13 ++++++++++++-
 src/b4/command.py  |  4 ++++
 src/b4/ez.py       | 29 +++++++++++++++++++++--------
 4 files changed, 59 insertions(+), 9 deletions(-)

diff --git a/docs/config.rst b/docs/config.rst
index a1145af..8c7b3eb 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -437,6 +437,28 @@ Contributor-oriented settings
 
   Default: ``None``
 
+``b4.send-notes``
+  When set, includes git notes in the patches below the ``---`` line. This is
+  useful for adding per-patch changelog entries or other annotations that
+  should not be part of the permanent commit message. Notes are appended by
+  passing ``--notes`` to ``git show`` when generating patches. This can be
+  overridden on the command line with ``--notes`` or ``--no-notes`` flags to
+  ``b4 send``.
+
+  .. note::
+
+     Since git notes are keyed by commit hash, they are normally lost
+     when commits are rebased or amended (e.g. by ``b4 prep --edit-cover``).
+     To make git preserve notes across these operations, set the following
+     in your git configuration::
+
+       [notes]
+         rewriteRef = refs/notes/commits
+         rewrite.rebase = true
+         rewrite.amend = true
+
+  Default: ``no``
+
 ``b4.prep-perpatch-check-cmd`` (v0.14+)
   The command to use when running ``--check``. The command is run once for each
   patch to check. The patch file to check is piped through stdin. If this
diff --git a/src/b4/__init__.py b/src/b4/__init__.py
index 3d774f7..4309325 100644
--- a/src/b4/__init__.py
+++ b/src/b4/__init__.py
@@ -3670,7 +3670,8 @@ def git_range_to_patches(gitdir: Optional[str], start: str, end: str,
                          extrahdrs: Optional[List[Tuple[str, str]]] = None,
                          ignore_commits: Optional[Set[str]] = None,
                          limit_committer: Optional[str] = None,
-                         presubject: Optional[str] = None) -> List[Tuple[str, EmailMessage]]:
+                         presubject: Optional[str] = None,
+                         with_notes: bool = False) -> List[Tuple[str, EmailMessage]]:
     gitargs = ['rev-list', '--no-merges', '--reverse']
     if limit_committer:
         gitargs += ['-F', f'--committer={limit_committer}']
@@ -3694,6 +3695,8 @@ def git_range_to_patches(gitdir: Optional[str], start: str, end: str,
             '--encoding=utf-8',
             '--find-renames',
         ]
+        if with_notes:
+            showargs.append('--notes')
 
         if git_check_minimal_version("2.40"):
             showargs.append("--default-prefix")
@@ -3758,6 +3761,14 @@ def git_range_to_patches(gitdir: Optional[str], start: str, end: str,
         payload = msg.get_payload(decode=True)
         if isinstance(payload, bytes):
             payload = payload.decode()
+            if with_notes:
+                # git show --notes adds notes with a "Notes:" header and
+                # indentation. Strip that to match the mailing list
+                # convention of plain text after the "---" line.
+                payload = re.sub(
+                    r'\nNotes:\n((?:[ \t]+.+\n)+)',
+                    lambda m: '\n' + re.sub(r'^[ \t]+', '', m.group(1), flags=re.MULTILINE),
+                    payload)
             if inbodyhdrs:
                 payload = '\n'.join(inbodyhdrs) + '\n\n' + payload
             if gitver and not payload.find('\n-- \n') > 0:
diff --git a/src/b4/command.py b/src/b4/command.py
index 1f8b8f1..37873c9 100644
--- a/src/b4/command.py
+++ b/src/b4/command.py
@@ -397,6 +397,10 @@ def setup_parser() -> argparse.ArgumentParser:
                          help='Resend a previously sent version of the series')
     sp_send.add_argument('--no-sign', action='store_true', default=False,
                          help='Do not add the cryptographic attestation signature header')
+    sp_send.add_argument('--notes', dest='send_notes', action='store_true', default=None,
+                         help='Include git notes in the patches below the --- line')
+    sp_send.add_argument('--no-notes', dest='send_notes', action='store_false',
+                         help='Do not include git notes (overrides b4.send-notes config)')
     sp_send.add_argument('--use-web-endpoint', dest='send_web', action='store_true', default=False,
                          help="Force going through the web endpoint")
     ag_sendh = sp_send.add_argument_group('Web submission', 'Authenticate with the web submission endpoint')
diff --git a/src/b4/ez.py b/src/b4/ez.py
index 298e382..7e7bf5a 100644
--- a/src/b4/ez.py
+++ b/src/b4/ez.py
@@ -1567,7 +1567,7 @@ def rethread(patches: List[Tuple[str, EmailMessage]]) -> None:
 
 def get_prep_branch_as_patches(movefrom: bool = True, thread: bool = True, addtracking: bool = True,
                                prefixes: Optional[List[str]] = None, usebranch: Optional[str] = None,
-                               expandprereqs: bool = True,
+                               expandprereqs: bool = True, with_notes: bool = False,
                                ) -> Tuple[List[Tuple[str, str]], List[Tuple[str, str]], str, List[Tuple[str, EmailMessage]]]:
     cover, tracking = load_cover(strip_comments=True, usebranch=usebranch)
 
@@ -1604,7 +1604,8 @@ def get_prep_branch_as_patches(movefrom: bool = True, thread: bool = True, addtr
                                       seriests=seriests,
                                       mailfrom=mailfrom,
                                       ignore_commits=ignore_commits,
-                                      presubject=presubject)
+                                      presubject=presubject,
+                                      with_notes=with_notes)
 
     base_commit, _, _, _, shortlog, diffstat = get_series_details(start_commit=start_commit,
                                                                   usebranch=usebranch)
@@ -1767,7 +1768,8 @@ def get_prep_branch_as_patches(movefrom: bool = True, thread: bool = True, addtr
     return alltos, allccs, tag_msg, patches
 
 
-def get_sent_tag_as_patches(tagname: str, revision: int, presubject: str = None) \
+def get_sent_tag_as_patches(tagname: str, revision: int, presubject: str = None,
+                            with_notes: bool = False) \
         -> Tuple[List[Tuple[str, str]], List[Tuple[str, str]], List[Tuple[str, EmailMessage]]]:
     cover, base_commit, change_id = get_base_changeid_from_tag(tagname)
 
@@ -1784,7 +1786,8 @@ def get_sent_tag_as_patches(tagname: str, revision: int, presubject: str = None)
                                       msgid_tpt=msgid_tpt,
                                       seriests=seriests,
                                       mailfrom=mailfrom,
-                                      presubject=presubject)
+                                      presubject=presubject,
+                                      with_notes=with_notes)
 
     alltos, allccs, cbody = get_cover_dests(cbody)
     if len(patches) == 1:
@@ -1796,8 +1799,11 @@ def get_sent_tag_as_patches(tagname: str, revision: int, presubject: str = None)
 
 
 def format_patch(output_dir: str) -> None:
+    config = b4.get_main_config()
+    with_notes = config.get('send-notes', '').lower() in {'yes', 'true', 'y'}
     try:
-        _, _, _, patches = get_prep_branch_as_patches(thread=False, movefrom=False, addtracking=False)
+        _, _, _, patches = get_prep_branch_as_patches(thread=False, movefrom=False, addtracking=False,
+                                                      with_notes=with_notes)
     except RuntimeError as ex:
         logger.critical('CRITICAL: Failed to convert range to patches: %s', ex)
         sys.exit(1)
@@ -1916,6 +1922,11 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
 
     config = b4.get_main_config()
 
+    if cmdargs.send_notes is not None:
+        with_notes = cmdargs.send_notes
+    else:
+        with_notes = config.get('send-notes', '').lower() in {'yes', 'true', 'y'}
+
     tag_msg = None
     cl_msgid = None
     _, tracking = load_cover(strip_comments=True)
@@ -1942,8 +1953,9 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
         presubject = tracking['series'].get('presubject', None)
 
         try:
-            todests, ccdests, patches = get_sent_tag_as_patches(tagname, revision=revision, 
-                                                                presubject=presubject)
+            todests, ccdests, patches = get_sent_tag_as_patches(tagname, revision=revision,
+                                                                presubject=presubject,
+                                                                with_notes=with_notes)
         except RuntimeError as ex:
             logger.critical('CRITICAL: Failed to convert tag to patches: %s', ex)
             sys.exit(1)
@@ -1963,7 +1975,8 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
             prefixes = None
 
         try:
-            todests, ccdests, tag_msg, patches = get_prep_branch_as_patches(prefixes=prefixes)
+            todests, ccdests, tag_msg, patches = get_prep_branch_as_patches(prefixes=prefixes,
+                                                                            with_notes=with_notes)
         except RuntimeError as ex:
             logger.critical('CRITICAL: Failed to convert range to patches: %s', ex)
             sys.exit(1)

---
base-commit: 477734000555ffc24bf873952e40367deee26f17
change-id: 20260224-add-git-notes-1b1c3c5bda1f

Best regards,
--  
Vishal Verma <vishal.l.verma@intel.com>


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-02-25  4:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-25  1:01 [PATCH b4] b4: Add option to b4-send to include git-notes Vishal Verma
2026-02-25  2:54 ` Konstantin Ryabitsev
2026-02-25  3:46   ` Verma, Vishal L
2026-02-25  4:07     ` konstantin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox