public inbox for tools@linux.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] b4: support send-email aliases
@ 2025-10-09 17:01 Jacob Keller
  2025-10-09 18:24 ` Konstantin Ryabitsev
  0 siblings, 1 reply; 8+ messages in thread
From: Jacob Keller @ 2025-10-09 17:01 UTC (permalink / raw)
  To: Kernel.org Tools, Konstantin Ryabitsev; +Cc: Jacob Keller

git send-email has supported the --translate-aliases option since v2.47, as
commit c038a6f1d748 ("send-email: teach git send-email option to translate
aliases", 2024-08-17)

The --translate-aliases option allows asking git send-email to convert an
alias into its real address according to an alias file in varying formats.
This enables using the same aliasing behavior as git, without needing to
implement separate parsing logic within b4.

To avoid breaking any users running older versions of git, the translation
is enabled only if the git version is new enough to contain the
translate-aliases option. The support was added by git commit c038a6f1d748
("send-email: teach git send-email option to translate aliases"). A quick
git describe shows this was released in git v2.47.

Make use of this functionality in cleanup_email_addrs(). To minimize
overhead, we first split the addresses into qualified and unqualified
addresses by the presence of the '@' symbol. Aliases won't be proper email
addresses.

The --translate-aliases mode of git send-email takes each alias on a
separate line and translates it in the output. Conveniently, if an input
can't be translated, it is returned unchanged.

Pass the unqualified addresses in as a string joined by newlines. Split the
standard output into translated addresses using email.utils.getaddresses().
Log what we translate each alias into, then recreate the new list of
addresses by combining the original set of qualified addresses with the
newly translated addresses.

This implementation minimizes overhead by only requiring a single git
send-email process, and by only translating addresses which are not yet
fully qualified.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
Git added support for exposing email address translation via git send-email
--translate-aliases in version 2.47. Add support to b4 to enable this
functionality.

Addresses are translated automatically if the git version is detected as
v2.47 or later. This is simple and avoids burdening uses with needing to
configure both git and b4.

This version minimizes overhead compared to previous versions by only using
a single git subprocess, and by only translating addresses once. Addresses
are split based on whether they are "qualified" with an '@' already or not.
It is assumed that no alias will contain the '@' character. This way, after
translating once, the address won't be passed in to git send-email again.
Further, non-qualified addresses are discarded by cleanup_email_addrs after
a translation attempt.

In my limited testing, this version has significantly less overhead than
the previous version, and additionally requires no configuration. Since we
check the git version, and since we don't attempt to translate any
qualified addresses, it should not break any existing users.
---
Changes in v3:
- Drop b4.send-translate-aliases config option
- Check git version before enabling
- Rewrite conversion algorithm to use a single git subprocess
- Only translate non-qualified addresses
- Link to v2: https://patch.msgid.link/20250709-jk-email-alias-support-v2-1-b0339905964e@intel.com

Changes in v2:
- Add b4.send-translate-aliases config option
- Link to v1: https://patch.msgid.link/20250627-jk-email-alias-support-v1-1-b79aa627cd71@intel.com
---
 src/b4/__init__.py | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/src/b4/__init__.py b/src/b4/__init__.py
index 20bce70ce41d..e8d614978090 100644
--- a/src/b4/__init__.py
+++ b/src/b4/__init__.py
@@ -4265,6 +4265,38 @@ def get_excluded_addrs() -> Set[str]:
 def cleanup_email_addrs(addresses: List[Tuple[str, str]], excludes: Set[str],
                         gitdir: Optional[str]) -> List[Tuple[str, str]]:
     global MAILMAP_INFO
+
+    # Translate aliases if support is available
+    if git_check_minimal_version("2.47"):
+        logger.debug('translating aliases via git send-email')
+
+        qual_addrs = []
+        unqual_addrs = []
+
+        # Assume aliases won't have '@' and are always unqualified
+        for e in list(addresses):
+            qual_addrs.append(e) if '@' in e[1] else unqual_addrs.append(e[1])
+
+        # Pass unqualified addresses to git send-email
+        if len(unqual_addrs) > 0:
+            data = '\n'.join(unqual_addrs).encode('utf-8')
+            args = ['send-email', '--translate-aliases']
+            ecode, out = git_run_command(gitdir,
+                                        ['send-email', '--translate-aliases'],
+                                        stdin=data)
+            if ecode == 0:
+                translated_addrs = email.utils.getaddresses(out.strip().splitlines())
+
+                for alias, entry in zip(unqual_addrs, translated_addrs):
+                    if alias != entry[1]:
+                        logger.debug('translated alias %s to qualified address "%s"',
+                                    alias, email.utils.formataddr(entry))
+
+                addresses = qual_addrs + translated_addrs
+            else:
+                logger.debug('git send-email --translate-aliases failed with exit code %s',
+                             ecode)
+
     for entry in list(addresses):
         # Only qualified addresses, please
         if not len(entry[1].strip()) or '@' not in entry[1]:

---
base-commit: b87af2e0dc6228c52ebd1c9df7b4c13aa4093f05
change-id: 20250627-jk-email-alias-support-d66b261cb5d9

Best regards,
--  
Jacob Keller <jacob.e.keller@intel.com>


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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 17:01 [PATCH v3] b4: support send-email aliases Jacob Keller
@ 2025-10-09 18:24 ` Konstantin Ryabitsev
  2025-10-09 19:01   ` Jacob Keller
                     ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Konstantin Ryabitsev @ 2025-10-09 18:24 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Kernel.org Tools

On Thu, Oct 09, 2025 at 10:01:09AM -0700, Jacob Keller wrote:
> This implementation minimizes overhead by only requiring a single git
> send-email process, and by only translating addresses which are not yet
> fully qualified.

Great stuff, thank you for working on this! I have a few improvement
suggestions below.

> +    # Translate aliases if support is available
> +    if git_check_minimal_version("2.47"):
> +        logger.debug('translating aliases via git send-email')
> +
> +        qual_addrs = []
> +        unqual_addrs = []

We're trying to be type-clean, so these should be:

            unqual_addrs: Set[str] = set()

We don't need qual_addrs (see below) and we can use set() here that buys us
uniqueness by default.

> +
> +        # Assume aliases won't have '@' and are always unqualified
> +        for e in list(addresses):
> +            qual_addrs.append(e) if '@' in e[1] else unqual_addrs.append(e[1])

Two things here:

1. we don't need to wrap addresses inside another list(), because in this loop
   we're not modifying the addresses list itself. We only do it further on
   because in that loop we do modify the addresses list, so we have to make a
   copy of it first. So, what you want is just:

        for e in addresses:
            if e[1] not in MAILMAP_INFO and '@' not in e[1]:
                unqual_addrs.add(e[1])

   I'll explain the MAILMAP_INFO check a bit further down.

> +
> +        # Pass unqualified addresses to git send-email
> +        if len(unqual_addrs) > 0:

You can just rely on implicit booleanness of python and `if unqual_addrs:`
here.

> +            data = '\n'.join(unqual_addrs).encode('utf-8')
> +            args = ['send-email', '--translate-aliases']
> +            ecode, out = git_run_command(gitdir,
> +                                        ['send-email', '--translate-aliases'],
> +                                        stdin=data)
> +            if ecode == 0:
> +                translated_addrs = email.utils.getaddresses(out.strip().splitlines())
> +
> +                for alias, entry in zip(unqual_addrs, translated_addrs):

Clever use of zip()!

> +                    if alias != entry[1]:
> +                        logger.debug('translated alias %s to qualified address "%s"',
> +                                    alias, email.utils.formataddr(entry))

Let's not bother with formataddr in a debug call, because it will get called
every time even if we're not in debug mode. Just put entry[1] here, because we
really just care to know about the address, not the name.

> +
> +                addresses = qual_addrs + translated_addrs

Let's not do it this way. We have MAILMAP_INFO, which is a global mapping var
we're keeping around for the lifetime of b4 command run. It exists
specifically for replacing email addresses, so how about we do this whole
chunk as follows:

                        if alias != entry[1]:
                            logger.debug(...)
                            # Stick it into MAILMAP_INFO so we cache this lookup
                            MAILMAP_INFO[alias] = entry
                        else:
                            logger.debug('"%s" is not a known alias', alias)
                            # This saves us from having to look it up again
                            MAILMAP_INFO[alias] = None

This has the following benefits:

1. If the same alias is used in 20 patches, we're only looking it up once.
2. It'll be automatically used in the following block of code without needing
   to modify the addresses list here.


Cheers,
-K

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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 18:24 ` Konstantin Ryabitsev
@ 2025-10-09 19:01   ` Jacob Keller
  2025-10-09 19:49   ` Jacob Keller
  2025-10-09 20:22   ` Jacob Keller
  2 siblings, 0 replies; 8+ messages in thread
From: Jacob Keller @ 2025-10-09 19:01 UTC (permalink / raw)
  To: Konstantin Ryabitsev; +Cc: Kernel.org Tools


[-- Attachment #1.1: Type: text/plain, Size: 463 bytes --]



On 10/9/2025 11:24 AM, Konstantin Ryabitsev wrote:
> On Thu, Oct 09, 2025 at 10:01:09AM -0700, Jacob Keller wrote:
>> This implementation minimizes overhead by only requiring a single git
>> send-email process, and by only translating addresses which are not yet
>> fully qualified.
> 
> Great stuff, thank you for working on this! I have a few improvement
> suggestions below.
> 

Great suggestions! I'll include this into a v4 and resubmit soon.

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]

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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 18:24 ` Konstantin Ryabitsev
  2025-10-09 19:01   ` Jacob Keller
@ 2025-10-09 19:49   ` Jacob Keller
  2025-10-09 20:21     ` Konstantin Ryabitsev
  2025-10-09 20:22   ` Jacob Keller
  2 siblings, 1 reply; 8+ messages in thread
