2626import os
2727from io import BytesIO
2828import logging
29+ from collections import defaultdict
2930
3031
3132# typing ------------------------------------------------------------------
@@ -335,8 +336,72 @@ def stats(self) -> Stats:
335336 return Stats ._list_from_string (self .repo , text )
336337
337338 @property
338- def trailers (self ) -> Dict :
339- """Get the trailers of the message as dictionary
339+ def trailers (self ) -> Dict [str , str ]:
340+ """Get the trailers of the message as a dictionary
341+
342+ :note: This property is deprecated, please use either ``Commit.trailers_list`` or ``Commit.trailers_dict``.
343+
344+ :return:
345+ Dictionary containing whitespace stripped trailer information.
346+ Only contains the latest instance of each trailer key.
347+ """
348+ return {
349+ k : v [0 ] for k , v in self .trailers_dict .items ()
350+ }
351+
352+ @property
353+ def trailers_list (self ) -> List [Tuple [str , str ]]:
354+ """Get the trailers of the message as a list
355+
356+ Git messages can contain trailer information that are similar to RFC 822
357+ e-mail headers (see: https://git-scm.com/docs/git-interpret-trailers).
358+
359+ This functions calls ``git interpret-trailers --parse`` onto the message
360+ to extract the trailer information, returns the raw trailer data as a list.
361+
362+ Valid message with trailer::
363+
364+ Subject line
365+
366+ some body information
367+
368+ another information
369+
370+ key1: value1.1
371+ key1: value1.2
372+ key2 : value 2 with inner spaces
373+
374+
375+ Returned list will look like this::
376+
377+ [
378+ ("key1", "value1.1"),
379+ ("key1", "value1.2"),
380+ ("key2", "value 2 with inner spaces"),
381+ ]
382+
383+
384+ :return:
385+ List containing key-value tuples of whitespace stripped trailer information.
386+ """
387+ cmd = ["git" , "interpret-trailers" , "--parse" ]
388+ proc : Git .AutoInterrupt = self .repo .git .execute (cmd , as_process = True , istream = PIPE ) # type: ignore
389+ trailer : str = proc .communicate (str (self .message ).encode ())[0 ].decode ("utf8" )
390+ trailer = trailer .strip ()
391+
392+ if not trailer :
393+ return []
394+
395+ trailer_list = []
396+ for t in trailer .split ("\n " ):
397+ key , val = t .split (":" , 1 )
398+ trailer_list .append ((key .strip (), val .strip ()))
399+
400+ return trailer_list
401+
402+ @property
403+ def trailers_dict (self ) -> Dict [str , List [str ]]:
404+ """Get the trailers of the message as a dictionary
340405
341406 Git messages can contain trailer information that are similar to RFC 822
342407 e-mail headers (see: https://git-scm.com/docs/git-interpret-trailers).
@@ -345,42 +410,35 @@ def trailers(self) -> Dict:
345410 to extract the trailer information. The key value pairs are stripped of
346411 leading and trailing whitespaces before they get saved into a dictionary.
347412
348- Valid message with trailer:
349-
350- .. code-block::
413+ Valid message with trailer::
351414
352415 Subject line
353416
354417 some body information
355418
356419 another information
357420
358- key1: value1
421+ key1: value1.1
422+ key1: value1.2
359423 key2 : value 2 with inner spaces
360424
361- dictionary will look like this:
362425
363- .. code-block ::
426+ Returned dictionary will look like this ::
364427
365428 {
366- "key1": "value1" ,
367- "key2": "value 2 with inner spaces"
429+ "key1": [ "value1.1", "value1.2"] ,
430+ "key2": [ "value 2 with inner spaces"],
368431 }
369432
370- :return: Dictionary containing whitespace stripped trailer information
371433
434+ :return:
435+ Dictionary containing whitespace stripped trailer information.
436+ Mapping trailer keys to a list of their corresponding values.
372437 """
373- d = {}
374- cmd = ["git" , "interpret-trailers" , "--parse" ]
375- proc : Git .AutoInterrupt = self .repo .git .execute (cmd , as_process = True , istream = PIPE ) # type: ignore
376- trailer : str = proc .communicate (str (self .message ).encode ())[0 ].decode ()
377- if trailer .endswith ("\n " ):
378- trailer = trailer [0 :- 1 ]
379- if trailer != "" :
380- for line in trailer .split ("\n " ):
381- key , value = line .split (":" , 1 )
382- d [key .strip ()] = value .strip ()
383- return d
438+ d = defaultdict (list )
439+ for key , val in self .trailers_list :
440+ d [key ].append (val )
441+ return dict (d )
384442
385443 @classmethod
386444 def _iter_from_process_or_stream (cls , repo : "Repo" , proc_or_stream : Union [Popen , IO ]) -> Iterator ["Commit" ]:
0 commit comments