mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +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 $?
 |