From: Jacob Keller @ 2025-10-09 19:49 UTC (permalink / raw)
  To: Konstantin Ryabitsev; +Cc: Kernel.org Tools


[-- Attachment #1.1: Type: text/plain, Size: 1240 bytes --]



On 10/9/2025 11:24 AM, Konstantin Ryabitsev wrote:
> 
> Let's not do it this way. We have MAILMAP_INFO, which is a global mapping var
> we're keeping around for the lifetime of b4 command run. It exists
> specifically for replacing email addresses, so how about we do this whole
> chunk as follows:
> 
>                         if alias != entry[1]:
>                             logger.debug(...)
>                             # Stick it into MAILMAP_INFO so we cache this lookup
>                             MAILMAP_INFO[alias] = entry
>                         else:
>                             logger.debug('"%s" is not a known alias', alias)
>                             # This saves us from having to look it up again
>                             MAILMAP_INFO[alias] = None
> 
> This has the following benefits:
> 
> 1. If the same alias is used in 20 patches, we're only looking it up once.
> 2. It'll be automatically used in the following block of code without needing
>    to modify the addresses list here.
> 

One other issue is that this means if you have an alias, it will not be
covered by mailmap.

I'm wondering if what we want is a separate ALIAS_INFO instead?

> 
> Cheers,
> -K


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]

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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 19:49   ` Jacob Keller
@ 2025-10-09 20:21     ` Konstantin Ryabitsev
  2025-10-09 22:47       ` Jacob Keller
  2025-10-09 22:47       ` Jacob Keller
  0 siblings, 2 replies; 8+ messages in thread
