Repository navigation
Expand file tree
/
Copy pathadd-commit-pr-id
More file actions
executable file
·159 lines (129 loc) · 5.33 KB
/
Copy pathadd-commit-pr-id
File metadata and controls
executable file
·159 lines (129 loc) · 5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env bash
#
# Copyright (C) 2026 Vates SAS
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ==============================================================================
# Make the title of every commit of the current branch end with ` (#<pr-number>)`.
# Commits whose title already ends with this ID only are left untouched, so the script can be run
# several times.
#
# Usage: add-commit-pr-id [--force] <base-sha> <head-sha> <pr-number>
#
# - `<base-sha>`: base of the pull request to check and modify.
# - `<head-sha>`: head of the pull request to check and modify.
# - `<pr-number>`: number of the pull request.
# - `--force`: replace the existing pull request IDs of the titles.
#
# Every trailing ` (#<digits>)` of a title is considered to be a pull request ID. Without `--force`,
# the commits that already end with another ID are ignored (and listed), the others are rewritten:
# an ignored commit is probably in the range because the base is wrong, or the range contains
# commits of other pull requests.
# ==============================================================================
set -euo pipefail
SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
readonly SCRIPT_DIR
readonly AMEND_SCRIPT_PATH="${SCRIPT_DIR}/amend-commit-pr-id"
source "${SCRIPT_DIR}/lib/git.sh"
source "${SCRIPT_DIR}/lib/pr-id.sh"
# ==============================================================================
readonly FORCE_OPTION="--force"
FORCE=false
ARGS=()
for arg in "$@"; do
if [ "$arg" = "$FORCE_OPTION" ]; then
FORCE=true
else
ARGS+=("$arg")
fi
done
readonly FORCE
readonly ARGS
if [ "${#ARGS[@]}" -ne 3 ]; then
echo "Usage: $0 [${FORCE_OPTION}] <base-sha> <head-sha> <pr-number>" >&2
exit 2
fi
BASE_SHA=$(resolve_commit "${ARGS[0]}")
readonly BASE_SHA
HEAD_SHA=$(resolve_commit "${ARGS[1]}")
readonly HEAD_SHA
readonly PR_NUMBER=${ARGS[2]}
if ! is_pr_number "$PR_NUMBER"; then
echo "Invalid pull request number: \`${PR_NUMBER}\`." >&2
exit 2
fi
# ------------------------------------------------------------------------------
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "The working tree has uncommitted changes: commit or stash them first." >&2
exit 1
fi
if [ "$(git rev-parse HEAD)" != "$HEAD_SHA" ]; then
echo "\`HEAD\` is not \`${HEAD_SHA}\`: checkout the head of the pull request first." >&2
exit 1
fi
# ------------------------------------------------------------------------------
if ! MERGE_BASE_SHA=$(git merge-base "$BASE_SHA" "$HEAD_SHA"); then
echo "No common ancestor between \`${BASE_SHA}\` and \`${HEAD_SHA}\`." >&2
exit 2
fi
readonly MERGE_BASE_SHA
readonly COMMIT_RANGE="${MERGE_BASE_SHA}..${HEAD_SHA}"
if [ -n "$(git rev-list --merges "${MERGE_BASE_SHA}..${HEAD_SHA}")" ]; then
echo "Merge commits found in \`${COMMIT_RANGE}\`: this script only supports linear history." >&2
exit 1
fi
mapfile -t hashes < <(git log --format=%H "${MERGE_BASE_SHA}..${HEAD_SHA}")
# ------------------------------------------------------------------------------
PR_ID_SUFFIX=$(get_pr_id_suffix "$PR_NUMBER")
readonly PR_ID_SUFFIX
to_rewrite=0
to_ignore=0
for hash in "${hashes[@]}"; do
subject=$(git log -1 --format=%s "$hash")
if [ "$(with_pr_id "$subject" "$PR_ID_SUFFIX")" = "$subject" ]; then
continue
fi
# The title already ends with a pull request ID other than the requested one.
if has_pr_id "$subject" && ! $FORCE; then
to_ignore=$((to_ignore + 1))
echo "Commit ${hash} ignored, it already has another pull request ID: \"${subject}\"." >&2
continue
fi
to_rewrite=$((to_rewrite + 1))
done
if [ "$to_rewrite" -eq 0 ]; then
if [ "$to_ignore" -eq 0 ]; then
echo "Nothing to do: every commit of \`${COMMIT_RANGE}\` already ends with \"${PR_ID_SUFFIX}\"."
else
echo "Nothing to do: ${to_ignore} commit(s) of \`${COMMIT_RANGE}\` ignored with another pull request ID, the others already end with \"${PR_ID_SUFFIX}\"."
fi
exit 0
fi
# ------------------------------------------------------------------------------
exec_options=()
if $FORCE; then
exec_options+=("$FORCE_OPTION")
fi
readonly PREVIOUS_HEAD=$HEAD_SHA
printf -v EXEC_COMMAND '%q ' "$AMEND_SCRIPT_PATH" "${exec_options[@]}" "$PR_ID_SUFFIX"
readonly EXEC_COMMAND
if ! rebase_output=$(GIT_SEQUENCE_EDITOR=: git rebase --interactive --no-autosquash --exec "$EXEC_COMMAND" "$MERGE_BASE_SHA" 2>&1); then
echo "$rebase_output" >&2
git rebase --abort || true
echo "Rewrite failed, branch restored (previous HEAD: ${PREVIOUS_HEAD})." >&2
exit 1
fi
echo "${to_rewrite} commit title(s) updated with \"${PR_ID_SUFFIX}\" (previous HEAD: ${PREVIOUS_HEAD})."
if [ "$to_ignore" -ne 0 ]; then
echo "${to_ignore} commit(s) ignored because they already have another pull request ID."
fi