Re: Best "triangle" workflow setup?
- From
- Robert Dailey <rcdailey.lists@gmail.com>
- Date
- May 12, 2017, 14:53 UTC
- Message-ID
- <CAHd499A=wwxFxZ2mq=PZcOBNRSiK1Xm_sE79AypBXQaYU_UcJQ@mail.gmail.com>
- In-Reply-To
- <20170511233640.ylf4to3hotngh6xe@sigill.intra.peff.net>
On Thu, May 11, 2017 at 6:36 PM, Jeff King <peff@peff.net> wrote:
Show 46 quoted lines
> On Thu, May 11, 2017 at 04:23:03PM -0500, Robert Dailey wrote: > >> On Thu, May 11, 2017 at 3:17 PM, Jeff King <peff@peff.net> wrote: >> > I think you want: >> > >> > [push] >> > default = current >> > [remote] >> > pushDefault = myfork >> > >> > to make "git push" do what you want. And then generally have branches >> > mark their counterparts on "origin" (which you can do either at creation >> > time, or probably by using "git push -u origin my-topic" when you push >> > them). >> >> So without the `pushDefault` setting, `current` will default to a >> remote named `origin` if there is no tracking branch set, correct? So >> `pushDefault` is effectively overriding this built-in default? In >> addition, it seems like since this overrides `branch.name.remote`, >> that this effectively makes the remote tracking branch *only* for >> `pull`. Is this a correct understanding? > > Right. The general idea of a triangular workflow is that where you pull > from is not the same as where you push to. We have branch.*.pushremote > if you really wanted to do it on a per-branch basis, but in my > experience you almost always want to use "myfork", because you can't > push to "origin" in the first place. :) > >> > This is similar to what I do for my git.git workflow, though I usually >> > have origin/master as the branch's upstream. I.e., I'd create them with: >> > >> > git checkout -b my-topic origin >> >> I'm looking through the `git checkout` and `git branch` documentation, >> but I don't see any mention of it being valid to use a remote name as >> the <start-point> parameter (you're using `origin` in the above >> example). Am I misunderstanding? Did you mean origin/my-topic? > > Using "origin" there will resolve to "origin/HEAD", i.e., origin/master. > So basically I am saying that all of my topic branches are based on > master, and if I were to rebase them (for example), I'd want to rebase > the whole thing. > > If I were to "git pull", they'd also pull from master, which may or may > not be what you want (though with pull.rebase, perhaps). I don't > generally use "git pull" at all for my git.git workflow.
Thanks, just curious, where in the git documentation is the "origin" to "origin/HEAD" resolution documented? I checked the git-revisions page but it doesn't seem to mention it there. Thanks for explaining though.
Also is there a similar mechanism for "track same-named branch on specified remote"? Something like "origin/."? I follow git-flow development process, so topic branches on hotfix or release branches will track origin/master (since origin/HEAD still points to master or develop). But I want to track "origin/release/1.2.3" without typing the full thing. Basically would be nice if there was a lazy shorthand for it similar to the "origin/HEAD" example you gave.
Show 89 quoted lines
>> > And then rebasing always happens on top of master (because "origin"
>> > doesn't even have my topic branch at all). If I want to compare with
>> > what I've pushed to my fork, I'd use "@{push}".
>>
>> Can you explain more about how your rebase chooses master instead of
>> your same-named remote tracking branch? Maybe provide some examples of
>> your rebase command and respective configuration (unless what you've
>> already provided is sufficient). As for @{push}, I haven't used this
>> before, so I'll dig in the docs and learn about it.
>
> The default for "git rebase" (if you don't specify a base) is the
> configured upstream, which in my case is origin/master. Most of my
> rebasing is "rebase -i" to rewrite bits, so it automatically picks all
> the commits on my topic branch.
>
> Maybe it would help to set up a trivial example:
>
> # just a helper to make dummy commits
> commit() { echo "$1" >"$1" && git add "$1" && git commit -m "$1"; }
>
> # some parent repo
> git init parent
> (cd parent && commit one)
>
> # and imagine you have a public fork, too
> git clone --bare parent myfork.git
>
> # and then you have your local clone; in real life this is obviously
> # the only one that would actually be on your machine, but this is a
> # toy example
> git clone parent local
> cd local
>
> # set up our triangular config
> git remote add myfork ../myfork.git
> git config remote.pushdefault myfork
> git config push.default current
>
> # now let's try a topic branch
> git checkout -b topic origin
> commit two
> commit three
>
> # config will show our topic based on origin/master:
> # [branch "topic"]
> # remote = origin
> # merge = refs/heads/master
> less .git/config
>
> # this should default to all the commits in our topic (i.e., two, three)
> git rebase -i
>
> # let's imagine upstream makes more commits on master. We can "pull
> # --rebase" to put our work on top
> (cd ../parent && commit four)
> git pull --rebase
>
> # pushes go to the matching branch on myfork
> git push
>
> # if you want to see what you haven't pushed yet, you can use @{push}
> commit five
> git log @{push}..
>
> # likewise, if you wanted to rebase only commits that you've been
> # working on since your last push:
> git rebase -i @{push}
>
> # Now imagine "origin" picks up your branch...
> (cd ../parent && git fetch ../myfork.git topic:topic)
>
> # Depending on your project's workflow, you may want to consider that
> # the new base for further development (and never rebase or rewrite
> # any commits that origin has). You do that by re-pointing your
> # @{upstream} config.
> git fetch
> git branch --set-upstream-to=origin/topic topic
>
> # now a "rebase -i" would show only the commits origin doesn't have
> # (five and six in this case)
> commit six
> git rebase -i
>
>
> Hopefully that shows off some of the ways you can use the upstream and
> push config in practice. Some people may not be as excited about the
> "rebase" default as I am. I spend quite a lot of time at the end of a
> series sifting through the commits via "rebase -i" and polishing them
> up. I also test with "git rebase -x 'make test'".Yes this is extremely helpful. I also like to default to rebase for everything. I have this in my global config:
[pull]
rebase = true
[branch]
autosetuprebase = always(Although I think autosetuprebase isn't needed anymore since pull.rebase is introduced, but I keep both just in case)
So to summarize:
* Pushing to fork only ever requires `git push` * Pushing to origin requires at least `git push origin` (for same-named branch) * Pulling from origin always uses remote tracking branch (set to mainline to merge latest into topic branch (single developer) or set to same-named branch for multiple developers)
Another improvement that it seems like I'll have is that I do not need to specify the branch name for my first-time pushes of a branch like I had to do when `push.default` was set to `simple`. Back then I had to do:
$ git push -u origin HEAD
Now all I gotta do is:
$ git push
I like this much better!! Thanks a ton!