/ test / lint / git-subtree-check.sh
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"