hub.sr.ht/hubsrht/trailers.py

84 lines
2.7 KiB
Python

import re
from typing import List, Tuple
_git_generated_prefixes = (
"Signed-off-by: ",
"(cherry picked from commit ",
)
def commit_trailers(message: str) -> List[Tuple[str, str]]:
"""
Extract the trailers from a commit message. Return a list of pairs of
(name, value).
This borrows a large amount of logic from git core (trailer.c).
"""
lines = message.strip().splitlines()
# The first paragraph is the title and cannot be trailers
while lines and lines[0] != '':
del lines[0]
recognized_prefix = False
only_spaces = True
trailer_lines = non_trailer_lines = 0
possible_continuation_lines = 0
# Get the start of the trailers by looking starting from the end for a
# blank line before a set of non-blank lines that (i) are all trailers, or
# (ii) contains at least one Git-generated trailer and consists of at least
# 25% trailers.
i = len(lines) - 1
while i >= 0:
line = lines[i]
if not line.strip():
# blank line
if only_spaces:
i -= 1
continue
if recognized_prefix and trailer_lines * 3 >= non_trailer_lines:
i += 1
break
if trailer_lines > 0 and non_trailer_lines == 0:
i += 1
break
return []
only_spaces = False
if any(line.startswith(p) for p in _git_generated_prefixes):
trailer_lines += 1
possible_continuation_lines = 0
recognized_prefix = True
elif re.search(r"^[A-Za-z\d][A-Za-z\d-]*\s*:", line):
trailer_lines += 1
possible_continuation_lines = 0
elif line[0] in (" ", "\t"):
possible_continuation_lines += 1
else:
non_trailer_lines += 1 + possible_continuation_lines
possible_continuation_lines = 0
i -= 1
# Iterate over all remaining lines and collect trailer names and values.
# If a line does not match a trailer and starts with a space or tab, its
# contents are appended to the current trailer value.
trailers = []
name = value = None
for line in lines[i:]:
match = re.match(r"^([A-Za-z\d][A-Za-z\d-]*)\s*:\s*(.*)$", line)
if match:
if name is not None and value is not None:
trailers.append((name, value))
name = match[1]
value = match[2]
elif name is not None and value is not None and line[0] in (" ", "\t"):
# continuation line
value += "\n" + line
if name is not None and value is not None:
trailers.append((name, value))
return trailers