{"thread":{"id":"54431","subject":"[PATCH v1] hook: add sample template for push-to-checkout","startedAt":"2020-10-15T18:54:30Z","lastAt":"2020-10-15T22:54:53Z","messageCount":3,"participants":["Adam Spiers","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"407654","messageId":"20201015184703.5015-1-git@adamspiers.org","threadId":"54431","inReplyTo":null,"subject":"[PATCH v1] hook: add sample template for push-to-checkout","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2020-10-15T18:47:03Z","receivedAt":"2020-10-15T18:54:30Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"The template is a more-or-less exact translation to shell of the C\ncode for the default behaviour for git's push-to-checkout hook defined\nin the push_to_deploy() function in builtin/receive-pack.c, to serve\nas a convenient starting point for modification.\n\nIt also contains relevant text extracted from the git-config(1) and\ngithooks(5) man pages.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n templates/hooks--push-to-checkout.sample | 74 ++++++++++++++++++++++++\n 1 file changed, 74 insertions(+)\n create mode 100755 templates/hooks--push-to-checkout.sample\n\ndiff --git a/templates/hooks--push-to-checkout.sample b/templates/hooks--push-to-checkout.sample\nnew file mode 100755\nindex 0000000000..2c6e06f8f1\n--- /dev/null\n+++ b/templates/hooks--push-to-checkout.sample\n@@ -0,0 +1,74 @@\n+#!/bin/bash\n+\n+# An example hook script to update a checked-out tree on a git push.\n+#\n+# This hook is invoked by git-receive-pack(1) when it reacts to git\n+# push and updates reference(s) in its repository, and when the push\n+# tries to update the branch that is currently checked out and the\n+# receive.denyCurrentBranch configuration variable is set to\n+# updateInstead.\n+#\n+# By default, such a push is refused if the working tree and the index\n+# of the remote repository has any difference from the currently\n+# checked out commit; when both the working tree and the index match\n+# the current commit, they are updated to match the newly pushed tip\n+# of the branch. This hook is to be used to override the default\n+# behaviour; however the code below reimplements the default behaviour\n+# as a starting point for convenient modification.\n+#\n+# The hook receives the commit with which the tip of the current\n+# branch is going to be updated:\n+commit=\"$1\"\n+#echo \"push-to-checkout $commit\"\n+\n+# It can exit with a non-zero status to refuse the push (when it does\n+# so, it must not modify the index or the working tree).\n+die () {\n+    echo >&2 \"$*\"\n+    exit 1\n+}\n+\n+# Or it can make any necessary changes to the working tree and to the\n+# index to bring them to the desired state when the tip of the current\n+# branch is updated to the new commit, and exit with a zero status.\n+#\n+# For example, the hook can simply run git read-tree -u -m HEAD \"$1\"\n+# in order to emulate git fetch that is run in the reverse direction\n+# with git push, as the two-tree form of git read-tree -u -m is\n+# essentially the same as git switch or git checkout that switches\n+# branches while keeping the local changes in the working tree that do\n+# not interfere with the difference between the branches.\n+\n+# The below is a more-or-less exact translation to shell of the C code\n+# for the default behaviour for git's push-to-checkout hook defined in\n+# the push_to_deploy() function in builtin/receive-pack.c.\n+#\n+# Note that the hook will be executed from the repository directory,\n+# not from the working tree, so if you want to perform operations on\n+# the working tree, you will have to adapt your code accordingly, e.g.\n+# by adding \"cd ..\" or using relative paths.\n+\n+if ! git update-index -q --ignore-submodules --refresh; then\n+    die \"Up-to-date check failed\"\n+fi\n+\n+if ! git diff-files --quiet --ignore-submodules --; then\n+    die \"Working directory has unstaged changes\"\n+fi\n+\n+# This is a rough translation of:\n+#\n+#    head_has_history() ? \"HEAD\" : EMPTY_TREE_SHA1_HEX\n+if git cat-file -t HEAD >&/dev/null; then\n+    head=HEAD\n+else\n+    head=4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+fi\n+\n+if ! git diff-index --quiet --cached --ignore-submodules $head --; then\n+    die \"Working directory has staged changes\"\n+fi\n+\n+if ! git read-tree -u -m $commit; then\n+    die \"Could not update working tree to new HEAD\"\n+fi\n-- \n2.26.1\n\n"},{"id":"407677","messageId":"xmqqo8l3dxu7.fsf@gitster.c.googlers.com","threadId":"54431","inReplyTo":"20201015184703.5015-1-git@adamspiers.org","subject":"Re: [PATCH v1] hook: add sample template for push-to-checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-10-15T20:43:28Z","receivedAt":"2020-10-15T20:43:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> The template is a more-or-less exact translation to shell of the C\n> code for the default behaviour for git's push-to-checkout hook defined\n> in the push_to_deploy() function in builtin/receive-pack.c, to serve\n> as a convenient starting point for modification.\n>\n> It also contains relevant text extracted from the git-config(1) and\n> githooks(5) man pages.\n>\n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n>  templates/hooks--push-to-checkout.sample | 74 ++++++++++++++++++++++++\n>  1 file changed, 74 insertions(+)\n>  create mode 100755 templates/hooks--push-to-checkout.sample\n>\n> diff --git a/templates/hooks--push-to-checkout.sample b/templates/hooks--push-to-checkout.sample\n> new file mode 100755\n> index 0000000000..2c6e06f8f1\n> --- /dev/null\n> +++ b/templates/hooks--push-to-checkout.sample\n> @@ -0,0 +1,74 @@\n> +#!/bin/bash\n\nIf we want to make this part of the sample hooks shown to everybody,\nwe should stick to /bin/sh if we could.  Do you have to rely on any\nbash-ism that are not found in other shells to write this script, or\nis this just shows your inertia that you always work with bash?\n\n> +# The hook receives the commit with which the tip of the current\n> +# branch is going to be updated:\n> +commit=\"$1\"\n\nStrictly speaking, a parameter on the right hand side of an\nassignment does not have to get dquoted to protect it from getting\nmunged at $IFS, so this can be\n\n\tcommit=$1\n\nbut it gives us an important clue.  If $1 is worth protecting from\ngetting munged at $IFS, then we should assume later use of $commit\nmust be careful the same way.\n\n> +#echo \"push-to-checkout $commit\"\n\nLeftover debugging statement to be removed (instead of commented\nout).\n\n> +# It can exit with a non-zero status to refuse the push (when it does\n> +# so, it must not modify the index or the working tree).\n> +die () {\n> +    echo >&2 \"$*\"\n> +    exit 1\n> +}\n\nStyle (Documentation/CodingGudielines).  One level of indent is a\nsingle tab byte in our codebase.\n\n\n> +if ! git update-index -q --ignore-submodules --refresh; then\n\nStyle (Documentation/CodingGudielines).\n\n> +    die \"Up-to-date check failed\"\n\nThis is \"failed to refresh\" (in other words, not being up-to-date is\nOK and that is not what we are checking), but the patch is meant as\na faithful translation of the original, so it is OK here.\n\n> +fi\n> +\n> +if ! git diff-files --quiet --ignore-submodules --; then\n> +    die \"Working directory has unstaged changes\"\n> +fi\n> +\n> +# This is a rough translation of:\n> +#\n> +#    head_has_history() ? \"HEAD\" : EMPTY_TREE_SHA1_HEX\n> +if git cat-file -t HEAD >&/dev/null; then\n\nIs \">&/dev/null\" portable?  I'd rather see it written like\n\n\tgit cat-file -t HEAD >/dev/null 2>&1\n\nto be portable.\n\ncf. https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07 \n\nBut there is a better way to spell it (see below).\n\n> +    head=HEAD\n> +else\n> +    head=4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\nThis hardcodes the object name from SHA-1 world, and will break\nafter the user migrates to SHA-256.\n\n> +fi\n\nHere is probably a better version.\n\n\tif git cat-file -e HEAD\n\tthen\n\t\thead=HEAD\n\telse\n\t\thead=$(git hash-object -t tree --stdin </dev/null)\n\tfi\n\n\n\n\n> +\n> +if ! git diff-index --quiet --cached --ignore-submodules $head --; then\n> +    die \"Working directory has staged changes\"\n> +fi\n> +\n> +if ! git read-tree -u -m $commit; then\n\nYou forgot \"\" around $commit here.  Here, an $IFS byte in $commit will\nget the command line munged.  I.e.\n\n\tif ! git read-tree -u -m \"$commit\"\n\tthen\n\t\t...\n\n> +    die \"Could not update working tree to new HEAD\"\n> +fi\n\nThanks.\n"},{"id":"407690","messageId":"20201015225446.b5tvyo3cquhslfry@gmail.com","threadId":"54431","inReplyTo":"xmqqo8l3dxu7.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2] hook: add sample template for push-to-checkout","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2020-10-15T22:54:46Z","receivedAt":"2020-10-15T22:54:53Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Thu, Oct 15, 2020 at 01:43:28PM -0700, Junio C Hamano wrote: \n>Adam Spiers <git@adamspiers.org> writes: \n>\n>>The template is a more-or-less exact translation to shell of the C \n>>code for the default behaviour for git's push-to-checkout hook defined \n>>in the push_to_deploy() function in builtin/receive-pack.c, to serve \n>>as a convenient starting point for modification. \n>>\n>>It also contains relevant text extracted from the git-config(1) and \n>>githooks(5) man pages. \n>>\n>>Signed-off-by: Adam Spiers <git@adamspiers.org> \n>>---\n>>  templates/hooks--push-to-checkout.sample | 74 ++++++++++++++++++++++++ \n>>  1 file changed, 74 insertions(+) \n>>  create mode 100755 templates/hooks--push-to-checkout.sample \n>>\n>>diff --git a/templates/hooks--push-to-checkout.sample b/templates/hooks--push-to-checkout.sample \n>>new file mode 100755 \n>>index 0000000000..2c6e06f8f1 \n>>--- /dev/null \n>>+++ b/templates/hooks--push-to-checkout.sample \n>>@@ -0,0 +1,74 @@ \n>>+#!/bin/bash \n>\n>If we want to make this part of the sample hooks shown to everybody, \n>we should stick to /bin/sh if we could.  Do you have to rely on any \n>bash-ism that are not found in other shells to write this script, or \n>is this just shows your inertia that you always work with bash? \n\nI usually work with bash and zsh, and just forgot to convert it in \nthis case.\n\n>>+# The hook receives the commit with which the tip of the current \n>>+# branch is going to be updated: \n>>+commit=\"$1\" \n>\n>Strictly speaking, a parameter on the right hand side of an \n>assignment does not have to get dquoted to protect it from getting \n>munged at $IFS, so this can be \n>\n>\tcommit=$1\n\nAh, interesting thanks - not sure why it's taken me 25 years to \ndiscover that. \n\n-- >8 --\nSubject: [PATCH v2] hook: add sample template for push-to-checkout\n\nThe template is a more-or-less exact translation to shell of the C\ncode for the default behaviour for git's push-to-checkout hook defined\nin the push_to_deploy() function in builtin/receive-pack.c, to serve\nas a convenient starting point for modification.\n\nIt also contains relevant text extracted from the git-config(1) and\ngithooks(5) man pages.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n  templates/hooks--push-to-checkout.sample | 78 ++++++++++++++++++++++++\n  1 file changed, 78 insertions(+)\n  create mode 100755 templates/hooks--push-to-checkout.sample\n\ndiff --git a/templates/hooks--push-to-checkout.sample b/templates/hooks--push-to-checkout.sample\nnew file mode 100755\nindex 0000000000..af5a0c0018\n--- /dev/null\n+++ b/templates/hooks--push-to-checkout.sample\n@@ -0,0 +1,78 @@\n+#!/bin/sh\n+\n+# An example hook script to update a checked-out tree on a git push.\n+#\n+# This hook is invoked by git-receive-pack(1) when it reacts to git\n+# push and updates reference(s) in its repository, and when the push\n+# tries to update the branch that is currently checked out and the\n+# receive.denyCurrentBranch configuration variable is set to\n+# updateInstead.\n+#\n+# By default, such a push is refused if the working tree and the index\n+# of the remote repository has any difference from the currently\n+# checked out commit; when both the working tree and the index match\n+# the current commit, they are updated to match the newly pushed tip\n+# of the branch. This hook is to be used to override the default\n+# behaviour; however the code below reimplements the default behaviour\n+# as a starting point for convenient modification.\n+#\n+# The hook receives the commit with which the tip of the current\n+# branch is going to be updated:\n+commit=$1\n+\n+# It can exit with a non-zero status to refuse the push (when it does\n+# so, it must not modify the index or the working tree).\n+die () {\n+\techo >&2 \"$*\"\n+\texit 1\n+}\n+\n+# Or it can make any necessary changes to the working tree and to the\n+# index to bring them to the desired state when the tip of the current\n+# branch is updated to the new commit, and exit with a zero status.\n+#\n+# For example, the hook can simply run git read-tree -u -m HEAD \"$1\"\n+# in order to emulate git fetch that is run in the reverse direction\n+# with git push, as the two-tree form of git read-tree -u -m is\n+# essentially the same as git switch or git checkout that switches\n+# branches while keeping the local changes in the working tree that do\n+# not interfere with the difference between the branches.\n+\n+# The below is a more-or-less exact translation to shell of the C code\n+# for the default behaviour for git's push-to-checkout hook defined in\n+# the push_to_deploy() function in builtin/receive-pack.c.\n+#\n+# Note that the hook will be executed from the repository directory,\n+# not from the working tree, so if you want to perform operations on\n+# the working tree, you will have to adapt your code accordingly, e.g.\n+# by adding \"cd ..\" or using relative paths.\n+\n+if ! git update-index -q --ignore-submodules --refresh\n+then\n+\tdie \"Up-to-date check failed\"\n+fi\n+\n+if ! git diff-files --quiet --ignore-submodules --\n+then\n+\tdie \"Working directory has unstaged changes\"\n+fi\n+\n+# This is a rough translation of:\n+#\n+#   head_has_history() ? \"HEAD\" : EMPTY_TREE_SHA1_HEX\n+if git cat-file -e HEAD 2>/dev/null\n+then\n+\thead=HEAD\n+else\n+\thead=$(git hash-object -t tree --stdin </dev/null)\n+fi\n+\n+if ! git diff-index --quiet --cached --ignore-submodules $head --\n+then\n+\tdie \"Working directory has staged changes\"\n+fi\n+\n+if ! git read-tree -u -m \"$commit\"\n+then\n+\tdie \"Could not update working tree to new HEAD\"\n+fi\n-- \n2.28.0\n\n"}]}