{"thread":{"id":"45940","subject":"Best \"triangle\" workflow setup?","startedAt":"2017-05-11T19:41:48Z","lastAt":"2017-05-13T06:43:56Z","messageCount":6,"participants":["Robert Dailey","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"319447","messageId":"CAHd499DjBHsWCf8h+i4EstR7qs-9hkO4En5-B-WA17DoPZQNoQ@mail.gmail.com","threadId":"45940","inReplyTo":null,"subject":"Best \"triangle\" workflow setup?","fromName":"Robert Dailey","fromEmail":"rcdailey.lists@gmail.com","sentAt":"2017-05-11T19:41:41Z","receivedAt":"2017-05-11T19:41:48Z","isPatch":false,"sender":{"key":"rcdailey.lists@gmail.com","avatar":null},"body":"I know Git has evolved to support the \"triangle\" workflow model in\ndifferent ways, with the goal of making it better. However because\nthere are so many different options from separate push URLs for\nremotes to various ways to manage tracking branches, it's not clear to\nme which specific configuration suits this workflow the best.\n\nSo my situation is that I have 3 repositories: The original upstream\nrepository, a fork of that repository (also remote), and my local\nclone of the upstream repository.\n\nWhat I want (as a default) is for `git pull` to pull from the\nsame-named branch on the upstream repository, but for `git push` to\npush to the same-named branch on the fork repository. However to\noverride this behavior for when I want to push directly to upstream\nrepo, I should be able to use an explicit `git push origin my-topic`\n(but `git push` by default will act as `git push fork my-topic`).\n\nWhat is the best way to achieve this? Is there a different workflow\nfrom what I'm imagining that works a little better (in other words, I\ndon't need it to work *exactly* as I've described, mainly I just want\nto avoid accidentally pushing changes to the upstream repo in the\ndefault case when I want to push to the fork instead for pull\nrequest)?\n\nThanks in advance for any advice.\n"},{"id":"319454","messageId":"20170511201707.aobrdiompzg6wu5k@sigill.intra.peff.net","threadId":"45940","inReplyTo":"CAHd499DjBHsWCf8h+i4EstR7qs-9hkO4En5-B-WA17DoPZQNoQ@mail.gmail.com","subject":"Re: Best \"triangle\" workflow setup?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-05-11T20:17:08Z","receivedAt":"2017-05-11T20:17:16Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 11, 2017 at 02:41:41PM -0500, Robert Dailey wrote:\n\n> What I want (as a default) is for `git pull` to pull from the\n> same-named branch on the upstream repository, but for `git push` to\n> push to the same-named branch on the fork repository. However to\n> override this behavior for when I want to push directly to upstream\n> repo, I should be able to use an explicit `git push origin my-topic`\n> (but `git push` by default will act as `git push fork my-topic`).\n\nI think you want:\n\n  [push]\n  default = current\n  [remote]\n  pushDefault = myfork\n\nto make \"git push\" do what you want. And then generally have branches\nmark their counterparts on \"origin\" (which you can do either at creation\ntime, or probably by using \"git push -u origin my-topic\" when you push\nthem).\n\nThis is similar to what I do for my git.git workflow, though I usually\nhave origin/master as the branch's upstream. I.e., I'd create them with:\n\n  git checkout -b my-topic origin\n\nAnd then rebasing always happens on top of master (because \"origin\"\ndoesn't even have my topic branch at all). If I want to compare with\nwhat I've pushed to my fork, I'd use \"@{push}\".\n\n-Peff\n"},{"id":"319470","messageId":"CAHd499AvDRH-KZ7piOE=QsVXSpMt0u5jVXUC=7UPnRVLYo-U5Q@mail.gmail.com","threadId":"45940","inReplyTo":"20170511201707.aobrdiompzg6wu5k@sigill.intra.peff.net","subject":"Re: Best \"triangle\" workflow setup?","fromName":"Robert Dailey","fromEmail":"rcdailey.lists@gmail.com","sentAt":"2017-05-11T21:23:03Z","receivedAt":"2017-05-11T21:23:10Z","isPatch":false,"sender":{"key":"rcdailey.lists@gmail.com","avatar":null},"body":"On Thu, May 11, 2017 at 3:17 PM, Jeff King <peff@peff.net> wrote:\n> I think you want:\n>\n>   [push]\n>   default = current\n>   [remote]\n>   pushDefault = myfork\n>\n> to make \"git push\" do what you want. And then generally have branches\n> mark their counterparts on \"origin\" (which you can do either at creation\n> time, or probably by using \"git push -u origin my-topic\" when you push\n> them).\n\nSo without the `pushDefault` setting, `current` will default to a\nremote named `origin` if there is no tracking branch set, correct? So\n`pushDefault` is effectively overriding this built-in default? In\naddition, it seems like since this overrides `branch.name.remote`,\nthat this effectively makes the remote tracking branch *only* for\n`pull`. Is this a correct understanding?\n\n> This is similar to what I do for my git.git workflow, though I usually\n> have origin/master as the branch's upstream. I.e., I'd create them with:\n>\n>   git checkout -b my-topic origin\n\nI'm looking through the `git checkout` and `git branch` documentation,\nbut I don't see any mention of it being valid to use a remote name as\nthe <start-point> parameter (you're using `origin` in the above\nexample). Am I misunderstanding? Did you mean origin/my-topic?\n\n> And then rebasing always happens on top of master (because \"origin\"\n> doesn't even have my topic branch at all). If I want to compare with\n> what I've pushed to my fork, I'd use \"@{push}\".\n\nCan you explain more about how your rebase chooses master instead of\nyour same-named remote tracking branch? Maybe provide some examples of\nyour rebase command and respective configuration (unless what you've\nalready provided is sufficient). As for @{push}, I haven't used this\nbefore, so I'll dig in the docs and learn about it.\n\nThanks for your advice, so far I like this direction but seems like\nthere is more for me to learn!\n"},{"id":"319486","messageId":"20170511233640.ylf4to3hotngh6xe@sigill.intra.peff.net","threadId":"45940","inReplyTo":"CAHd499AvDRH-KZ7piOE=QsVXSpMt0u5jVXUC=7UPnRVLYo-U5Q@mail.gmail.com","subject":"Re: Best \"triangle\" workflow setup?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-05-11T23:36:41Z","receivedAt":"2017-05-11T23:36:52Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 11, 2017 at 04:23:03PM -0500, Robert Dailey wrote:\n\n> On Thu, May 11, 2017 at 3:17 PM, Jeff King <peff@peff.net> wrote:\n> > I think you want:\n> >\n> >   [push]\n> >   default = current\n> >   [remote]\n> >   pushDefault = myfork\n> >\n> > to make \"git push\" do what you want. And then generally have branches\n> > mark their counterparts on \"origin\" (which you can do either at creation\n> > time, or probably by using \"git push -u origin my-topic\" when you push\n> > them).\n> \n> So without the `pushDefault` setting, `current` will default to a\n> remote named `origin` if there is no tracking branch set, correct? So\n> `pushDefault` is effectively overriding this built-in default? In\n> addition, it seems like since this overrides `branch.name.remote`,\n> that this effectively makes the remote tracking branch *only* for\n> `pull`. Is this a correct understanding?\n\nRight. The general idea of a triangular workflow is that where you pull\nfrom is not the same as where you push to. We have branch.*.pushremote\nif you really wanted to do it on a per-branch basis, but in my\nexperience you almost always want to use \"myfork\", because you can't\npush to \"origin\" in the first place. :)\n\n> > This is similar to what I do for my git.git workflow, though I usually\n> > have origin/master as the branch's upstream. I.e., I'd create them with:\n> >\n> >   git checkout -b my-topic origin\n> \n> I'm looking through the `git checkout` and `git branch` documentation,\n> but I don't see any mention of it being valid to use a remote name as\n> the <start-point> parameter (you're using `origin` in the above\n> example). Am I misunderstanding? Did you mean origin/my-topic?\n\nUsing \"origin\" there will resolve to \"origin/HEAD\", i.e., origin/master.\nSo basically I am saying that all of my topic branches are based on\nmaster, and if I were to rebase them (for example), I'd want to rebase\nthe whole thing.\n\nIf I were to \"git pull\", they'd also pull from master, which may or may\nnot be what you want (though with pull.rebase, perhaps). I don't\ngenerally use \"git pull\" at all for my git.git workflow.\n\n> > And then rebasing always happens on top of master (because \"origin\"\n> > doesn't even have my topic branch at all). If I want to compare with\n> > what I've pushed to my fork, I'd use \"@{push}\".\n> \n> Can you explain more about how your rebase chooses master instead of\n> your same-named remote tracking branch? Maybe provide some examples of\n> your rebase command and respective configuration (unless what you've\n> already provided is sufficient). As for @{push}, I haven't used this\n> before, so I'll dig in the docs and learn about it.\n\nThe default for \"git rebase\" (if you don't specify a base) is the\nconfigured upstream, which in my case is origin/master. Most of my\nrebasing is \"rebase -i\" to rewrite bits, so it automatically picks all\nthe commits on my topic branch.\n\nMaybe it would help to set up a trivial example:\n\n  # just a helper to make dummy commits\n  commit() { echo \"$1\" >\"$1\" && git add \"$1\" && git commit -m \"$1\"; }\n\n  # some parent repo\n  git init parent\n  (cd parent && commit one)\n\n  # and imagine you have a public fork, too\n  git clone --bare parent myfork.git\n\n  # and then you have your local clone; in real life this is obviously\n  # the only one that would actually be on your machine, but this is a\n  # toy example\n  git clone parent local\n  cd local\n\n  # set up our triangular config\n  git remote add myfork ../myfork.git\n  git config remote.pushdefault myfork\n  git config push.default current\n\n  # now let's try a topic branch\n  git checkout -b topic origin\n  commit two\n  commit three\n\n  # config will show our topic based on origin/master:\n  #  [branch \"topic\"]\n  #\tremote = origin\n  #\tmerge = refs/heads/master\n  less .git/config\n\n  # this should default to all the commits in our topic (i.e., two, three)\n  git rebase -i\n\n  # let's imagine upstream makes more commits on master. We can \"pull\n  # --rebase\" to put our work on top\n  (cd ../parent && commit four)\n  git pull --rebase\n\n  # pushes go to the matching branch on myfork\n  git push\n\n  # if you want to see what you haven't pushed yet, you can use @{push}\n  commit five\n  git log @{push}..\n\n  # likewise, if you wanted to rebase only commits that you've been\n  # working on since your last push:\n  git rebase -i @{push}\n\n  # Now imagine \"origin\" picks up your branch...\n  (cd ../parent && git fetch ../myfork.git topic:topic)\n\n  # Depending on your project's workflow, you may want to consider that\n  # the new base for further development (and never rebase or rewrite\n  # any commits that origin has). You do that by re-pointing your\n  # @{upstream} config.\n  git fetch\n  git branch --set-upstream-to=origin/topic topic\n\n  # now a \"rebase -i\" would show only the commits origin doesn't have\n  # (five and six in this case)\n  commit six\n  git rebase -i\n\n\nHopefully that shows off some of the ways you can use the upstream and\npush config in practice.  Some people may not be as excited about the\n\"rebase\" default as I am. I spend quite a lot of time at the end of a\nseries sifting through the commits via \"rebase -i\" and polishing them\nup. I also test with \"git rebase -x 'make test'\".\n\n-Peff\n"},{"id":"319533","messageId":"CAHd499A=wwxFxZ2mq=PZcOBNRSiK1Xm_sE79AypBXQaYU_UcJQ@mail.gmail.com","threadId":"45940","inReplyTo":"20170511233640.ylf4to3hotngh6xe@sigill.intra.peff.net","subject":"Re: Best \"triangle\" workflow setup?","fromName":"Robert Dailey","fromEmail":"rcdailey.lists@gmail.com","sentAt":"2017-05-12T14:53:23Z","receivedAt":"2017-05-12T18:45:29Z","isPatch":false,"sender":{"key":"rcdailey.lists@gmail.com","avatar":null},"body":"On Thu, May 11, 2017 at 6:36 PM, Jeff King <peff@peff.net> wrote:\n> On Thu, May 11, 2017 at 04:23:03PM -0500, Robert Dailey wrote:\n>\n>> On Thu, May 11, 2017 at 3:17 PM, Jeff King <peff@peff.net> wrote:\n>> > I think you want:\n>> >\n>> >   [push]\n>> >   default = current\n>> >   [remote]\n>> >   pushDefault = myfork\n>> >\n>> > to make \"git push\" do what you want. And then generally have branches\n>> > mark their counterparts on \"origin\" (which you can do either at creation\n>> > time, or probably by using \"git push -u origin my-topic\" when you push\n>> > them).\n>>\n>> So without the `pushDefault` setting, `current` will default to a\n>> remote named `origin` if there is no tracking branch set, correct? So\n>> `pushDefault` is effectively overriding this built-in default? In\n>> addition, it seems like since this overrides `branch.name.remote`,\n>> that this effectively makes the remote tracking branch *only* for\n>> `pull`. Is this a correct understanding?\n>\n> Right. The general idea of a triangular workflow is that where you pull\n> from is not the same as where you push to. We have branch.*.pushremote\n> if you really wanted to do it on a per-branch basis, but in my\n> experience you almost always want to use \"myfork\", because you can't\n> push to \"origin\" in the first place. :)\n>\n>> > This is similar to what I do for my git.git workflow, though I usually\n>> > have origin/master as the branch's upstream. I.e., I'd create them with:\n>> >\n>> >   git checkout -b my-topic origin\n>>\n>> I'm looking through the `git checkout` and `git branch` documentation,\n>> but I don't see any mention of it being valid to use a remote name as\n>> the <start-point> parameter (you're using `origin` in the above\n>> example). Am I misunderstanding? Did you mean origin/my-topic?\n>\n> Using \"origin\" there will resolve to \"origin/HEAD\", i.e., origin/master.\n> So basically I am saying that all of my topic branches are based on\n> master, and if I were to rebase them (for example), I'd want to rebase\n> the whole thing.\n>\n> If I were to \"git pull\", they'd also pull from master, which may or may\n> not be what you want (though with pull.rebase, perhaps). I don't\n> generally use \"git pull\" at all for my git.git workflow.\n\nThanks, just curious, where in the git documentation is the \"origin\"\nto \"origin/HEAD\" resolution documented? I checked the git-revisions\npage but it doesn't seem to mention it there. Thanks for explaining\nthough.\n\nAlso is there a similar mechanism for \"track same-named branch on\nspecified remote\"? Something like \"origin/.\"? I follow git-flow\ndevelopment process, so topic branches on hotfix or release branches\nwill track origin/master (since origin/HEAD still points to master or\ndevelop). But I want to track \"origin/release/1.2.3\" without typing\nthe full thing. Basically would be nice if there was a lazy shorthand\nfor it similar to the \"origin/HEAD\" example you gave.\n\n\n>> > And then rebasing always happens on top of master (because \"origin\"\n>> > doesn't even have my topic branch at all). If I want to compare with\n>> > what I've pushed to my fork, I'd use \"@{push}\".\n>>\n>> Can you explain more about how your rebase chooses master instead of\n>> your same-named remote tracking branch? Maybe provide some examples of\n>> your rebase command and respective configuration (unless what you've\n>> already provided is sufficient). As for @{push}, I haven't used this\n>> before, so I'll dig in the docs and learn about it.\n>\n> The default for \"git rebase\" (if you don't specify a base) is the\n> configured upstream, which in my case is origin/master. Most of my\n> rebasing is \"rebase -i\" to rewrite bits, so it automatically picks all\n> the commits on my topic branch.\n>\n> Maybe it would help to set up a trivial example:\n>\n>   # just a helper to make dummy commits\n>   commit() { echo \"$1\" >\"$1\" && git add \"$1\" && git commit -m \"$1\"; }\n>\n>   # some parent repo\n>   git init parent\n>   (cd parent && commit one)\n>\n>   # and imagine you have a public fork, too\n>   git clone --bare parent myfork.git\n>\n>   # and then you have your local clone; in real life this is obviously\n>   # the only one that would actually be on your machine, but this is a\n>   # toy example\n>   git clone parent local\n>   cd local\n>\n>   # set up our triangular config\n>   git remote add myfork ../myfork.git\n>   git config remote.pushdefault myfork\n>   git config push.default current\n>\n>   # now let's try a topic branch\n>   git checkout -b topic origin\n>   commit two\n>   commit three\n>\n>   # config will show our topic based on origin/master:\n>   #  [branch \"topic\"]\n>   #     remote = origin\n>   #     merge = refs/heads/master\n>   less .git/config\n>\n>   # this should default to all the commits in our topic (i.e., two, three)\n>   git rebase -i\n>\n>   # let's imagine upstream makes more commits on master. We can \"pull\n>   # --rebase\" to put our work on top\n>   (cd ../parent && commit four)\n>   git pull --rebase\n>\n>   # pushes go to the matching branch on myfork\n>   git push\n>\n>   # if you want to see what you haven't pushed yet, you can use @{push}\n>   commit five\n>   git log @{push}..\n>\n>   # likewise, if you wanted to rebase only commits that you've been\n>   # working on since your last push:\n>   git rebase -i @{push}\n>\n>   # Now imagine \"origin\" picks up your branch...\n>   (cd ../parent && git fetch ../myfork.git topic:topic)\n>\n>   # Depending on your project's workflow, you may want to consider that\n>   # the new base for further development (and never rebase or rewrite\n>   # any commits that origin has). You do that by re-pointing your\n>   # @{upstream} config.\n>   git fetch\n>   git branch --set-upstream-to=origin/topic topic\n>\n>   # now a \"rebase -i\" would show only the commits origin doesn't have\n>   # (five and six in this case)\n>   commit six\n>   git rebase -i\n>\n>\n> Hopefully that shows off some of the ways you can use the upstream and\n> push config in practice.  Some people may not be as excited about the\n> \"rebase\" default as I am. I spend quite a lot of time at the end of a\n> series sifting through the commits via \"rebase -i\" and polishing them\n> up. I also test with \"git rebase -x 'make test'\".\n\nYes this is extremely helpful. I also like to default to rebase for\neverything. I have this in my global config:\n\n\n[pull]\n    rebase = true\n[branch]\n    autosetuprebase = always\n\n(Although I think autosetuprebase isn't needed anymore since\npull.rebase is introduced, but I keep both just in case)\n\nSo to summarize:\n\n* Pushing to fork only ever requires `git push`\n* Pushing to origin requires at least `git push origin` (for same-named branch)\n* Pulling from origin always uses remote tracking branch (set to\nmainline to merge latest into topic branch (single developer) or set\nto same-named branch for multiple developers)\n\nAnother improvement that it seems like I'll have is that I do not need\nto specify the branch name for my first-time pushes of a branch like I\nhad to do when `push.default` was set to `simple`. Back then I had to\ndo:\n\n$ git push -u origin HEAD\n\nNow all I gotta do is:\n\n$ git push\n\nI like this much better!! Thanks a ton!\n"},{"id":"319599","messageId":"20170513064348.wwnkle4bkhih2clx@sigill.intra.peff.net","threadId":"45940","inReplyTo":"CAHd499A=wwxFxZ2mq=PZcOBNRSiK1Xm_sE79AypBXQaYU_UcJQ@mail.gmail.com","subject":"Re: Best \"triangle\" workflow setup?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-05-13T06:43:48Z","receivedAt":"2017-05-13T06:43:56Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, May 12, 2017 at 09:53:23AM -0500, Robert Dailey wrote:\n\n> Thanks, just curious, where in the git documentation is the \"origin\"\n> to \"origin/HEAD\" resolution documented? I checked the git-revisions\n> page but it doesn't seem to mention it there. Thanks for explaining\n> though.\n\nIn gitrevisions(7), the <refname> section, rule 6:\n\n  otherwise, refs/remotes/<refname>/HEAD if it exists.\n\nAnd then the contents of origin/HEAD are discussed a bit in\ngit-remote(1), under set-head.\n\n> Also is there a similar mechanism for \"track same-named branch on\n> specified remote\"? Something like \"origin/.\"? I follow git-flow\n> development process, so topic branches on hotfix or release branches\n> will track origin/master (since origin/HEAD still points to master or\n> develop). But I want to track \"origin/release/1.2.3\" without typing\n> the full thing. Basically would be nice if there was a lazy shorthand\n> for it similar to the \"origin/HEAD\" example you gave.\n\nNo, I don't think there's a shorthand for \"the same-named branch\". But\nthere are two things that might help:\n\n  1. If you're always branching from release/1.2.3 instead of \"master\",\n     you can use \"git remote set-head\" to change it.\n\n  2. If you want a local branch \"foo\" to track refs/remotes/origin/foo\n     (which already exists) then the checkout-dwim should work for you:\n\n        $ git checkout foo\n\tBranch foo set up to track remote branch foo from origin.\n\tSwitched to a new branch 'foo'\n\n-Peff\n"}]}