From: rafael-santiago <voidbrainvoid@xxxxxxxxxxxx> The idea behind this commit can be useful for teams that share git-hooks into a custom directory and dealing with projects that must be developed, built, maintained on several different platforms. This commit allows the execution of git hooks based on the current operating system. A "native hook" is defined in the form: hooks/hook-name_platform Where platform must be equivalent to the content returned in sysname field in utsname struct when calling uname() [but all normalized in lowercase]. On Windows, independent of version, flavor, SP, whatever it is simply "windows". When a native hook is not found the standard hook (.git/hook/hook-name), if found is executed, of course. In other words, the hook without a platform postfix (_yyz) is the standard hook. When native hook is not set as executable but standard is set, the standard will be executed. The main motivation of this extension is to reduce dependency of scripting languages, logical trinkets etc just to execute minor tasks during scm events that could be done natively but differently from a platform to another. Less dependencies, cleaner repos: a small step for a better world for any software developer. Signed-off-by: Rafael Santiago <voidbrainvoid@xxxxxxxxxxxx> --- Give support for hooks based on platform The idea behind this commit can be useful for teams that share git-hooks into a custom directory and dealing with projects that must be developed, built, maintained on several different platforms. This commit allows the execution of git hooks based on the current operating system. A "native hook" is defined in the form: hooks/hook-name_platform Where platform must be equivalent to the content returned in sysname field in utsname struct when calling uname() [but all normalized in lowercase]. On Windows, independent of version, flavor, SP, whatever it is simply "windows". When a native hook is not found the standard hook (.git/hook/hook-name), if found is executed, of course. In other words, the hook without a platform postfix (_yyz) is the standard hook. When native hook is not set as executable but standard is set, the standard will be executed. The main motivation of this extension is to reduce dependency of scripting languages, logical trinkets etc just to execute minor tasks during scm events that could be done natively but differently from a platform to another. Less dependencies, cleaner repos: a small step for a better world for any software developer. Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1069%2Frafael-santiago%2Fmaster-v1 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1069/rafael-santiago/master-v1 Pull-Request: https://github.com/git/git/pull/1069 run-command.c | 41 ++++++++++++++++++++ t/t7527-pre-commit-native-hook.sh | 63 +++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100755 t/t7527-pre-commit-native-hook.sh diff --git a/run-command.c b/run-command.c index f72e72cce73..973c1a3434b 100644 --- a/run-command.c +++ b/run-command.c @@ -1319,9 +1319,50 @@ int async_with_fork(void) #endif } +static inline const char *platform_name(void) +{ + static const char *platform = NULL; +#ifndef GIT_WINDOWS_NATIVE + static struct utsname un = { 0 }; +#endif + if (platform != NULL) + return platform; + +#ifndef GIT_WINDOWS_NATIVE + if (uname(&un) == 0) { + for (size_t s = 0; un.sysname[s] != 0; s++) + un.sysname[s] = tolower(un.sysname[s]); + platform = un.sysname; + } +#else + platform = "windows"; +#endif + + return platform; +} + +static const char *find_native_hook(const char *name) +{ + char native_name[64] = ""; + const char *platform = NULL; + if (name == NULL || strstr(name, "_") != NULL) + return NULL; + + platform = platform_name(); + if (platform == NULL) + return NULL; + + if (snprintf(native_name, sizeof(native_name) - 1, "%s_%s", name, platform) >= sizeof(native_name) - 1) + return NULL; + return find_hook(native_name); +} + const char *find_hook(const char *name) { + const char *native_hook = find_native_hook(name); static struct strbuf path = STRBUF_INIT; + if (native_hook != NULL) + return native_hook; strbuf_reset(&path); strbuf_git_path(&path, "hooks/%s", name); diff --git a/t/t7527-pre-commit-native-hook.sh b/t/t7527-pre-commit-native-hook.sh new file mode 100755 index 00000000000..f3835f943af --- /dev/null +++ b/t/t7527-pre-commit-native-hook.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +test_description='Test native hooks extension' + +. ./test-lib.sh + +expected_platform=$(uname -s | tr A-Z a-z) + +if [ $(expr substr $(uname -s | tr A-Z a-z) 1 5) == "mingw" ] ; then + expected_platform="windows" +fi + +test_expect_success 'set standard and native pre-commit hooks' ' + mkdir -p test-repo && + cd test-repo && + git init && + mkdir -p .git/hooks && + echo \#!/bin/sh > .git/hooks/pre-commit && + echo echo Hello generic. >> .git/hooks/pre-commit && + chmod u+x .git/hooks/pre-commit && + echo \#!/bin/sh > .git/hooks/pre-commit_${expected_platform} && + echo echo Hello ${expected_platform} >> .git/hooks/pre-commit_${expected_platform} && + chmod u+x .git/hooks/pre-commit_${expected_platform} && + echo test > README && + git add README && + git commit -am "1-2-3 this is a test." 2>out.txt && + cat out.txt | grep Hello\ ${expected_platform} +' + +if [ ${expected_platform} != "windows" ] ; then + # chmod does not work well on Windows. + test_expect_success 'set standard and native pre-commit hooks but let the native one not executable' ' + mkdir -p test-repo && + cd test-repo && + git init && + mkdir -p .git/hooks && + echo \#!/bin/sh > .git/hooks/pre-commit && + echo echo Hello generic. >> .git/hooks/pre-commit && + chmod u+x .git/hooks/pre-commit && + echo \#!/bin/sh > .git/hooks/pre-commit_${expected_platform} && + echo echo Hello ${expected_platform} >> .git/hooks/pre-commit_${expected_platform} && + echo test > README && + git add README && + git commit -am "1-2-3 this is a test." 2>out.txt && + cat out.txt | grep Hello\ generic + ' + + test_expect_success 'set standard pre-commit hook only' ' + mkdir -p test-repo && + cd test-repo && + git init && + mkdir -p .git/hooks && + echo \#!/bin/sh > .git/hooks/pre-commit && + echo echo Hello standard hook. >> .git/hooks/pre-commit && + chmod u+x .git/hooks/pre-commit && + echo test > README && + git add README && + git commit -am "1-2-3 this is a test." 2>out.txt && + cat out.txt | grep Hello\ standard\ hook + ' +fi + +test_done base-commit: 225bc32a989d7a22fa6addafd4ce7dcd04675dbf -- gitgitgadget