summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/build_tools/format-diff.sh
blob: 62e8834f7d1ad47471a3c9b2daea613ebe6f58b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env bash
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
# If clang_format_diff.py command is not specfied, we assume we are able to
# access directly without any path.

print_usage () {
  echo "Usage:"
  echo "format-diff.sh [OPTIONS]"
  echo "-c: check only."
  echo "-h: print this message."
}

while getopts ':ch' OPTION; do
  case "$OPTION" in
    c)
      CHECK_ONLY=1
      ;;
    h)
      print_usage
      exit 1
      ;;
    ?)
      print_usage
      exit 1
      ;;
  esac
done

REPO_ROOT="$(git rev-parse --show-toplevel)"

if [ "$CLANG_FORMAT_DIFF" ]; then
  echo "Note: CLANG_FORMAT_DIFF='$CLANG_FORMAT_DIFF'"
  # Dry run to confirm dependencies like argparse
  if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
    true #Good
  else
    exit 128
  fi
else
  # First try directly executing the possibilities
  if clang-format-diff --help &> /dev/null < /dev/null; then
    CLANG_FORMAT_DIFF=clang-format-diff
  elif clang-format-diff.py --help &> /dev/null < /dev/null; then
    CLANG_FORMAT_DIFF=clang-format-diff.py
  elif $REPO_ROOT/clang-format-diff.py --help &> /dev/null < /dev/null; then
    CLANG_FORMAT_DIFF=$REPO_ROOT/clang-format-diff.py
  else
    # This probably means we need to directly invoke the interpreter.
    # But first find clang-format-diff.py
    if [ -f "$REPO_ROOT/clang-format-diff.py" ]; then
      CFD_PATH="$REPO_ROOT/clang-format-diff.py"
    elif which clang-format-diff.py &> /dev/null; then
      CFD_PATH="$(which clang-format-diff.py)"
    else
      echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
      echo "You can download clang-format-diff.py by running: "
      echo "    curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py"
      echo "You should make sure the downloaded script is not compromised."
      echo "You can download clang-format by running:"
      echo "    brew install clang-format"
      echo "  Or"
      echo "    apt install clang-format"
      echo "  This might work too:"
      echo "    yum install git-clang-format"
      echo "Then make sure clang-format is available and executable from \$PATH:"
      echo "    clang-format --version"
      exit 128
    fi
    # Check argparse pre-req on interpreter, or it will fail
    if echo import argparse | ${PYTHON:-python3}; then
      true # Good
    else
      echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
      echo "installed. You can try either of the follow ways to install it:"
      echo "  1. Manually download argparse: https://pypi.python.org/pypi/argparse"
      echo "  2. easy_install argparse (if you have easy_install)"
      echo "  3. pip install argparse (if you have pip)"
      exit 129
    fi
    # Unfortunately, some machines have a Python2 clang-format-diff.py
    # installed but only a Python3 interpreter installed. Unfortunately,
    # automatic 2to3 migration is insufficient, so suggest downloading latest.
    if grep -q "print '" "$CFD_PATH" && \
       ${PYTHON:-python3} --version | grep -q 'ython 3'; then
      echo "You have clang-format-diff.py for Python 2 but are using a Python 3"
      echo "interpreter (${PYTHON:-python3})."
      echo "You can download clang-format-diff.py for Python 3 by running: "
      echo "    curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py"
      echo "You should make sure the downloaded script is not compromised."
      exit 130
    fi
    CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
    # This had better work after all those checks
    if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
      true #Good
    else
      exit 128
    fi
  fi
fi

# TODO(kailiu) following work is not complete since we still need to figure
# out how to add the modified files done pre-commit hook to git's commit index.
#
# Check if this script has already been added to pre-commit hook.
# Will suggest user to add this script to pre-commit hook if their pre-commit
# is empty.
# PRE_COMMIT_SCRIPT_PATH="`git rev-parse --show-toplevel`/.git/hooks/pre-commit"
# if ! ls $PRE_COMMIT_SCRIPT_PATH &> /dev/null
# then
#   echo "Would you like to add this script to pre-commit hook, which will do "
#   echo -n "the format check for all the affected lines before you check in (y/n):"
#   read add_to_hook
#   if [ "$add_to_hook" == "y" ]
#   then
#     ln -s `git rev-parse --show-toplevel`/build_tools/format-diff.sh $PRE_COMMIT_SCRIPT_PATH
#   fi
# fi
set -e

uncommitted_code=`git diff HEAD`

# If there's no uncommitted changes, we assume user are doing post-commit
# format check, in which case we'll try to check the modified lines vs. the
# facebook/rocksdb.git main branch. Otherwise, we'll check format of the
# uncommitted code only.
if [ -z "$uncommitted_code" ]
then
  # Attempt to get name of facebook/rocksdb.git remote.
  [ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(LC_ALL=POSIX LANG=POSIX git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
  # Fall back on 'origin' if that fails
  [ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin
  # Use main branch from that remote
  [ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/$(LC_ALL=POSIX LANG=POSIX git remote show $FORMAT_REMOTE | sed -n '/HEAD branch/s/.*: //p')"
  # Get the common ancestor with that remote branch. Everything after that
  # common ancestor would be considered the contents of a pull request, so
  # should be relevant for formatting fixes.
  FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
  # Get the differences
  diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
  echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
else
  # Check the format of uncommitted lines,
  diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
  echo "Checking format of uncommitted changes..."
fi

if [ -z "$diffs" ]
then
  echo "Nothing needs to be reformatted!"
  exit 0
elif [ $CHECK_ONLY ]
then
  echo "Your change has unformatted code. Please run make format!"
  if [ $VERBOSE_CHECK ]; then
    clang-format --version
    echo "$diffs"
  fi
  exit 1
fi

# Highlight the insertion/deletion from the clang-format-diff.py's output
COLOR_END="\033[0m"
COLOR_RED="\033[0;31m"
COLOR_GREEN="\033[0;32m"

echo -e "Detect lines that doesn't follow the format rules:\r"
# Add the color to the diff. lines added will be green; lines removed will be red.
echo "$diffs" |
  sed -e "s/\(^-.*$\)/`echo -e \"$COLOR_RED\1$COLOR_END\"`/" |
  sed -e "s/\(^+.*$\)/`echo -e \"$COLOR_GREEN\1$COLOR_END\"`/"

echo -e "Would you like to fix the format automatically (y/n): \c"

# Make sure under any mode, we can read user input.
exec < /dev/tty
read to_fix

if [ "$to_fix" != "y" ]
then
  exit 1
fi

# Do in-place format adjustment.
if [ -z "$uncommitted_code" ]
then
  git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
else
  git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1
fi
echo "Files reformatted!"

# Amend to last commit if user do the post-commit format check
if [ -z "$uncommitted_code" ]; then
  echo -e "Would you like to amend the changes to last commit (`git log HEAD --oneline | head -1`)? (y/n): \c"
  read to_amend

  if [ "$to_amend" == "y" ]
  then
    git commit -a --amend --reuse-message HEAD
    echo "Amended to last commit"
  fi
fi