Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# (c) 2025, Sasha Levin <sashal@kernel.org>
4
5usage() {
6 echo "Usage: $(basename "$0") [--selftest] [--force] <commit-id> [commit-subject]"
7 echo "Resolves a short git commit ID to its full SHA-1 hash, particularly useful for fixing references in commit messages."
8 echo ""
9 echo "Arguments:"
10 echo " --selftest Run self-tests"
11 echo " --force Try to find commit by subject if ID lookup fails"
12 echo " commit-id Short git commit ID to resolve"
13 echo " commit-subject Optional commit subject to help resolve between multiple matches"
14 exit 1
15}
16
17# Convert subject with ellipsis to grep pattern
18convert_to_grep_pattern() {
19 local subject="$1"
20 # First escape ALL regex special characters
21 local escaped_subject
22 escaped_subject=$(printf '%s\n' "$subject" | sed 's/[[\.*^$()+?{}|]/\\&/g')
23 # Also escape colons, parentheses, and hyphens as they are special in our context
24 escaped_subject=$(echo "$escaped_subject" | sed 's/[:-]/\\&/g')
25 # Then convert escaped ... sequence to .*?
26 escaped_subject=$(echo "$escaped_subject" | sed 's/\\\.\\\.\\\./.*?/g')
27 echo "^${escaped_subject}$"
28}
29
30git_resolve_commit() {
31 local force=0
32 if [ "$1" = "--force" ]; then
33 force=1
34 shift
35 fi
36
37 # Split input into commit ID and subject
38 local input="$*"
39 local commit_id="${input%% *}"
40 local subject=""
41
42 # Extract subject if present (everything after the first space)
43 if [[ "$input" == *" "* ]]; then
44 subject="${input#* }"
45 # Strip the ("...") quotes if present
46 subject="${subject#*(\"}"
47 subject="${subject%\")*}"
48 fi
49
50 # Get all possible matching commit IDs
51 local matches
52 readarray -t matches < <(git rev-parse --disambiguate="$commit_id" 2>/dev/null)
53
54 # Return immediately if we have exactly one match
55 if [ ${#matches[@]} -eq 1 ]; then
56 echo "${matches[0]}"
57 return 0
58 fi
59
60 # If no matches and not in force mode, return failure
61 if [ ${#matches[@]} -eq 0 ] && [ $force -eq 0 ]; then
62 return 1
63 fi
64
65 # If we have a subject, try to find a match with that subject
66 if [ -n "$subject" ]; then
67 # Convert subject with possible ellipsis to grep pattern
68 local grep_pattern
69 grep_pattern=$(convert_to_grep_pattern "$subject")
70
71 # In force mode with no ID matches, use git log --grep directly
72 if [ ${#matches[@]} -eq 0 ] && [ $force -eq 1 ]; then
73 # Use git log to search, but filter to ensure subject matches exactly
74 local match
75 match=$(git log --format="%H %s" --grep="$grep_pattern" --perl-regexp -10 | \
76 while read -r hash subject; do
77 if echo "$subject" | grep -qP "$grep_pattern"; then
78 echo "$hash"
79 break
80 fi
81 done)
82 if [ -n "$match" ]; then
83 echo "$match"
84 return 0
85 fi
86 else
87 # Normal subject matching for existing matches
88 for match in "${matches[@]}"; do
89 if git log -1 --format="%s" "$match" | grep -qP "$grep_pattern"; then
90 echo "$match"
91 return 0
92 fi
93 done
94 fi
95 fi
96
97 # No match found
98 return 1
99}
100
101run_selftest() {
102 local test_cases=(
103 '00250b5 ("MAINTAINERS: add new Rockchip SoC list")'
104 '0037727 ("KVM: selftests: Convert xen_shinfo_test away from VCPU_ID")'
105 'ffef737 ("net/tls: Fix skb memory leak when running kTLS traffic")'
106 'd3d7 ("cifs: Improve guard for excluding $LXDEV xattr")'
107 'dbef ("Rename .data.once to .data..once to fix resetting WARN*_ONCE")'
108 '12345678' # Non-existent commit
109 '12345 ("I'\''m a dummy commit")' # Valid prefix but wrong subject
110 '--force 99999999 ("net/tls: Fix skb memory leak when running kTLS traffic")' # Force mode with non-existent ID but valid subject
111 '83be ("firmware: ... auto-update: fix poll_complete() ... errors")' # Wildcard test
112 '--force 999999999999 ("firmware: ... auto-update: fix poll_complete() ... errors")' # Force mode wildcard test
113 )
114
115 local expected=(
116 "00250b529313d6262bb0ebbd6bdf0a88c809f6f0"
117 "0037727b3989c3fe1929c89a9a1dfe289ad86f58"
118 "ffef737fd0372ca462b5be3e7a592a8929a82752"
119 "d3d797e326533794c3f707ce1761da7a8895458c"
120 "dbefa1f31a91670c9e7dac9b559625336206466f"
121 "" # Expect empty output for non-existent commit
122 "" # Expect empty output for wrong subject
123 "ffef737fd0372ca462b5be3e7a592a8929a82752" # Should find commit by subject in force mode
124 "83beece5aff75879bdfc6df8ba84ea88fd93050e" # Wildcard test
125 "83beece5aff75879bdfc6df8ba84ea88fd93050e" # Force mode wildcard test
126 )
127
128 local expected_exit_codes=(
129 0
130 0
131 0
132 0
133 0
134 1 # Expect failure for non-existent commit
135 1 # Expect failure for wrong subject
136 0 # Should succeed in force mode
137 0 # Should succeed with wildcard
138 0 # Should succeed with force mode and wildcard
139 )
140
141 local failed=0
142
143 echo "Running self-tests..."
144 for i in "${!test_cases[@]}"; do
145 # Capture both output and exit code
146 local result
147 result=$(git_resolve_commit ${test_cases[$i]}) # Removed quotes to allow --force to be parsed
148 local exit_code=$?
149
150 # Check both output and exit code
151 if [ "$result" != "${expected[$i]}" ] || [ $exit_code != ${expected_exit_codes[$i]} ]; then
152 echo "Test case $((i+1)) FAILED"
153 echo "Input: ${test_cases[$i]}"
154 echo "Expected output: '${expected[$i]}'"
155 echo "Got output: '$result'"
156 echo "Expected exit code: ${expected_exit_codes[$i]}"
157 echo "Got exit code: $exit_code"
158 failed=1
159 else
160 echo "Test case $((i+1)) PASSED"
161 fi
162 done
163
164 if [ $failed -eq 0 ]; then
165 echo "All tests passed!"
166 exit 0
167 else
168 echo "Some tests failed!"
169 exit 1
170 fi
171}
172
173# Check for selftest
174if [ "$1" = "--selftest" ]; then
175 run_selftest
176 exit $?
177fi
178
179# Handle --force flag
180force=""
181if [ "$1" = "--force" ]; then
182 force="--force"
183 shift
184fi
185
186# Verify arguments
187if [ $# -eq 0 ]; then
188 usage
189fi
190
191# Skip validation in force mode
192if [ -z "$force" ]; then
193 # Validate that the first argument matches at least one git commit
194 if [ "$(git rev-parse --disambiguate="$1" 2>/dev/null | wc -l)" -eq 0 ]; then
195 echo "Error: '$1' does not match any git commit"
196 exit 1
197 fi
198fi
199
200git_resolve_commit $force "$@"
201exit $?