From: Konstantin Ryabitsev @ 2025-10-09 20:21 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Kernel.org Tools

On Thu, Oct 09, 2025 at 12:49:07PM -0700, Jacob Keller wrote:
> > This has the following benefits:
> > 
> > 1. If the same alias is used in 20 patches, we're only looking it up once.
> > 2. It'll be automatically used in the following block of code without needing
> >    to modify the addresses list here.
> > 
> 
> One other issue is that this means if you have an alias, it will not be
> covered by mailmap.
> 
> I'm wondering if what we want is a separate ALIAS_INFO instead?

Ah, hm, you're right.

Yes, go ahead and make a new global caching map. It's very unlikely to be
more than a few entries large, so the impact should be minimal.

Then we do want to run through the list twice:
1. first run replacing aliases (new loop)
2. second run checking against mailmap (existing loop)

-K

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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 18:24 ` Konstantin Ryabitsev
  2025-10-09 19:01   ` Jacob Keller
  2025-10-09 19:49   ` Jacob Keller
@ 2025-10-09 20:22   ` Jacob Keller
  2 siblings, 0 replies; 8+ messages in thread
From: Jacob Keller @ 2025-10-09 20:22 UTC (permalink / raw)
  To: Konstantin Ryabitsev; +Cc: Kernel.org Tools


[-- Attachment #1.1: Type: text/plain, Size: 2244 bytes --]



On 10/9/2025 11:24 AM, Konstantin Ryabitsev wrote:
> On Thu, Oct 09, 2025 at 10:01:09AM -0700, Jacob Keller wrote:
>> This implementation minimizes overhead by only requiring a single git
>> send-email process, and by only translating addresses which are not yet
>> fully qualified.
> 
> Great stuff, thank you for working on this! I have a few improvement
> suggestions below.
> 
>> +    # Translate aliases if support is available
>> +    if git_check_minimal_version("2.47"):
>> +        logger.debug('translating aliases via git send-email')
>> +
>> +        qual_addrs = []
>> +        unqual_addrs = []
> 
> We're trying to be type-clean, so these should be:
> 
>             unqual_addrs: Set[str] = set()
> 

Unfortunately, set is not ordered which breaks the zip I used below. I
considered some options, but I think this can stay a list and we can
either use an OrderedDict or just check for duplicate entries on insert.

> 
> Let's not do it this way. We have MAILMAP_INFO, which is a global mapping var
> we're keeping around for the lifetime of b4 command run. It exists
> specifically for replacing email addresses, so how about we do this whole
> chunk as follows:
> 
>                         if alias != entry[1]:
>                             logger.debug(...)
>                             # Stick it into MAILMAP_INFO so we cache this lookup
>                             MAILMAP_INFO[alias] = entry
>                         else:
>                             logger.debug('"%s" is not a known alias', alias)
>                             # This saves us from having to look it up again
>                             MAILMAP_INFO[alias] = None
> 
> This has the following benefits:
> 
> 1. If the same alias is used in 20 patches, we're only looking it up once.
> 2. It'll be automatically used in the following block of code without needing
>    to modify the addresses list here.
> 

One issue with this, is we'll also need to modify the check for
unqualified addresses in the following loop. Otherwise our addresses
will immediately get discarded before we find them in MAILMAP_INFO. I
think I got that figured out though.

Thanks,
Jake

> 
> Cheers,
> -K


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]

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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 20:21     ` Konstantin Ryabitsev
  2025-10-09 22:47       ` Jacob Keller
