git-subtree-check.sh
1 #!/bin/sh 2 # Copyright (c) 2015-2021 The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6 export LC_ALL=C 7 8 check_remote=0 9 while getopts "?hr" opt; do 10 case $opt in 11 '?' | h) 12 echo "Usage: $0 [-r] DIR [COMMIT]" 13 echo " $0 -?" 14 echo "" 15 echo "Checks that a certain prefix is pure subtree, and optionally whether the" 16 echo "referenced commit is present in any fetched remote." 17 echo "" 18 echo "DIR is the prefix within the repository to check." 19 echo "COMMIT is the commit to check, if it is not provided, HEAD will be used." 20 echo "" 21 echo "-r Check that subtree commit is present in repository." 22 echo " To do this check, fetch the subtreed remote first. Example:" 23 echo "" 24 echo " git fetch https://github.com/bitcoin-core/secp256k1.git" 25 echo " test/lint/git-subtree-check.sh -r src/secp256k1" 26 exit 1 27 ;; 28 r) 29 check_remote=1 30 ;; 31 esac 32 done 33 shift $((OPTIND-1)) 34 35 if [ -z "$1" ]; then 36 echo "Need to provide a DIR, see $0 -?" 37 exit 1 38 fi 39 40 # Strip trailing / from directory path (in case it was added by autocomplete) 41 DIR="${1%/}" 42 COMMIT="$2" 43 if [ -z "$COMMIT" ]; then 44 COMMIT=HEAD 45 fi 46 47 # Taken from git-subtree (Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>) 48 find_latest_squash() 49 { 50 dir="$1" 51 sq= 52 main= 53 sub= 54 git log --grep="^git-subtree-dir: $dir/*\$" \ 55 --pretty=format:'START %H%n%s%n%n%b%nEND%n' "$COMMIT" | 56 while read a b _; do 57 case "$a" in 58 START) sq="$b" ;; 59 git-subtree-mainline:) main="$b" ;; 60 git-subtree-split:) sub="$b" ;; 61 END) 62 if [ -n "$sub" ]; then 63 if [ -n "$main" ]; then 64 # a rejoin commit? 65 # Pretend its sub was a squash. 66 sq="$sub" 67 fi 68 echo "$sq" "$sub" 69 break 70 fi 71 sq= 72 main= 73 sub= 74 ;; 75 esac 76 done 77 } 78 79 # find latest subtree update 80 latest_squash="$(find_latest_squash "$DIR")" 81 if [ -z "$latest_squash" ]; then 82 echo "ERROR: $DIR is not a subtree" >&2 83 exit 2 84 fi 85 # shellcheck disable=SC2086 86 set $latest_squash 87 old=$1 88 rev=$2 89 90 # get the tree in the current commit 91 tree_actual=$(git ls-tree -d "$COMMIT" "$DIR" | head -n 1) 92 if [ -z "$tree_actual" ]; then 93 echo "FAIL: subtree directory $DIR not found in $COMMIT" >&2 94 exit 1 95 fi 96 # shellcheck disable=SC2086 97 set $tree_actual 98 tree_actual_type=$2 99 tree_actual_tree=$3 100 echo "$DIR in $COMMIT currently refers to $tree_actual_type $tree_actual_tree" 101 if [ "d$tree_actual_type" != "dtree" ]; then 102 echo "FAIL: subtree directory $DIR is not a tree in $COMMIT" >&2 103 exit 1 104 fi 105 106 # get the tree at the time of the last subtree update 107 tree_commit=$(git show -s --format="%T" "$old") 108 echo "$DIR in $COMMIT was last updated in commit $old (tree $tree_commit)" 109 110 # ... and compare the actual tree with it 111 if [ "$tree_actual_tree" != "$tree_commit" ]; then 112 git diff "$tree_commit" "$tree_actual_tree" >&2 113 echo "FAIL: subtree directory was touched without subtree merge" >&2 114 exit 1 115 fi 116 117 if [ "$check_remote" != "0" ]; then 118 # get the tree in the subtree commit referred to 119 if [ "d$(git cat-file -t "$rev" 2>/dev/null)" != dcommit ]; then 120 echo "subtree commit $rev unavailable: cannot compare. Did you add and fetch the remote?" >&2 121 exit 1 122 fi 123 tree_subtree=$(git show -s --format="%T" "$rev") 124 echo "$DIR in $COMMIT was last updated to upstream commit $rev (tree $tree_subtree)" 125 126 # ... and compare the actual tree with it 127 if [ "$tree_actual_tree" != "$tree_subtree" ]; then 128 echo "FAIL: subtree update commit differs from upstream tree!" >&2 129 exit 1 130 fi 131 fi 132 133 echo "GOOD"