{"thread":{"id":"40753","subject":"how to check for uncommitted/unstaged changes on remote side before pushing","startedAt":"2015-11-08T21:23:20Z","lastAt":"2015-11-09T09:38:20Z","messageCount":4,"participants":["Marc Haber","Dennis Kaarsemaker"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"273071","messageId":"20151108212320.GA18762@torres.zugschlus.de","threadId":"40753","inReplyTo":null,"subject":"how to check for uncommitted/unstaged changes on remote side before pushing","fromName":"Marc Haber","fromEmail":"mh+git@zugschlus.de","sentAt":"2015-11-08T21:23:20Z","receivedAt":"2015-11-08T21:23:20Z","isPatch":false,"sender":{"key":"mh+git@zugschlus.de","avatar":null},"body":"Hi,\n\nI am trying to abuse git as a code distribution channel and would like\nto be able to trigger redistribution just by git push.\n\nThe idea is to push to a remote to the branch that is currently\nchecked out followed by a git reset --hard in the post-receive hook. I\nhave already figured out that I need to set receive.denyCurrentBranch\nto ignore to be able to push to the currently checked out branch.\n\nI am also aware that it is a good idea to git pull before git push\njust in case there were local commits on the remote.\n\ngit reset --hard will unconditionally throw away local uncommitted\nchanges. I would like to detect this situation on the remote and abort\nthe receive progress. But my pre-receive hook does not work as\nintended. Here is my code:\n\n#!/bin/bash\n\necho \"this is the pre-receive hook on $HOSTNAME\"\n\nif [ \"$(git status --porcelain | wc -l)\" -ne 0 ]; then\n  echo \"there are uncommitted changes\"\n  echo \"PWD=$PWD\"\n  echo \"git status --porcelain\"\n  git status --porcelain\n  echo \"end git status\"\n  exit 1\nfi\n\nWhen I invoke my hook from the remote, everything is fine:\n\nmh@jessie:~/.stdacct$ .git/hooks/pre-receive\nthis is the pre-receive hook on jessie\nthere are uncommitted changes\nPWD=/home/mh/.stdacct\ngit status --porcelain\n M dotfiles/.bashrc\n M hooks/pre-receive-hook\n?? hooks/pre-receive-hook~\nend git status\nmh@jessie:~/.stdacct$ \n\nHowever, the output is different when invoked during a push:\n\n[7/504]mh@swivel:~/git/stdacct$ git push jessie\nCounting objects: 4, done.\nDelta compression using up to 8 threads.\nCompressing objects: 100% (4/4), done.\nWriting objects: 100% (4/4), 363 bytes | 0 bytes/s, done.\nTotal 4 (delta 3), reused 0 (delta 0)\nremote: this is the pre-receive hook on jessie\nremote: there are uncommitted changes\nremote: PWD=/home/mh/.stdacct/.git\nremote: git status --porcelain\nremote: ?? HEAD\nremote: ?? ORIG_HEAD\nremote: ?? config\nremote: ?? description\nremote: ?? hooks/applypatch-msg.sample\nremote: ?? hooks/commit-msg.sample\nremote: ?? hooks/post-receive\nremote: ?? hooks/post-update.sample\nremote: ?? hooks/pre-applypatch.sample\nremote: ?? hooks/pre-commit.sample\nremote: ?? hooks/pre-push.sample\nremote: ?? hooks/pre-rebase.sample\nremote: ?? hooks/pre-receive\nremote: ?? hooks/prepare-commit-msg.sample\nremote: ?? hooks/update.sample\nremote: ?? index\nremote: ?? index.lock\nremote: ?? info/\nremote: ?? logs/\nremote: ?? objects/\nremote: ?? refs/\nremote: end git status\nTo ssh://jessie/home/mh/.stdacct\n ! [remote rejected] jessie -> jessie (pre-receive hook declined)\nerror: failed to push some refs to 'ssh://jessie/home/mh/.stdacct'\n\nIt looks like the hook thinks that it is invoked inside the .git/hooks\ndirectory which is not considered part of the repository.\n\nWhat is going wrong here?\n\nIf my entire approach is wrong, what is the recommended way to prevent\na repository with unstaged or uncommitted changes from being pushed to?\n\nGreetings\nMarc\n\n\n-- \n-----------------------------------------------------------------------------\nMarc Haber         | \"I don't trust Computers. They | Mailadresse im Header\nLeimen, Germany    |  lose things.\"    Winona Ryder | Fon: *49 6224 1600402\nNordisch by Nature |  How to make an American Quilt | Fax: *49 6224 1600421\n"},{"id":"273073","messageId":"1447057500.5074.8.camel@kaarsemaker.net","threadId":"40753","inReplyTo":"20151108212320.GA18762@torres.zugschlus.de","subject":"Re: how to check for uncommitted/unstaged changes on remote side before pushing","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2015-11-09T08:25:00Z","receivedAt":"2015-11-09T08:25:00Z","isPatch":false,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On zo, 2015-11-08 at 22:23 +0100, Marc Haber wrote:\n> Hi,\n> \n> I am trying to abuse git as a code distribution channel and would\n> like\n> to be able to trigger redistribution just by git push.\n\n[insert obligatory remark about git not being a deployment tool]\n\n> The idea is to push to a remote to the branch that is currently\n> checked out followed by a git reset --hard in the post-receive hook.\n> I\n> have already figured out that I need to set receive.denyCurrentBranch\n> to ignore to be able to push to the currently checked out branch.\n\nYou'll need a new enough git, so you can set it to updateInstead (and\nmaybe use a push-to-checkout hook).\n\n> I am also aware that it is a good idea to git pull before git push\n> just in case there were local commits on the remote.\n\nNo, hooks should never pull, merge or do anything that could be\ninteractive.\n\n> git reset --hard will unconditionally throw away local uncommitted\n> changes. I would like to detect this situation on the remote and\n> abort\n> the receive progress. But my pre-receive hook does not work as\n> intended. Here is my code:\n>\n> [snip code]\n>\n> What is going wrong here?\n\nYou mention a post-receive hook first, but have written a pre-receive\nhook. Not sure if that's what you intended (or even if that's what's\ngoing wrong).\n\n> If my entire approach is wrong, what is the recommended way to \n> prevent a repository with unstaged or uncommitted changes from being \n> pushed to?\n\nPush-to-checkout is a very simplistic way of deploying and while it\nworks in simple cases, I'd not recommend it. \n\nTwo safer/saner approaches are:\n- Have a separate non-bare repo, and make the post-receive hook in a\n  bare repo trigger a fetch+reset in the non-bare one\n- Use git archive and symlink trickery for even better deploys\n\nQuestions like this come up in #git all the time, so I wrote up a few\nmore detailed recipes here, including working hooks and config for all\nthree ways of deploying: \nhttp://git.seveas.net/simple-deployments-with-git.html\n\n-- \nDennis Kaarsemaker\nwww.kaarsemaker.net\n"},{"id":"273074","messageId":"20151109093124.GB18762@torres.zugschlus.de","threadId":"40753","inReplyTo":"1447057500.5074.8.camel@kaarsemaker.net","subject":"Re: how to check for uncommitted/unstaged changes on remote side before pushing","fromName":"Marc Haber","fromEmail":"mh+git@zugschlus.de","sentAt":"2015-11-09T09:31:24Z","receivedAt":"2015-11-09T09:31:24Z","isPatch":false,"sender":{"key":"mh+git@zugschlus.de","avatar":null},"body":"Hi Dennis,\n\nthanks for your comments. I appreciate that.\n\nOn Mon, Nov 09, 2015 at 09:25:00AM +0100, Dennis Kaarsemaker wrote:\n> On zo, 2015-11-08 at 22:23 +0100, Marc Haber wrote:\n> > Hi,\n> > \n> > I am trying to abuse git as a code distribution channel and would\n> > like\n> > to be able to trigger redistribution just by git push.\n> \n> [insert obligatory remark about git not being a deployment tool]\n\nThat's why I said \"abuse\" ;-)\n\nAbusing git here has the advantage that one can save local changes and\nmerge them, which is handy for the task at hand (which is deploying my\ndotfiles to \"my\" servers).\n\n> > The idea is to push to a remote to the branch that is currently\n> > checked out followed by a git reset --hard in the post-receive hook.\n> > I\n> > have already figured out that I need to set receive.denyCurrentBranch\n> > to ignore to be able to push to the currently checked out branch.\n> \n> You'll need a new enough git, so you can set it to updateInstead (and\n> maybe use a push-to-checkout hook).\n\nThat's a nice new feature. Unfortunately even the git 2.1.4 which is\nin current Debian stable does not support that yet, but I think I can\nlive with a backport, and in the past git has shown to be reasonably\neasy to backport.\n\n> > I am also aware that it is a good idea to git pull before git push\n> > just in case there were local commits on the remote.\n> \n> No, hooks should never pull, merge or do anything that could be\n> interactive.\n\nI mean that I would manually pull on the master before pushing to the\nremote, thus making sure that the push will go through fine. With the\npull, I would get all committed remote changes merged locally on the\nmaster, leaving uncommitted and unstaged changes to worry about.\n\n> > git reset --hard will unconditionally throw away local uncommitted\n> > changes. I would like to detect this situation on the remote and\n> > abort\n> > the receive progress. But my pre-receive hook does not work as\n> > intended. Here is my code:\n> >\n> > [snip code]\n> >\n> > What is going wrong here?\n> \n> You mention a post-receive hook first, but have written a pre-receive\n> hook. Not sure if that's what you intended (or even if that's what's\n> going wrong).\n\nMy intention was having a pre-receive hook check for uncommitted or\nunstaged changes and abort the process if there were any. If there\nwere none, the push would go through and a post-receive hook would\nupdate the working copy.\n\nWith updateInstead, I could probably go without the post-receive hook,\nbut in my understanding, the check for uncommitted/unstaged changes\nwould still be handy and helpful.\n\n> > If my entire approach is wrong, what is the recommended way to \n> > prevent a repository with unstaged or uncommitted changes from being \n> > pushed to?\n> \n> Push-to-checkout is a very simplistic way of deploying and while it\n> works in simple cases, I'd not recommend it.\n> \n> Two safer/saner approaches are:\n> - Have a separate non-bare repo, and make the post-receive hook in a\n>   bare repo trigger a fetch+reset in the non-bare one\n> - Use git archive and symlink trickery for even better deploys\n\nBoth these approaches would make it harder or even impossible to merge\nlocal changes done on the remotes.\n\n> Questions like this come up in #git all the time, so I wrote up a few\n> more detailed recipes here, including working hooks and config for all\n> three ways of deploying: \n> http://git.seveas.net/simple-deployments-with-git.html\n\nThanks, that is appreciated. I refrained from coming to #git since my\nexperience is that complex issues are hard to manage on IRC. I have,\nhowever just joined as Zugschlus, so if you want to talk to me\ndirectly feel free to do so.\n\nGreetings\nMarc\n\n-- \n-----------------------------------------------------------------------------\nMarc Haber         | \"I don't trust Computers. They | Mailadresse im Header\nLeimen, Germany    |  lose things.\"    Winona Ryder | Fon: *49 6224 1600402\nNordisch by Nature |  How to make an American Quilt | Fax: *49 6224 1600421\n"},{"id":"273075","messageId":"1447061900.5074.12.camel@kaarsemaker.net","threadId":"40753","inReplyTo":"20151109093124.GB18762@torres.zugschlus.de","subject":"Re: how to check for uncommitted/unstaged changes on remote side before pushing","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2015-11-09T09:38:20Z","receivedAt":"2015-11-09T09:38:20Z","isPatch":false,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On ma, 2015-11-09 at 10:31 +0100, Marc Haber wrote:\n> Abusing git here has the advantage that one can save local changes \n> and merge them, which is handy for the task at hand (which is \n> deploying my dotfiles to \"my\" servers).\n\nFor this I really like vcsh (https://github.com/RichiH/vcsh/) in\ncombination with a .bashrc.d snippet that updates dotfiles upon login\nwhen possible (\nhttps://github.com/seveas/dotfiles/blob/master/.bashrc.d/vcsh.sh)\n\n\n-- \nDennis Kaarsemaker\nwww.kaarsemaker.net\n"}]}