{"thread":{"id":"6634","subject":"Pushing to a non-bare repository","startedAt":"2007-02-03T03:01:28Z","lastAt":"2007-02-03T21:18:04Z","messageCount":6,"participants":["Steven Grimm","Junio C Hamano","Simon 'corecode' Schubert","Linus Torvalds"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"33428","messageId":"45C3FB08.1020805@midwinter.com","threadId":"6634","inReplyTo":null,"subject":"Pushing to a non-bare repository","fromName":"Steven Grimm","fromEmail":"koreth@midwinter.com","sentAt":"2007-02-03T03:01:28Z","receivedAt":"2007-02-03T03:01:28Z","isPatch":false,"sender":{"key":"koreth@midwinter.com","avatar":"https://gravatar.com/avatar/71b4d2e8b62f168bdc9e9205341159e3567003b4f9e2127c617c5fa0a1f5bad2?d=mp&s=160"},"body":"This keeps coming up as I introduce people to git. In previous \ndiscussions, the conclusion often seems to be that users don't really \nknow what they want the semantics to be. So here's what I want. I think \nit's actually not that complicated to get something that'll be far more \nuseful than what we have today.\n\n1. If the target repo has a branch other than the target branch checked \nout, allow the push and don't touch the working copy. (By \"allow the \npush\" here I mean proceed with the existing push behavior, which might \nstill error out if, e.g., the target branch has commits that aren't in \nthe local repo yet.)\n\n2. If the target repo has the target branch checked out and the working \ncopy equals its head, allow the push and reset the working copy to the \nnewly created head. Basically this is equivalent to a simple \nfast-forward pull in the other direction.\n\n3. If the target repo has the target branch checked out and there are \nuncommitted edits, block the push and return an error indicating the \ntarget repo is being edited and can't be pushed to until the edits are \nundone or committed.\n\nThat's it. No merging of local edits in the target, just the same \nsemantics as a push to a bare repo (in case 1) or a fast-forward \noperation with no merging (case 2). Would it be even nicer to have some \nautomatic merge magic in case 3? Maybe, but cases 1 and 2 are still very \nuseful without it.\n\nIn particular, those two cases would completely cover the two situations \nwhere I most often want to push to a checked-out parent: a common \nintegration area that we can browse manually or point a Web server or a \ncompiler at as a source directory, and (more important to me) a \nbidirectional sync mechanism between my laptop and my server, where I \nwant to commit my changes then send them to the other side to continue \nworking. Right now git requires me to set up a dummy parent repository \nthat both my laptop and my server repositories are clones of, and I have \nto run an extra \"pull\" after each push to get the changes where I want \nthem. No purpose is served by that parent repository except to satisfy \ngit's requirement that a push target has to be bare.\n\nAnother place where the current situation is a bit weak is when I want \nto clone someone's repo to help them fix a bug. If I clone it onto my \nlaptop, I end up having to email diffs back; our corporate network \ndoesn't allow us to make TCP connections to our laptops, only from our \nlaptops, so it is impossible for the other person to log onto the server \nand pull from my clone. If I could push back to a topic branch in the \nother person's repo, they could merge my change into their working copy \nat their leisure.\n\nIs this actually more complicated than the above? What am I missing?\n\n-Steve\n"},{"id":"33430","messageId":"7vfy9ndiqp.fsf@assigned-by-dhcp.cox.net","threadId":"6634","inReplyTo":"45C3FB08.1020805@midwinter.com","subject":"Re: Pushing to a non-bare repository","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-03T03:26:22Z","receivedAt":"2007-02-03T03:26:22Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Steven Grimm <koreth@midwinter.com> writes:\n\n> This keeps coming up as I introduce people to git. In previous\n> discussions, the conclusion often seems to be that users don't really\n> know what they want the semantics to be.\n\nI do not think that was the conclusion.  My suggestion to you\nwould be to stop thinking as if push is a converse of pull\n(because it is not; push is a counterpart of fetch), and leave\npush as is.\n\nIn a typical mothership-satellite configuration, I would\nrecommend a workflow like this:\n\nOn Mothership:\n\tremote.satellite.url: satellite:project.git\n        remote.satellite.push: refs/heads/master:remotes/mothership/master\n\nOn Satellite:\n\tremote.mothership.url: mothership:project.git\n        remote.mothership.fetch: refs/heads/master:remotes/mothership/master\n\nSo when you happen to be on the mothership and you would want to\nsync the satellite (because you are tired of working at your\noffice and would want to continue on your satellite notebook\nsitting on the couch, relaxed), you would push:\n\n\tmothership$ git push satellite\n\nThen you go to the satellite machine, and merge the mothership in:\n\n\tsatellite$ git merge mothership\n\nIf you forgot to push before leaving the mothership, you do not\nhave to worry; you can initiate the transfer from the satellite:\n\n\tsatellite$ git fetch mothership\n\nand then do the same merge:\n\n        satellite$ git merge mothership\n\nOf course you could do \"git pull mothership\" to do the above two\nas a single step, but the point is then the \"patch flow\" would\nalways be the same no matter from which side you initiate the\ntransfer from mothership to satellite.  The changes made on the\nmothership will be done on 'master' branch on the mothership and\nflow into remote/mothership/master on the satellite.  And you\nwill merge changes from the mothership to the satellite always\nvia remotes/mothership/master tracking branch.\n\nTo go the other way, you would further define these two:\n\nOn Mothership:\n        remote.satellite.fetch: refs/heads/master:remotes/satellite/master\n\nOn Satellite:\n        remote.mothership.push: refs/heads/master:remotes/satellite/master\n\nand \"git push mothership\" from satellite, or \"git fetch\nsatellite\" from the mothership.\n\nAnother reason not to do what you described inside git-push is\nthat you do not need to -- I think you should be able to do all\nthat from your update (or post-update) hook.\n"},{"id":"33440","messageId":"45C459DD.8080201@fs.ei.tum.de","threadId":"6634","inReplyTo":"7vfy9ndiqp.fsf@assigned-by-dhcp.cox.net","subject":"Re: Pushing to a non-bare repository","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-02-03T09:46:05Z","receivedAt":"2007-02-03T09:46:05Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Junio C Hamano wrote:\n>> This keeps coming up as I introduce people to git. In previous\n>> discussions, the conclusion often seems to be that users don't really\n>> know what they want the semantics to be.\n> \n> I do not think that was the conclusion.  My suggestion to you\n> would be to stop thinking as if push is a converse of pull\n> (because it is not; push is a counterpart of fetch), and leave\n> push as is.\n\nHowever, your suggestion does not help people who don't know better.  Pushing into the HEAD branch of another repo breaks stuff there.  Badly.  This should be prevented, really.\n\nI don't say \"Push needs to behave like a fast forward pull\", because that's wrong.  You can't just change the workdir, possibly due to permissions problems.  But push has to abort if you try to forward HEAD on the remote, except if it is bare, of course.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"33470","messageId":"7vabzv58q4.fsf@assigned-by-dhcp.cox.net","threadId":"6634","inReplyTo":"45C459DD.8080201@fs.ei.tum.de","subject":"Re: Pushing to a non-bare repository","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-03T19:42:11Z","receivedAt":"2007-02-03T19:42:11Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Simon 'corecode' Schubert <corecode@fs.ei.tum.de> writes:\n\n> I don't say \"Push needs to behave like a fast forward pull\",\n> because that's wrong.  You can't just change the workdir,\n> possibly due to permissions problems.  But push has to abort\n> if you try to forward HEAD on the remote, except if it is\n> bare, of course.\n\nYou can tweak receive-pack.c::update() to do that, perhaps along\nthe lines of this.\n\n---\ndiff --git a/receive-pack.c b/receive-pack.c\nindex 7311c82..ccd48e4 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -9,6 +9,7 @@\n \n static const char receive_pack_usage[] = \"git-receive-pack <git-dir>\";\n \n+static char *current_branch;\n static int deny_non_fast_forwards = 0;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n@@ -115,6 +116,18 @@ static int update(struct command *cmd)\n \t\t\t     name);\n \t}\n \n+\tif (!is_bare_repository()) {\n+\t\t/*\n+\t\t * Pusing into a live repository.  updating .git/HEAD\n+\t\t * or the current branch is a no-no...\n+\t\t */\n+\t\tif (strncmp(name, \"refs/\", 5) ||\n+\t\t    (current_branch && !strcmp(name, current_branch))) {\n+\t\t\tcmd->error_string = \"update current\";\n+\t\t\treturn error(\"refusing to update the current branch '%s' in a live repository\", name);\n+\t\t}\n+\t}\n+\n \tstrcpy(new_hex, sha1_to_hex(new_sha1));\n \tstrcpy(old_hex, sha1_to_hex(old_sha1));\n \n@@ -436,6 +449,13 @@ int main(int argc, char **argv)\n \telse if (0 <= receive_unpack_limit)\n \t\tunpack_limit = receive_unpack_limit;\n \n+\tif (!is_bare_repository()) {\n+\t\tunsigned char h[20];\n+\t\tconst char *head = resolve_ref(\"HEAD\", h, 0, NULL);\n+\t\tif (head && strcmp(\"HEAD\", head))\n+\t\t\tcurrent_branch = xstrdup(head);\n+\t}\n+\n \twrite_head_info();\n \n \t/* EOF */\n"},{"id":"33473","messageId":"Pine.LNX.4.64.0702031146050.8424@woody.linux-foundation.org","threadId":"6634","inReplyTo":"45C459DD.8080201@fs.ei.tum.de","subject":"Re: Pushing to a non-bare repository","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-02-03T19:48:46Z","receivedAt":"2007-02-03T19:48:46Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 3 Feb 2007, Simon 'corecode' Schubert wrote:\n> \n> However, your suggestion does not help people who don't know better.  Pushing\n> into the HEAD branch of another repo breaks stuff there.  Badly.  This should\n> be prevented, really.\n\nNo, please don't break it. I do it all the time.\n\nI just do \"git push remote\".\n\nAnd then eventually on the remote end I do end up having to \"update\", but \nthat's a simple \"git checkout -f\".\n\nIt's actually really convenient. It works even if the remote end had \ndirty stuff in their tree, and \"git diff\" still works (because the push \ndidn't update the index), and a simple \"git diff HEAD\" shows that \"oops, \nwe're not up-to-date\".\n\nYeah, I guess it's a bit error-prone (\"Why does 'git log' say I have a new \nversion, but the working tree doesn't seem new?\"), but it actually does \nwork. \n\n\t\tLinus\n"},{"id":"33484","messageId":"7vd54r2b5f.fsf@assigned-by-dhcp.cox.net","threadId":"6634","inReplyTo":"Pine.LNX.4.64.0702031146050.8424@woody.linux-foundation.org","subject":"Re: Pushing to a non-bare repository","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-03T21:18:04Z","receivedAt":"2007-02-03T21:18:04Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"(sorry, resending to avoid vger spam filter)\n\nLinus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Sat, 3 Feb 2007, Simon 'corecode' Schubert wrote:\n>> \n>> However, your suggestion does not help people who don't know better.  Pushing\n>> into the HEAD branch of another repo breaks stuff there.  Badly.  This should\n>> be prevented, really.\n>\n> No, please don't break it. I do it all the time.\n>\n> I just do \"git push remote\".\n>\n> And then eventually on the remote end I do end up having to \"update\", but \n> that's a simple \"git checkout -f\".\n\nI do not think anybody is disputing the fact that the current\ngit-push does work as expected.\n\nBut expectation is a relative thing.  The current behaviour is\nlikely to confuse new people; it is like a sharp-edged razor\nthat can cut through fingers if the user does not know which end\nis the handle and which end is the blade.\n\nI sent a \"if Simon wanted to do it, it is simple\" concept patch,\nbut obviously Simon or anybody can do the same check in the\nupdate hook if needed.  However, that would make the newbie\nsafety opt_in, and there inevitably will come a discussion on\nwhat should be the default.\n\nAnd I suspect that the recent trend is that people would want\nthe default to be more newbie friendly, while leaving an opt_in\nescape hatch for power-user convenience.  Obviously my patch\ndoes not have that escape hatch, and as such it is not meant for\ninclusion as-is.\n"}]}