git hook+expand+unexpand化解空格与TAB之争
SPACE与TAB之争由来已久,但是有一个观点是两派都公认赞同的,那就是 不能两者混用.
为此,Linux很贴心的提供了 expand
和 unexpand
命令来帮助我们进行 TAB
和 SPACE
之间的相互转换。
使用expand将TAB转换为SPACE
使用expand将TAB转换为SPACE的方法很简单,直接执行 expand 文件
就会把 文件
中的所有 TAB
都替换空格(默认按8个空格进行对齐)了。
例如假设我们有下面一个测试文件
cat -T /tmp/test
^Ihello ^Itest^I
其中 cat
的 -T
选项文件内容其中的 TAB
显示为 ^I
我们现在来用 expand
对其进行转换
expand /tmp/test |cat -T
hello test
如果觉得使用8空格对齐太空了,那么可以使用 -t
选项指定对齐的空格数。比如下面命令按4个空格进行对齐:
expand -t 4 /tmp/test |cat -T
hello test
另一方面,我们在写代码时可能只需要转换行首的空格,而字符串中的 TAB
是不需要进行转换的,那么就可以使用 -i
选项指定 expand
只对行首的空格进行转换:
expand -i -t 4 /tmp/test |cat -T
hello ^Itest^I
使用unexpand将SPACE转换为TAB
unexpand
, 从名字上就知道它是 expand
的反操作,即把SPACE转换为TAB.
他们的使用方法也很类似,但是有一点不同就是 expand
在默认情况下替换所有的 TAB
,当只替换行首的 TAB
时需要指定 -i
选项;而 unexpand
在默认情况下只替换行首的 SPACE
,若要替换 SPACE
时需要明确指定 -a
选项.
还是例如假设我们有下面一个测试文件
cat -T /tmp/test.space
hello test
我们现在来用 unexpand
对其进行转换
unexpand /tmp/test.space |cat -T
^Ihello test
你会发现只替换了前面的8个空格为 TAB
同样我们可以使用 -t
选项指定 TAB
对齐的空格数。比如下面命令按4个空格进行对齐:
unexpand -t 4 /tmp/test.space |cat -T
^I^Ihello^Itest^I
结果的前面是两个 TAB
,也就是每四个空格转换为了一个 TAB
.
若要将所有的空格全部替换,那么需要明确使用 -a
选项进行转换:
unexpand -a -t 4 /tmp/test.space |cat -T
^I^Ihello^Itest^I
使用git hook实现代码的自动转换
在确定好规范使用SPACE或TAB后,我们可以借助git的pre-commit hook来让我们在代码提交前自动进行规范化的转换。
比如,假设我们定义的规范是只使用 SPACE
规范,那么我们可以创建下面的 pre-commit hook
if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=$(git hash-object -t tree /dev/null) fi # Redirect output to stderr. exec 1>&2 TMPFILE=$(mktemp) trap 'rm -f ${TMPFILE}' EXIT for file in $(git diff --cached --name-only --diff-filter=ACM $against) do # 只对text类文件进行转换 if file "${file}" |grep 'text' 2>&1 >/dev/null;then echo "expanding ${file}" expand -i -t 4 "${file}" > "${TMPFILE}" cp "${TMPFILE}" "${file}" git add "${file}" fi done
将该文件保存为 .git/hooks/pre-commit
,然后添加可执行权限后,每次提交就会对文件进行格式化处理了:
lujun9972:a/ (master U:1 ?:3✗) $ cat -T test ^Ihello ^Itest^I^I lujun9972:a/ (master U:1 ?:3✗) $ git commit -a -m update expanding test [master 88b2f66] update 1 file changed, 1 deletion(-) lujun9972:a/ (master ?:3✗) $ cat -T test hello ^Itest^I^I
然后我们再通过 post-commit
hook 将转换的文件再转换回TAB来:
TMPFILE=$(mktemp) against="HEAD^1" trap 'rm -f ${TMPFILE}' EXIT for file in $(git diff --cached --name-only --diff-filter=ACM $against) do # 只对text类文件进行转换 if file "${file}" |grep 'text' 2>&1 >/dev/null;then echo "unexpanding ${file}" unexpand -t 4 "${file}" > "${TMPFILE}" cp "${TMPFILE}" "${file}" git add "${file}" fi done
类似的,我们还可以通过 post-merge
hook(实现跟post-commit一样) 来将远端仓库中的代码也自动转回 TAB
,
如此一来, TAB
还好者们就可以安心的在本地使用 TAB
,然后由git帮你自动进行 SPACE
的转换了.