{"thread":{"id":"42846","subject":"Server-side preventing some files from being overwritten","startedAt":"2016-07-14T15:37:15Z","lastAt":"2016-07-21T12:27:23Z","messageCount":12,"participants":["Thorsten Glaser","Stefan Beller","Junio C Hamano","Ævar Arnfjörð Bjarmason","Jakub Narębski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"291423","messageId":"alpine.DEB.2.20.1607141725390.25238@tglase.lan.tarent.de","threadId":"42846","inReplyTo":null,"subject":"Server-side preventing some files from being overwritten","fromName":"Thorsten Glaser","fromEmail":"t.glaser@tarent.de","sentAt":"2016-07-14T15:31:05Z","receivedAt":"2016-07-14T15:37:15Z","isPatch":false,"sender":{"key":"t.glaser@tarent.de","avatar":"https://avatars.githubusercontent.com/u/861078?v=4"},"body":"Hi *,\n\nis there a way, for example with some sort of pre-receive hook,\nto prevent some files from being overwritten by a push?\n\nUse case:\n\nIn some project, we use Flyway to manage the database schema,\nand Jenkins to automatically build master’s HEAD after each\npush (and install it, thus activating the schema files almost\nimmediately). Now, I wish to prevent coworkers from changing\nanything in the SQL subdirectory that has ever been pushed,\nforcing them to push new SQL files with ALTER statements instead.\n(Yes, I am aware this will likely make me even less liked. No,\nthis is not an issue.)\n\nAs for the comparison, only the changes between the previous\nHEAD of master and the new HEAD of master after the push would\nhave been accepted need to be taken into account; any intermediate\ncommits, merges, etc. are no problem (because Jenkins does not\nbuild them, and because, once a push fails, the developer will\nhave to add a commit reverting their change and moving it to\nanother file on top, I’m no friend of rewriting).\n\nFile matching would be “any files under a certain subdirectory”,\nnothing fancier than that.\n\nI’ve tried a web search (with two different search engines) for\n“git prevent pushed files from modification”, but this seems to\nonly show people who want to ignore local changes or somesuch…\n\nI’ve asked in IRC, but with no answer for hours I thought that\nmaybe this was the better place to ask for it.\n\nThanks in advance,\n//mirabilos\n-- \ntarent solutions GmbH\nRochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/\nTel: +49 228 54881-393 • Fax: +49 228 54881-235\nHRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941\nGeschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg\n"},{"id":"291437","messageId":"CAGZ79kb=2rpYucjhavNB_XHLk9rjKSoHzL9bwM5buDO0GyW3vw@mail.gmail.com","threadId":"42846","inReplyTo":"alpine.DEB.2.20.1607141725390.25238@tglase.lan.tarent.de","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-07-14T16:57:12Z","receivedAt":"2016-07-14T16:57:18Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Jul 14, 2016 at 8:31 AM, Thorsten Glaser <t.glaser@tarent.de> wrote:\n> Hi *,\n>\n> is there a way, for example with some sort of pre-receive hook,\n> to prevent some files from being overwritten by a push?\n\npre-receive hooks are a thing!\n\n pre-receive\n       This hook is invoked by git-receive-pack on the remote\nrepository, which happens when a git push is done on a local\nrepository. Just before starting to update refs on the remote\nrepository,\n       the pre-receive hook is invoked. Its exit status determines the\nsuccess or failure of the update.\n\n       This hook executes once for the receive operation. It takes no\narguments, but for each ref to be updated it receives on standard\ninput a line of the format:\n\n           <old-value> SP <new-value> SP <ref-name> LF\n\n       where <old-value> is the old object name stored in the ref,\n<new-value> is the new object name to be stored in the ref and\n<ref-name> is the full name of the ref. When creating a new ref,\n       <old-value> is 40 0.\n\n       If the hook exits with non-zero status, none of the refs will\nbe updated. If the hook exits with zero, updating of individual refs\ncan still be prevented by the update hook.\n\n       Both standard output and standard error output are forwarded to\ngit send-pack on the other end, so you can simply echo messages for\nthe user.\n\n\nAs you have access to the old and new value, just check them out to\n/tmp and inspect the files?\n(That is slower than optimal I'd assume, so to get it faster you could\ngo roughly like\n\n    git ls-tree -r <oldvalue> | grep <certain dir>  >old_interest\n    git ls-tree -r <newvalue> | grep <certain dir> >new_interest\n    if diff old_interest new_interest\n    then\n        echo \"There is a difference in  <certain dir>\n        exit 1\n    fi\n\n\n\n>\n> Use case:\n>\n> In some project, we use Flyway to manage the database schema,\n> and Jenkins to automatically build master’s HEAD after each\n> push (and install it, thus activating the schema files almost\n> immediately). Now, I wish to prevent coworkers from changing\n> anything in the SQL subdirectory that has ever been pushed,\n> forcing them to push new SQL files with ALTER statements instead.\n> (Yes, I am aware this will likely make me even less liked. No,\n> this is not an issue.)\n>\n> As for the comparison, only the changes between the previous\n> HEAD of master and the new HEAD of master after the push would\n> have been accepted need to be taken into account; any intermediate\n> commits, merges, etc. are no problem (because Jenkins does not\n> build them, and because, once a push fails, the developer will\n> have to add a commit reverting their change and moving it to\n> another file on top, I’m no friend of rewriting).\n>\n> File matching would be “any files under a certain subdirectory”,\n> nothing fancier than that.\n>\n> I’ve tried a web search (with two different search engines) for\n> “git prevent pushed files from modification”, but this seems to\n> only show people who want to ignore local changes or somesuch…\n>\n> I’ve asked in IRC, but with no answer for hours I thought that\n> maybe this was the better place to ask for it.\n>\n> Thanks in advance,\n> //mirabilos\n> --\n> tarent solutions GmbH\n> Rochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/\n> Tel: +49 228 54881-393 • Fax: +49 228 54881-235\n> HRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941\n> Geschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"291446","messageId":"alpine.DEB.2.20.1607141948530.25238@tglase.lan.tarent.de","threadId":"42846","inReplyTo":"CAGZ79kb=2rpYucjhavNB_XHLk9rjKSoHzL9bwM5buDO0GyW3vw@mail.gmail.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Thorsten Glaser","fromEmail":"t.glaser@tarent.de","sentAt":"2016-07-14T17:51:09Z","receivedAt":"2016-07-14T17:51:19Z","isPatch":false,"sender":{"key":"t.glaser@tarent.de","avatar":"https://avatars.githubusercontent.com/u/861078?v=4"},"body":"On Thu, 14 Jul 2016, Stefan Beller wrote:\n\n> go roughly like\n\nThanks, that did the trick!\n\nAlthough I’m ordinarily loath to write GNU bash scripts, this\nhelps avoiding temporary files. This works:\n\n-----cutting here may damage your screen surface-----\n#!/bin/bash\nexport LC_ALL=C\nsubdir=x/y\nwhile IFS=' ' read -r old new name; do\n\ttest x\"$name\" = x\"refs/heads/master\" || continue\n\tif test x\"0\" != x\"$(comm -23z \\\n\t    <(git ls-tree -r -z \"$old\" \"$subdir\" | sort -z) \\\n\t    <(git ls-tree -r -z \"$new\" \"$subdir\" | sort -z) | wc -c)\"; then\n\t\techo >&2 'Untouchable files touched, commit rejected!'\n\t\texit 1\n\tfi\ndone\nexit 0\n-----cutting here may damage your screen surface-----\n\nOf course, set “subdir” in line 3 correctly, and GNU coreutils\nare required for the NUL line termination, which is not an issue\nhere. (BSD has “-R ''” for sort(1), for example.)\n\nbye,\n//mirabilos\n-- \ntarent solutions GmbH\nRochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/\nTel: +49 228 54881-393 • Fax: +49 228 54881-235\nHRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941\nGeschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg\n"},{"id":"291451","messageId":"xmqqpoqgqdlz.fsf@gitster.mtv.corp.google.com","threadId":"42846","inReplyTo":"alpine.DEB.2.20.1607141948530.25238@tglase.lan.tarent.de","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T18:27:36Z","receivedAt":"2016-07-14T18:28:01Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thorsten Glaser <t.glaser@tarent.de> writes:\n\n> Although I’m ordinarily loath to write GNU bash scripts, this\n> helps avoiding temporary files. This works:\n>\n> -----cutting here may damage your screen surface-----\n> #!/bin/bash\n> export LC_ALL=C\n> subdir=x/y\n> while IFS=' ' read -r old new name; do\n> \ttest x\"$name\" = x\"refs/heads/master\" || continue\n> \tif test x\"0\" != x\"$(comm -23z \\\n> \t    <(git ls-tree -r -z \"$old\" \"$subdir\" | sort -z) \\\n> \t    <(git ls-tree -r -z \"$new\" \"$subdir\" | sort -z) | wc -c)\"; then\n> \t\techo >&2 'Untouchable files touched, commit rejected!'\n> \t\texit 1\n> \tfi\n\nCan't this become simpler, e.g.\n\n\tif ! git diff-tree --quiet \"$old\" \"$new\" -- \"$subdir\"\n\tthen\n\t\techo >&2 \"Ooh, $subdir is touched\"\n\t\texit 1\n\tfi\n\n> done\n> exit 0\n> -----cutting here may damage your screen surface-----\n>\n> Of course, set “subdir” in line 3 correctly, and GNU coreutils\n> are required for the NUL line termination, which is not an issue\n> here. (BSD has “-R ''” for sort(1), for example.)\n>\n> bye,\n> //mirabilos\n"},{"id":"291455","messageId":"CAPc5daXxAqXtUvs1Xr_vgncXqcvR8-VG67oDAgOQ8xPcT7nCeQ@mail.gmail.com","threadId":"42846","inReplyTo":"xmqqpoqgqdlz.fsf@gitster.mtv.corp.google.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T18:44:17Z","receivedAt":"2016-07-14T18:44:41Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Thu, Jul 14, 2016 at 11:27 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Thorsten Glaser <t.glaser@tarent.de> writes:\n>\n>>       if test x\"0\" != x\"$(comm -23z \\\n>>           <(git ls-tree -r -z \"$old\" \"$subdir\" | sort -z) \\\n>>           <(git ls-tree -r -z \"$new\" \"$subdir\" | sort -z) | wc -c)\"; then\n>>               echo >&2 'Untouchable files touched, commit rejected!'\n>>               exit 1\n>>       fi\n>\n> Can't this become simpler, e.g.\n>\n>         if ! git diff-tree --quiet \"$old\" \"$new\" -- \"$subdir\"\n>         then\n>                 echo >&2 \"Ooh, $subdir is touched\"\n>                 exit 1\n>         fi\n\nEhh, you need to tell diff-tree to recurse, i.e. \"diff-tree -r\".\n"},{"id":"291457","messageId":"alpine.DEB.2.20.1607142049240.25238@tglase.lan.tarent.de","threadId":"42846","inReplyTo":"xmqqpoqgqdlz.fsf@gitster.mtv.corp.google.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Thorsten Glaser","fromEmail":"t.glaser@tarent.de","sentAt":"2016-07-14T18:50:24Z","receivedAt":"2016-07-14T18:50:32Z","isPatch":false,"sender":{"key":"t.glaser@tarent.de","avatar":"https://avatars.githubusercontent.com/u/861078?v=4"},"body":"On Thu, 14 Jul 2016, Junio C Hamano wrote:\n\n> Can't this become simpler, e.g.\n> \n> \tif ! git diff-tree --quiet \"$old\" \"$new\" -- \"$subdir\"\n\nThought about diff-tree, but additions are permitted,\nand diffing the actual file content has overhead too.\n\nJust counting the number of object hashes removed from\nthe old tree (recursed) works out just fine.\n\nbye,\n//mirabilos\n-- \ntarent solutions GmbH\nRochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/\nTel: +49 228 54881-393 • Fax: +49 228 54881-235\nHRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941\nGeschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg\n"},{"id":"291458","messageId":"CAGZ79kYFb5UJ=cnXZTbcO3L4+wc5G24_pV2BhHkOH-+wM9CFJQ@mail.gmail.com","threadId":"42846","inReplyTo":"CAPc5daXxAqXtUvs1Xr_vgncXqcvR8-VG67oDAgOQ8xPcT7nCeQ@mail.gmail.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-07-14T18:50:27Z","receivedAt":"2016-07-14T18:50:36Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Jul 14, 2016 at 11:44 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> On Thu, Jul 14, 2016 at 11:27 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Thorsten Glaser <t.glaser@tarent.de> writes:\n>>\n>>>       if test x\"0\" != x\"$(comm -23z \\\n>>>           <(git ls-tree -r -z \"$old\" \"$subdir\" | sort -z) \\\n>>>           <(git ls-tree -r -z \"$new\" \"$subdir\" | sort -z) | wc -c)\"; then\n>>>               echo >&2 'Untouchable files touched, commit rejected!'\n>>>               exit 1\n>>>       fi\n>>\n>> Can't this become simpler, e.g.\n>>\n>>         if ! git diff-tree --quiet \"$old\" \"$new\" -- \"$subdir\"\n>>         then\n>>                 echo >&2 \"Ooh, $subdir is touched\"\n\nNo need to go for >&2 here, as it makes no difference to\nthe client.\n\n>>                 exit 1\n>>         fi\n>\n> Ehh, you need to tell diff-tree to recurse, i.e. \"diff-tree -r\".\n"},{"id":"291462","messageId":"xmqqbn20qbmr.fsf@gitster.mtv.corp.google.com","threadId":"42846","inReplyTo":"alpine.DEB.2.20.1607142049240.25238@tglase.lan.tarent.de","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T19:10:20Z","receivedAt":"2016-07-14T19:10:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thorsten Glaser <t.glaser@tarent.de> writes:\n\n> On Thu, 14 Jul 2016, Junio C Hamano wrote:\n>\n>> Can't this become simpler, e.g.\n>> \n>> \tif ! git diff-tree --quiet \"$old\" \"$new\" -- \"$subdir\"\n>\n> Thought about diff-tree, but additions are permitted,\n\n\tdiff-tree -r --diff-filter=a \"$old\" \"$new\" -- \"$subdir\" \n\nor something like that?\n\n> and diffing the actual file content has overhead too.\n\nIt is why exactly \"diff-tree\" is desired here, as you do not have to\ndiff actual file at all.\n"},{"id":"291539","messageId":"CACBZZX7YFQ3o+5wbVz0ca6__3ViD0b-qbrX06CkxhAHE8uQ6oQ@mail.gmail.com","threadId":"42846","inReplyTo":"CAPc5daXxAqXtUvs1Xr_vgncXqcvR8-VG67oDAgOQ8xPcT7nCeQ@mail.gmail.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2016-07-15T13:56:04Z","receivedAt":"2016-07-15T14:03:02Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Thu, Jul 14, 2016 at 8:44 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> On Thu, Jul 14, 2016 at 11:27 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Thorsten Glaser <t.glaser@tarent.de> writes:\n>>\n>>>       if test x\"0\" != x\"$(comm -23z \\\n>>>           <(git ls-tree -r -z \"$old\" \"$subdir\" | sort -z) \\\n>>>           <(git ls-tree -r -z \"$new\" \"$subdir\" | sort -z) | wc -c)\"; then\n>>>               echo >&2 'Untouchable files touched, commit rejected!'\n>>>               exit 1\n>>>       fi\n>>\n>> Can't this become simpler, e.g.\n>>\n>>         if ! git diff-tree --quiet \"$old\" \"$new\" -- \"$subdir\"\n>>         then\n>>                 echo >&2 \"Ooh, $subdir is touched\"\n>>                 exit 1\n>>         fi\n>\n> Ehh, you need to tell diff-tree to recurse, i.e. \"diff-tree -r\".\n\nNote that although this is literally what Thorsten is asking for, I\nthink it's worth noting for the list explicitly that all these\nexamples that do \"diff $old..$new\" will *not* prevent your repository\nfrom having *commits* that touch those files, but they will prevent\nyou from having *pushes* where the end state is a net change in those\nfiles.\n\nI.e. it allows pushing a series which is a series of two commits which:\n\n  1. Change the forbidden file(s)\n  2. Undo changes to the forbidden file(s)\n\nThis *can* be critically important or not matter at all depending on\nyour use case, i.e. does it matter that disallowed and potentially\nmalicious changes come up in \"git bisect\", or will you ever be rolling\nout anything but the latest tip of the branch you're testing in\nproduction?\n\nIf the answer to either of those is \"yes\" you need something that does\na \"git log --stat $old..$new\" and parses out if *any* of the commits\nmake changes to those files.\n\nSee e.g. my https://github.com/avar/pre-receive-reject-binaries for\none example of that, although it rejects binaries you could easily\nmodify it to check the filename(s) instead.\n"},{"id":"291542","messageId":"alpine.DEB.2.20.1607151729560.18668@tglase.lan.tarent.de","threadId":"42846","inReplyTo":"CACBZZX7YFQ3o+5wbVz0ca6__3ViD0b-qbrX06CkxhAHE8uQ6oQ@mail.gmail.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Thorsten Glaser","fromEmail":"t.glaser@tarent.de","sentAt":"2016-07-15T15:30:30Z","receivedAt":"2016-07-15T15:30:39Z","isPatch":false,"sender":{"key":"t.glaser@tarent.de","avatar":"https://avatars.githubusercontent.com/u/861078?v=4"},"body":"On Fri, 15 Jul 2016, Ævar Arnfjörð Bjarmason wrote:\n\n> I.e. it allows pushing a series which is a series of two commits which:\n> \n>   1. Change the forbidden file(s)\n>   2. Undo changes to the forbidden file(s)\n\nThat’s precisely allowed.\n\nbye,\n//mirabilos\n-- \ntarent solutions GmbH\nRochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/\nTel: +49 228 54881-393 • Fax: +49 228 54881-235\nHRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941\nGeschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg\n"},{"id":"291909","messageId":"5790B055.4060603@gmail.com","threadId":"42846","inReplyTo":"CAGZ79kb=2rpYucjhavNB_XHLk9rjKSoHzL9bwM5buDO0GyW3vw@mail.gmail.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-07-21T11:21:57Z","receivedAt":"2016-07-21T11:22:13Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 2016-07-14 o 18:57, Stefan Beller pisze:\n> On Thu, Jul 14, 2016 at 8:31 AM, Thorsten Glaser <t.glaser@tarent.de> wrote:\n>> Hi *,\n>>\n>> is there a way, for example with some sort of pre-receive hook,\n>> to prevent some files from being overwritten by a push?\n> \n> pre-receive hooks are a thing!\n\nOr you can use third-party tools with support for such cases, such\nas Gitolite.  VREFs allow to restrict pushes by the filename, see\nhttp://gitolite.com/gitolite/vref.html#NAME (though I am not sure\nif it checks only the end state, or if it checks every commit\nthat was pushed).\n\nBest,\n-- \nJakub Narębski\n"},{"id":"291910","messageId":"alpine.DEB.2.20.1607211426450.12455@tglase.lan.tarent.de","threadId":"42846","inReplyTo":"5790B055.4060603@gmail.com","subject":"Re: Server-side preventing some files from being overwritten","fromName":"Thorsten Glaser","fromEmail":"t.glaser@tarent.de","sentAt":"2016-07-21T12:27:15Z","receivedAt":"2016-07-21T12:27:23Z","isPatch":false,"sender":{"key":"t.glaser@tarent.de","avatar":"https://avatars.githubusercontent.com/u/861078?v=4"},"body":"On Thu, 21 Jul 2016, Jakub Narębski wrote:\n\n> Or you can use third-party tools with support for such cases, such\n\nNot if the repository is already managed by some other tool\n(FusionForge, in this case).\n\nThanks anyway,\n//mirabilos\n-- \ntarent solutions GmbH\nRochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/\nTel: +49 228 54881-393 • Fax: +49 228 54881-235\nHRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941\nGeschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg\n"}]}