Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

scripts: Add git-resolve tool for full SHA-1 resolution

Introduce git-resolve.sh, a tool that resolves short git commit IDs to their
full SHA-1 hash. This is particularly useful for navigating references in commit
messages and verifying Fixes tags.

When faced with ambiguous commit IDs or imprecise references in messages,
this tool can help by resolving commit hashes based on not just the ID
itself but also the commit subject, making it more robust than standard
git rev-parse.

This is especially valuable for maintainers who need to verify Fixes tags
or cross-reference commits. Unlike proposals to add dates to Fixes tags
(which would break existing tooling), this script provides a way to
disambiguate commits without changing the established tag format.

The script includes several features:
- Resolves short commit IDs to full SHA-1 hashes
- Uses commit subjects to disambiguate between multiple potential matches
- Supports wildcard patterns in subjects with ellipsis (...)
- Provides a force mode to attempt resolution by subject when ID lookup fails
- Includes comprehensive self-tests

Signed-off-by: Sasha Levin <sashal@kernel.org>
Reviewed-by: Kees Cook <kees@kernel.org>
Link: https://lore.kernel.org/r/20250311165336.248120-1-sashal@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Sasha Levin and committed by
Greg Kroah-Hartman
7ae52a3d d062463e

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