Skip to content

Commit ba708fd

Browse files
committed
Allow to ignore paths using a .clang-format-hook-exclude file
1 parent ddfcbdb commit ba708fd

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

apply-format

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ declare apply_to_staged=false
109109
declare staged=false
110110
declare in_place=false
111111
declare style=file
112+
declare ignored=()
112113
while [ $# -gt 0 ]; do
113114
declare arg="$1"
114115
shift # Past option.
@@ -138,6 +139,16 @@ while [ $# -gt 0 ]; do
138139
style="$1"
139140
shift
140141
;;
142+
--internal-opt-ignore-regex=* )
143+
ignored+=("${arg//--internal-opt-ignore-regex=/}")
144+
;;
145+
--internal-opt-ignore-regex )
146+
ignored+=("${arg//--internal-opt-ignore-regex=/}")
147+
[ $# -gt 0 ] || \
148+
error_exit "No argument for --internal-opt-ignore-regex option."
149+
ignored+=("$1")
150+
shift
151+
;;
141152
-- )
142153
# Stop processing further arguments.
143154
if [ $# -gt 0 ]; then
@@ -299,11 +310,21 @@ else # Diff-only.
299310
read -r -a format_diff_args <<< "$format_diff"
300311
[ "$in_place" = true ] && format_diff_args+=("-i")
301312

313+
# Build the regex for paths to consider or ignore.
314+
# We use negative lookahead assertions which preceed the list of allowed patterns
315+
# (that is, the extensions we want).
316+
exclusions_regex=
317+
if [ "${#ignored[@]}" -gt 0 ]; then
318+
for pattern in "${ignored[@]}"; do
319+
exclusions_regex="$exclusions_regex(?!$pattern)"
320+
done
321+
fi
322+
302323
"${git_args[@]}" "$@" \
303324
| "${format_diff_args[@]}" \
304325
-p1 \
305326
-style="$style" \
306-
-iregex='.*\.(c|cpp|cxx|cc|h|m|mm|js|java)' \
327+
-iregex="$exclusions_regex"'.*\.(c|cpp|cxx|cc|h|m|mm|js|java)' \
307328
> "$patch_dest" \
308329
|| exit 1
309330

git-pre-commit-format

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,23 @@ fi
260260

261261
readonly style=$(cd "$top_dir" && git config hooks.clangFormatDiffStyle || echo file)
262262

263+
apply_format_opts=(
264+
"--style=$style"
265+
--cached
266+
)
267+
268+
readonly exclusions_file="$top_dir/.clang-format-hook-exclude"
269+
if [ -e "$exclusions_file" ]; then
270+
while IFS= read -r line; do
271+
if [[ "$line" && "$line" != "#"* ]]; then
272+
apply_format_opts+=("--internal-opt-ignore-regex=$line")
273+
fi
274+
done < "$exclusions_file"
275+
fi
276+
263277
readonly patch=$(mktemp)
264278
trap '{ rm -f "$patch"; }' EXIT
265-
"$apply_format" --style="$style" --cached > "$patch" || \
279+
"$apply_format" --style="$style" --cached "${apply_format_opts[@]}" > "$patch" || \
266280
error_exit $'\nThe apply-format script failed.'
267281

268282
if [ "$(wc -l < "$patch")" -eq 0 ]; then

tests/test_hook.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ def install(self, allow_errors=False):
4848
raise
4949
return False, exc.output
5050

51+
def write_ignore_list(self, *args):
52+
with WorkDir(os.path.join(self.repo.git_dir, '..')):
53+
with open('.clang-format-hook-exclude', 'w') as exclude_file:
54+
exclude_file.write('\n'.join(args))
55+
exclude_file.write('\n')
56+
5157
def test_install(self):
5258
res, output = self.install()
5359
self.assertTrue(res)
@@ -205,6 +211,57 @@ def test_install_from_scripts_dir(self):
205211
# The file on disk is updated.
206212
self.assertEqual(self.repo.read_file(data.FILENAME), data.FIXED)
207213

214+
def test_commit_ignorefile_empty(self):
215+
self.install()
216+
217+
self.repo.write_file(data.FILENAME, data.CODE)
218+
self.repo.add(data.FILENAME)
219+
220+
# Don't ignore anything.
221+
self.write_ignore_list()
222+
223+
self.repo.commit(input_text='a\n')
224+
# The file on disk is updated as it's not ignored.
225+
self.assertEqual(self.repo.read_file(data.FILENAME), data.FIXED)
226+
227+
def test_commit_ignorefile_comments_only(self):
228+
self.install()
229+
230+
self.repo.write_file(data.FILENAME, data.CODE)
231+
self.repo.add(data.FILENAME)
232+
233+
# Don't ignore anything, but add comments and empty lines.
234+
self.write_ignore_list('# Hello world', '', '# Bar baz')
235+
236+
self.repo.commit(input_text='a\n')
237+
# The file on disk is updated as it's not ignored.
238+
self.assertEqual(self.repo.read_file(data.FILENAME), data.FIXED)
239+
240+
def test_commit_ignorefile_ignore_foo(self):
241+
self.install()
242+
243+
self.repo.write_file(data.FILENAME, data.CODE)
244+
self.repo.add(data.FILENAME)
245+
246+
# Ignore the C file we modify.
247+
self.write_ignore_list(data.FILENAME)
248+
249+
self.repo.commit()
250+
# The file on disk is updated but its formatting was not fixed.
251+
self.assertEqual(self.repo.read_file(data.FILENAME), data.CODE)
252+
253+
def test_commit_ignorefile_ignore_pattern(self):
254+
self.install()
255+
256+
self.repo.write_file(data.FILENAME, data.CODE)
257+
self.repo.add(data.FILENAME)
258+
259+
# Ignore the C file we modify through a pattern.
260+
self.write_ignore_list(r'f.o\.c')
261+
262+
self.repo.commit()
263+
# The file on disk is updated but its formatting was not fixed.
264+
self.assertEqual(self.repo.read_file(data.FILENAME), data.CODE)
208265

209266

210267
class HookClonedTestCase(CloneRepoMixin,

0 commit comments

Comments
 (0)