mirror of
https://github.com/torvalds/linux.git
synced 2025-08-15 22:21:42 +02:00

I forgot to include it when I've originally submitted the script.
Fixes: 7ae52a3d7f
("scripts: Add git-resolve tool for full SHA-1 resolution")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Link: https://lore.kernel.org/r/20250421135915.1915062-1-sashal@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
201 lines
5.8 KiB
Bash
Executable file
201 lines
5.8 KiB
Bash
Executable file
#!/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 $?
|