Git pre-commit 挂钩,自动授予所有正在提交的 .sh 文件的执行权限 (+x)

Git pre-commit hook that automatically grants execution permissions (+x) on all .sh files which are being committed

我正在尝试解决此处说明的完全相同的问题:

How to commit executable shell scripts with Git on Windows

“如果您在 Windows 上开发涉及 shell 脚本的软件,在 UNIX 上也应该 运行,那么您就有问题了。

Windows NTFS 等文件系统不支持 UNIX 权限位。

每当您在 Windows 上创建新的 shell 脚本或重命名现有脚本(在签出时可能已经可执行)时,这些脚本将无法执行。当您推送代码时,这些脚本不会 运行 基于 UNIX 的机器。"

为解决上述问题而提出的给定预提交钩子脚本写在 python。

#!/usr/bin/env python
import subprocess

if __name__ == '__main__':
    output = subprocess.check_output(["git", "ls-files", "-s", "--", "*.sh"], shell=True).decode("utf-8")  # type: str
    files_to_fix = []
    for line in output.splitlines():
        # Example for "line": '100644 82f6a7d558e1b38c8b47ec5084fe20f970f09981 0 test-update.sh'
        entry = line.replace('\t', ' ').split(" ", maxsplit=3)
        mode = entry[0][3:]  # strips the first 3 chars ("100") which we don't care about
        filename = entry[3]
        if mode == "644":
            files_to_fix.append(filename)

    for file_path in files_to_fix:
        # git update-index --chmod=+x script.sh
        subprocess.check_call(["git", "update-index", "--chmod=+x", file_path], shell=True)

不精通bash重写bash。有可能在 bash 中实现吗?

使用 bash 脚本挂钩:

#!/usr/bin/env bash

files_to_fix=()
while read -r -d '' mode _ _ file_path; do
  [[ $mode == *644 ]] && files_to_fix+=("$file_path")
done < <(git ls-files --stage -z '*.sh')
git update-index --chmod=+x -- "${files_to_fix[@]}"

或 POSIX shell:

#!/usr/bin/env sh

git ls-files --stage '*.sh' | while read -r mode _ _ file_path; do
  case $mode in
    *644) git update-index --chmod=+x -- "$file_path" ;;
    *) ;;
  esac
done

这个衬里使用 find 来识别没有设置执行位的文件而不是寻找权限 644,所以它适用于 unusual[=21] =] 类似 640 甚至 200 的模式(只写!):

find . ! -perm -u+x -type f -name "*.sh" -exec git update-index --chmod=+x -- {} \;

将其保存在 .git/hooks/pre-commit 中(并使您的钩子可执行!)