git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Best "triangle" workflow setup?

From
RDRobert 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!
Previous: Jeff KingNext: Jeff King
Message 5 of 6 in “Best "triangle" workflow setup?”
  1. Robert DaileyMay 11, 2017
  2. Jeff KingMay 11, 2017
  3. Robert DaileyMay 11, 2017
  4. Jeff KingMay 11, 2017
  5. Robert DaileyMay 12, 2017
  6. Jeff KingMay 13, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.