--- /dev/null
+#! /usr/bin/perl
+
+#################################################################
+# add_commit_links.pl -- add commit links to the release notes
+#
+# Copyright (c) 2024, PostgreSQL Global Development Group
+#
+# src/tools/add_commit_links.pl
+#################################################################
+
+#
+# This script adds commit links to the release notes.
+#
+# Usage: cd to top of source tree and issue
+# src/tools/add_commit_links.pl release_notes_file
+#
+# The script can add links for release note items that lack them, and update
+# those that have them. The script is sensitive to the release note file being
+# in a specific format:
+#
+# * File name contains the major version number preceded by a dash
+# and followed by a period
+# * Commit text is generated by src/tools/git_changelog
+# * SGML comments around commit text start in the first column
+# * The commit item title ends with an attribution that ends with
+# a closing parentheses
+# * previously added URL link text is unmodified
+# * a "
" follows the commit item title
+#
+# The major version number is used to select the commit hash for minor
+# releases. An error will be generated if valid commits are found but
+# no proper location for the commit links is found.
+
+use strict;
+use warnings FATAL => 'all';
+
+sub process_file
+{
+ my $file = shift;
+
+ my $in_comment = 0;
+ my $prev_line_ended_with_paren = 0;
+ my $prev_leading_space = '';
+ my $lineno = 0;
+
+ my @hashes = ();
+
+ my $tmpfile = $file . '.tmp';
+
+ # Get major version number from the file name.
+ $file =~ m/-(\d+)\./;
+ my $major_version = $1;
+
+ open(my $fh, '<', $file) || die "could not open file %s: $!\n", $file;
+ open(my $tfh, '>', $tmpfile) || die "could not open file %s: $!\n",
+ $tmpfile;
+
+ while (<$fh>)
+ {
+ $lineno++;
+
+ $in_comment = 1 if (m/^/);
+ }
+
+ close($fh);
+ close($tfh);
+
+ rename($tmpfile, $file) || die "could not rename %s to %s: $!\n",
+ $tmpfile,
+ $file;
+
+ return;
+}
+
+if (@ARGV == 0)
+{
+ printf(STDERR "Usage: %s release_notes_file [...]\n", $0);
+ exit(1);
+}
+
+for my $file (@ARGV)
+{
+ process_file($file);
+}