1 #!/bin/sh
2 # Copyright (c) 2015-present 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"
134