Skip to content

Commit bdf2079

Browse files
committed
releng: added gitlog_to_whatsnew script
1 parent dd85b87 commit bdf2079

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

releng/gitlog_to_whatsnew

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import subprocess
5+
import sys
6+
7+
8+
_RECORD_SEP = "\x1e" # record separator
9+
_FIELD_SEP = "\x1f" # unit separator
10+
11+
12+
def _run_git_log(rev_range: str) -> str:
13+
fmt = f"%H{_FIELD_SEP}%s{_FIELD_SEP}%b{_RECORD_SEP}"
14+
# Note: keep commit message body verbatim (%b); subject is %s
15+
cmd = [
16+
"git",
17+
"log",
18+
rev_range,
19+
"--reverse",
20+
f"--format={fmt}",
21+
]
22+
try:
23+
return subprocess.check_output(cmd, text=True)
24+
except subprocess.CalledProcessError as e:
25+
return e.output
26+
27+
28+
def _format_commit(subject: str, body: str, include_hash: bool, commit_hash: str) -> str:
29+
subject = subject.rstrip("\n")
30+
body = body.rstrip("\n")
31+
32+
header = subject
33+
if include_hash:
34+
header = f"{header} ({commit_hash[:12]})"
35+
36+
out_lines = [f"- {header}"]
37+
38+
if body.strip() != "":
39+
# Indent body verbatim under the bullet.
40+
# Keep empty lines as a bare indentation line.
41+
for line in body.splitlines():
42+
if line == "":
43+
out_lines.append(" ")
44+
else:
45+
out_lines.append(f" {line}")
46+
47+
return "\n".join(out_lines)
48+
49+
50+
def main(argv: list[str]) -> int:
51+
parser = argparse.ArgumentParser(
52+
description=(
53+
"Format git commit messages (subject+body) as WHATSNEW-style markdown bullets. "
54+
"Useful as raw material for release notes."
55+
)
56+
)
57+
parser.add_argument(
58+
"range",
59+
nargs="?",
60+
default="v1.4.2..HEAD",
61+
help="Git revision range to list, default: v1.4.2..HEAD",
62+
)
63+
parser.add_argument(
64+
"--include-hash",
65+
action="store_true",
66+
help="Append abbreviated commit hash to the bullet header",
67+
)
68+
parser.add_argument(
69+
"--separator",
70+
default="\n\n",
71+
help="Text to print between commits (default: blank line)",
72+
)
73+
74+
args = parser.parse_args(argv)
75+
76+
raw = _run_git_log(args.range)
77+
if raw is None:
78+
return 2
79+
80+
records = [r for r in raw.split(_RECORD_SEP) if r.strip() != ""]
81+
82+
formatted: list[str] = []
83+
for rec in records:
84+
parts = rec.split(_FIELD_SEP)
85+
if len(parts) < 3:
86+
continue
87+
commit_hash = parts[0]
88+
subject = parts[1]
89+
body = _FIELD_SEP.join(parts[2:])
90+
formatted.append(_format_commit(subject, body, args.include_hash, commit_hash))
91+
92+
sys.stdout.write(args.separator.join(formatted))
93+
if formatted:
94+
sys.stdout.write("\n")
95+
96+
return 0
97+
98+
99+
if __name__ == "__main__":
100+
raise SystemExit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)