@ 2025-10-09 22:47       ` Jacob Keller
  1 sibling, 0 replies; 8+ messages in thread
From: Jacob Keller @ 2025-10-09 22:47 UTC (permalink / raw)
  To: Konstantin Ryabitsev; +Cc: Kernel.org Tools


[-- Attachment #1.1: Type: text/plain, Size: 1177 bytes --]



On 10/9/2025 1:21 PM, Konstantin Ryabitsev wrote:
> On Thu, Oct 09, 2025 at 12:49:07PM -0700, Jacob Keller wrote:
>>> This has the following benefits:
>>>
>>> 1. If the same alias is used in 20 patches, we're only looking it up once.
>>> 2. It'll be automatically used in the following block of code without needing
>>>    to modify the addresses list here.
>>>
>>
>> One other issue is that this means if you have an alias, it will not be
>> covered by mailmap.
>>
>> I'm wondering if what we want is a separate ALIAS_INFO instead?
> 
> Ah, hm, you're right.
> 
> Yes, go ahead and make a new global caching map. It's very unlikely to be
> more than a few entries large, so the impact should be minimal.
> 

Right. It should only ever really be the number of aliases + potentially
any unintentional unqualified addresses that aren't proper aliases. Its
not even the size of every alias in your aliases file since we only load
ones which get matched.

> Then we do want to run through the list twice:
> 1. first run replacing aliases (new loop)
> 2. second run checking against mailmap (existing loop)
> 
> -K

Yep, that's what I did in v4.

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]

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

* Re: [PATCH v3] b4: support send-email aliases
  2025-10-09 20:21     ` Konstantin Ryabitsev
@ 2025-10-09 22:47       ` Jacob Keller
  2025-10-09 22:47       ` Jacob Keller
  1 sibling, 0 replies; 8+ messages in thread
From: Jacob Keller @ 2025-10-09 22:47 UTC (permalink / raw)
  To: Konstantin Ryabitsev; +Cc: Kernel.org Tools


[-- Attachment #1.1: Type: text/plain, Size: 1177 bytes --]



On 10/9/2025 1:21 PM, Konstantin Ryabitsev wrote:
> On Thu, Oct 09, 2025 at 12:49:07PM -0700, Jacob Keller wrote:
>>> This has the following benefits:
>>>
>>> 1. If the same alias is used in 20 patches, we're only looking it up once.
>>> 2. It'll be automatically used in the following block of code without needing
>>>    to modify the addresses list here.
>>>
>>
>> One other issue is that this means if you have an alias, it will not be
>> covered by mailmap.
>>
>> I'm wondering if what we want is a separate ALIAS_INFO instead?
> 
> Ah, hm, you're right.
> 
> Yes, go ahead and make a new global caching map. It's very unlikely to be
> more than a few entries large, so the impact should be minimal.
> 

Right. It should only ever really be the number of aliases + potentially
any unintentional unqualified addresses that aren't proper aliases. Its
not even the size of every alias in your aliases file since we only load
ones which get matched.

> Then we do want to run through the list twice:
> 1. first run replacing aliases (new loop)
> 2. second run checking against mailmap (existing loop)
> 
> -K

Yep, that's what I did in v4.

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]

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

end of thread, other threads:[~2025-10-09 22:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-09 17:01 [PATCH v3] b4: support send-email aliases Jacob Keller
2025-10-09 18:24 ` Konstantin Ryabitsev
2025-10-09 19:01   ` Jacob Keller
2025-10-09 19:49   ` Jacob Keller
2025-10-09 20:21     ` Konstantin Ryabitsev
2025-10-09 22:47       ` Jacob Keller
2025-10-09 22:47       ` Jacob Keller
2025-10-09 20:22   ` Jacob Keller

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