{"thread":{"id":"64730","subject":"Re: Triangular workflows","startedAt":"2026-01-05T22:19:47Z","lastAt":"2026-01-05T22:36:33Z","messageCount":2,"participants":["D. Ben Knoble"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"533085","messageId":"CALnO6CB7-w0tNMiYn5=SCBow637vRRrKRj_9k1h1DS4crJaVtQ@mail.gmail.com","threadId":"64730","inReplyTo":"CAHwyqnWwJuD4T9tuCArW5eY=rPCHKT71LroRRx-aYfDGwr8E9g@mail.gmail.com","subject":"Re: Triangular workflows","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-01-05T22:19:36Z","receivedAt":"2026-01-05T22:19:47Z","isPatch":false,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Thu, Jan 1, 2026 at 2:43 PM Harald Nordgren <haraldnordgren@gmail.com> wrote:\n>\n> Hi Ben!\n>\n> Did you ever get to this? And does it match what I do in the tests for PATCH v10:\n>\n> ```\n> git config remote.pushDefault origin\n> git branch --set-upstream-to upstream/main\n> ```\n>\n>\n> Harald\n\nYeah, that's definitely part of it for me. I've been meaning to write\nthis down elsewhere for a while, but here's what my setup for\ntriangular workflows looks like.\n\nFirst, there are typically 2 remotes (but not always!). In the\nexamples, I'll use \"origin\" (the place I usually cloned from first,\nthe most official version of the code, etc.; also the place I pull\nfrom) and \"benknoble\" (the place I push to). The setup works just fine\nwith a single origin, though.\n\nNext, I globally configure\n\n    push.default = current\n    pull.rebase = true\n    branch.autoSetupRebase = always\n\nThe first works with other settings to make the @{push} ref work (and\nto make \"git push\" work without arguments). I really like the @{push}\nref, and I'm not aware of any other way to enable it.\n\nThen, when setting up a repository I configure remotes and make sure\nto configure\n\n    remote.pushDefault = benknoble\n\n(if there is such a remote).\n\nThe next step is usually starting a branch:\n\n    git switch -c branch origin # or origin/main, or whatever\n\nWith the above settings, I immediately have\n- branch@{upstream} (@{u}) -> origin/…\n- branch@{push} (@{push}) -> benknoble/branch (or origin/branch,\ndepending on the case)\n\n\n\n-- \nD. Ben Knoble\n"},{"id":"533088","messageId":"CALnO6CAUSU-Pq_r-WYm3o0to6H8MdqiYOuoKaRfL1PTt30VaoQ@mail.gmail.com","threadId":"64730","inReplyTo":"CALnO6CB7-w0tNMiYn5=SCBow637vRRrKRj_9k1h1DS4crJaVtQ@mail.gmail.com","subject":"Re: Triangular workflows","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-01-05T22:36:22Z","receivedAt":"2026-01-05T22:36:33Z","isPatch":false,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Mon, Jan 5, 2026 at 5:19 PM D. Ben Knoble <ben.knoble@gmail.com> wrote:\n>\n> On Thu, Jan 1, 2026 at 2:43 PM Harald Nordgren <haraldnordgren@gmail.com> wrote:\n> >\n> > Hi Ben!\n> >\n> > Did you ever get to this? And does it match what I do in the tests for PATCH v10:\n> >\n> > ```\n> > git config remote.pushDefault origin\n> > git branch --set-upstream-to upstream/main\n> > ```\n> >\n> >\n> > Harald\n>\n> Yeah, that's definitely part of it for me. I've been meaning to write\n> this down elsewhere for a while, but here's what my setup for\n> triangular workflows looks like.\n>\n> First, there are typically 2 remotes (but not always!). In the\n> examples, I'll use \"origin\" (the place I usually cloned from first,\n> the most official version of the code, etc.; also the place I pull\n> from) and \"benknoble\" (the place I push to). The setup works just fine\n> with a single origin, though.\n>\n> Next, I globally configure\n>\n>     push.default = current\n>     pull.rebase = true\n>     branch.autoSetupRebase = always\n>\n> The first works with other settings to make the @{push} ref work (and\n> to make \"git push\" work without arguments). I really like the @{push}\n> ref, and I'm not aware of any other way to enable it.\n>\n> Then, when setting up a repository I configure remotes and make sure\n> to configure\n>\n>     remote.pushDefault = benknoble\n>\n> (if there is such a remote).\n>\n> The next step is usually starting a branch:\n>\n>     git switch -c branch origin # or origin/main, or whatever\n>\n> With the above settings, I immediately have\n> - branch@{upstream} (@{u}) -> origin/…\n> - branch@{push} (@{push}) -> benknoble/branch (or origin/branch,\n> depending on the case)\n\nPremature send :/\n\nAnyway, after working for a bit, I can use \"git pull\" to synchronize\nwith upstream via rebasing (since it's WIP, I don't mind, though for\nGit I have to remember to --keep-base anytime I rebase). Ditto for\n\"git rebase\" without a fetch, or when squashing (\"git rs\" = \"git\nrebase --autosquash\"). When I want to send out a new version, I \"git\npush\" (or \"pf\", an alias for \"push --force-with-lease\"; additionally\nI've already configured push.useForceIfIncludes = true); I typically\nfirst take a range-diff as described in my other mail (alias: rdup\ndoes \"git range-diff @{upstream} @{push} @\" and rdupc does \"git rdup |\ncopy-range-diff\").\n\nThe last piece of the puzzle for me are \"interrogation commands\": like\n\"git status\", where am I wrt to all these branches? I use 2\n\n- \"git sbup\" is my alias for \"git show-branch HEAD HEAD@{upstream} HEAD@{push}\"\n- \"git div\" is described at [1], but it draws a graph between my HEAD\nand either upstream or push, depending on whether I've already pushed\nout the current version (the newest version [2] is a bit smarter than\nthe original post describes). The graph uses cherry-mark and some\nother options so I can decide how to handle divergence: do I need to\nintegrate someone else's changes? From which branch? How? I also use\nthis a lot to look between a new release candidate and a past release\nat work to make sure we're not missing any patches that might have\ngone in on the last release branch but not into the latest candidate.\nAnd with repositories that commit accepted patches to the trunk, I\nknow I can delete my branch when \"git div\" shows all \"=\", even though\nGit refuses without \"--force\" (for good and clear reasons, just a\nnote).\n\nAnyway, my workflow for this all came about because I wanted to \"git\npull\" and \"git push\" (and \"git rebase\") without arguments and still be\nable to sync from upstream while sending to another place for PR-style\nreview.\n\nThe only downside so far is that \"git div\" in git.git can show too\nmuch information due to all the topic branch merges. Adding\n--first-parent helps, but the --boundary commits still make a mess for\ntrying to view things. But in repositories that commit accepted\npatches directly to the main branch, it works great. (If anyone has a\nversion of \"show me the graph of divergence\" that doesn't turn into a\nspider web when looking at git.git, LMK. A good test case is the\nrecent je/doc-reset topic. I have a remote \"broken-out\" pointing at\ngitster/git on GitHub, and \"git div broken-out/je/doc-reset\norigin/master\" is difficult to read after the first few lines. But if\nyou rebase it atop the latest master, it's very easy to see what's\ngoing on.)\n\n[1]: https://benknoble.github.io/blog/2024/11/15/useful-utilities/#git\n[2]: https://github.com/benknoble/Dotfiles/blob/master/links/bin/git-div\n\n-- \nD. Ben Knoble\n"}]}