blob: e9b5940c0f2837895ffd88b0f540031c538d5436 (
plain)
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# (c) 2025, Sasha Levin <sashal@kernel.org>
usage() {
echo "Usage: $(basename "$0") [--selftest] [--force] <commit-id> [commit-subject]"
echo "Resolves a short git commit ID to its full SHA-1 hash, particularly useful for fixing references in commit messages."
echo ""
echo "Arguments:"
echo " --selftest Run self-tests"
echo " --force Try to find commit by subject if ID lookup fails"
echo " commit-id Short git commit ID to resolve"
echo " commit-subject Optional commit subject to help resolve between multiple matches"
exit 1
}
# Convert subject with ellipsis to grep pattern
convert_to_grep_pattern() {
local subject="$1"
# First escape ALL regex special characters
local escaped_subject
escaped_subject=$(printf '%s\n' "$subject" | sed 's/[[\.*^$()+?{}|]/\\&/g')
# Also escape colons, parentheses, and hyphens as they are special in our context
escaped_subject=$(echo "$escaped_subject" | sed 's/[:-]/\\&/g')
# Then convert escaped ... sequence to .*?
escaped_subject=$(echo "$escaped_subject" | sed 's/\\\.\\\.\\\./.*?/g')
echo "^${escaped_subject}$"
}
git_resolve_commit() {
local force=0
if [ "$1" = "--force" ]; then
force=1
shift
fi
# Split input into commit ID and subject
local input="$*"
local commit_id="${input%% *}"
local subject=""
# Extract subject if present (everything after the first space)
if [[ "$input" == *" "* ]]; then
subject="${input#* }"
# Strip the ("...") quotes if present
subject="${subject#*(\"}"
subject="${subject%\")*}"
fi
# Get all possible matching commit IDs
local matches
readarray -t matches < <(git rev-parse --disambiguate="$commit_id" 2>/dev/null)
# Return immediately if we have exactly one match
if [ ${#matches[@]} -eq 1 ]; then
echo "${matches[0]}"
return 0
fi
# If no matches and not in force mode, return failure
if [ ${#matches[@]} -eq 0 ] && [ $force -eq 0 ]; then
return 1
fi
# If we have a subject, try to find a match with that subject
if [ -n "$subject" ]; then
# Convert subject with possible ellipsis to grep pattern
local grep_pattern
grep_pattern=$(convert_to_grep_pattern "$subject")
# In force mode with no ID matches, use git log --grep directly
if [ ${#matches[@]} -eq 0 ] && [ $force -eq 1 ]; then
# Use git log to search, but filter to ensure subject matches exactly
local match
match=$(git log --format="%H %s" --grep="$grep_pattern" --perl-regexp -10 | \
while read -r hash subject; do
if echo "$subject" | grep -qP "$grep_pattern"; then
echo "$hash"
break
fi
done)
if [ -n "$match" ]; then
echo "$match"
return 0
fi
else
# Normal subject matching for existing matches
for match in "${matches[@]}"; do
if git log -1 --format="%s" "$match" | grep -qP "$grep_pattern"; then
echo "$match"
return 0
fi
done
fi
fi
# No match found
return 1
}
run_selftest() {
local test_cases=(
'00250b5 ("MAINTAINERS: add new Rockchip SoC list")'
'0037727 ("KVM: selftests: Convert xen_shinfo_test away from VCPU_ID")'
'ffef737 ("net/tls: Fix skb memory leak when running kTLS traffic")'
'd3d7 ("cifs: Improve guard for excluding $LXDEV xattr")'
'dbef ("Rename .data.once to .data..once to fix resetting WARN*_ONCE")'
'12345678' # Non-existent commit
'12345 ("I'\''m a dummy commit")' # Valid prefix but wrong subject
'--force 99999999 ("net/tls: Fix skb memory leak when running kTLS traffic")' # Force mode with non-existent ID but valid subject
'83be ("firmware: ... auto-update: fix poll_complete() ... errors")' # Wildcard test
'--force 999999999999 ("firmware: ... auto-update: fix poll_complete() ... errors")' # Force mode wildcard test
)
local expected=(
"00250b529313d6262bb0ebbd6bdf0a88c809f6f0"
"0037727b3989c3fe1929c89a9a1dfe289ad86f58"
"ffef737fd0372ca462b5be3e7a592a8929a82752"
"d3d797e326533794c3f707ce1761da7a8895458c"
"dbefa1f31a91670c9e7dac9b559625336206466f"
"" # Expect empty output for non-existent commit
"" # Expect empty output for wrong subject
"ffef737fd0372ca462b5be3e7a592a8929a82752" # Should find commit by subject in force mode
"83beece5aff75879bdfc6df8ba84ea88fd93050e" # Wildcard test
"83beece5aff75879bdfc6df8ba84ea88fd93050e" # Force mode wildcard test
)
local expected_exit_codes=(
0
0
0
0
0
1 # Expect failure for non-existent commit
1 # Expect failure for wrong subject
0 # Should succeed in force mode
0 # Should succeed with wildcard
0 # Should succeed with force mode and wildcard
)
local failed=0
echo "Running self-tests..."
for i in "${!test_cases[@]}"; do
# Capture both output and exit code
local result
result=$(git_resolve_commit ${test_cases[$i]}) # Removed quotes to allow --force to be parsed
local exit_code=$?
# Check both output and exit code
if [ "$result" != "${expected[$i]}" ] || [ $exit_code != ${expected_exit_codes[$i]} ]; then
echo "Test case $((i+1)) FAILED"
echo "Input: ${test_cases[$i]}"
echo "Expected output: '${expected[$i]}'"
echo "Got output: '$result'"
echo "Expected exit code: ${expected_exit_codes[$i]}"
echo "Got exit code: $exit_code"
failed=1
else
echo "Test case $((i+1)) PASSED"
fi
done
if [ $failed -eq 0 ]; then
echo "All tests passed!"
exit 0
else
echo "Some tests failed!"
exit 1
fi
}
# Check for selftest
if [ "$1" = "--selftest" ]; then
run_selftest
exit $?
fi
# Handle --force flag
force=""
if [ "$1" = "--force" ]; then
force="--force"
shift
fi
# Verify arguments
if [ $# -eq 0 ]; then
usage
fi
# Skip validation in force mode
if [ -z "$force" ]; then
# Validate that the first argument matches at least one git commit
if [ "$(git rev-parse --disambiguate="$1" 2>/dev/null | wc -l)" -eq 0 ]; then
echo "Error: '$1' does not match any git commit"
exit 1
fi
fi
git_resolve_commit $force "$@"
exit $?
|