git-subtree-check.sh raw

   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