git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* Unexpected behavior with branch.*.{remote,pushremote,merge}
@ 2020-10-10 18:27 Ben Denhartog
  2020-10-10 18:38 ` Ben Denhartog
  0 siblings, 1 reply; 11+ messages in thread
From: Ben Denhartog @ 2020-10-10 18:27 UTC (permalink / raw)
  To: git

I have a few repositories on my system that exist primarily as local copies of remote repositories, in that I normally just want to track and follow the upstream project (however, I periodically contribute back upstream so they are technically forks -- origin is my remote, upstream is theirs).

In these repositories, I set the following configuration:

```
[remote "origin"]
  url = https://git.foo.com/me/bar.git
  fetch = +refs/heads/*:refs/remotes/origin/*
[remote "upstream"]
  url = https://git.foo.com/them/bar.git
  fetch = +refs/heads/main:refs/remotes/upstream/main
  tagopt = --no-tags
[branch "main"]
  remote = upstream
  pushRemote = origin
  merge = refs/heads/master
  rebase = true
```

Based on my understanding, this should effectively force my local `main` branch to track against `upstream/main`, but push to `origin/main`. I notice some odd behavior when fetching, primarily that FETCH_HEAD doesn't resolve to `upstream/main` as I would expect:

```
➜ git fetch --all
Fetching origin
Fetching upstream
remote: Enumerating objects: 23, done.
remote: Counting objects: 100% (23/23), done.
remote: Total 32 (delta 23), reused 23 (delta 23), pack-reused 9
Unpacking objects: 100% (32/32), 12.97 KiB | 949.00 KiB/s, done.
From https://git.foo.com/them/bar
   63f7159..e65b80e  main     -> upstream/main


➜ git status -sbu
## main...upstream/main [behind 9]


➜ git rev-parse HEAD upstream/main origin/main FETCH_HEAD
63f71597979edb16cb9f80d0431115e22dcb716d
e65b80edd2a2162f67120a98e84bb489f15fcf97
23e6881719f661c37336d9fcf7a9005a7dfce0cf
23e6881719f661c37336d9fcf7a9005a7dfce0cf
```

As we see from the output, `FETCH_HEAD` is resolving to the same commit as `origin/main`, when I would instead expect it to resolve to the same commit as `upstream/main`. Here are the contents of `.git/FETCH_HEAD` in its entirety:

```
➜ cat .git/FETCH_HEAD
23e6881719f661c37336d9fcf7a9005a7dfce0cf        not-for-merge   branch 'main' of https://git.foo.com/me/foo
e65b80edd2a2162f67120a98e84bb489f15fcf97                branch 'main' of https://git.foo.com/them/foo
```

Curiously, `git rebase FETCH_HEAD` seems to think the local branch is up to date (erroneously), however `git-pull --rebase=true` and `git-merge FETCH_HEAD` both work as expected and merge/rebase with `upstream/main`.

Am I going about this incorrectly? The main purpose behind configuring my "mostly just a fork" repository is that it simplifies tracking against an upstream remote for projects which I do not work on actively. Of course, you might argue that I don't need to keep my remote around for this purpose and can just use a straightforward `git-clone` here -- but I'd rather not, and would prefer responses addressing the perceived bug rather than suggesting this particular alternative workflow.

-- 
  Ben Denhartog
  ben@sudoforge.com

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-10-10 18:27 Unexpected behavior with branch.*.{remote,pushremote,merge} Ben Denhartog
@ 2020-10-10 18:38 ` Ben Denhartog
  0 siblings, 0 replies; 11+ messages in thread
From: Ben Denhartog @ 2020-10-10 18:38 UTC (permalink / raw)
  To: git

There's a small typo that I didn't catch here -- `branch.main.merge` should be `refs/heads/main` in this example. Rest assured this is not the source of my issue (the actual repository I used for this still uses `master` as `HEAD`)

-- 
  Ben Denhartog
  ben@sudoforge.com

On Sat, Oct 10, 2020, at 12:27, Ben Denhartog wrote:
> I have a few repositories on my system that exist primarily as local 
> copies of remote repositories, in that I normally just want to track 
> and follow the upstream project (however, I periodically contribute 
> back upstream so they are technically forks -- origin is my remote, 
> upstream is theirs).
> 
> In these repositories, I set the following configuration:
> 
> ```
> [remote "origin"]
>   url = https://git.foo.com/me/bar.git
>   fetch = +refs/heads/*:refs/remotes/origin/*
> [remote "upstream"]
>   url = https://git.foo.com/them/bar.git
>   fetch = +refs/heads/main:refs/remotes/upstream/main
>   tagopt = --no-tags
> [branch "main"]
>   remote = upstream
>   pushRemote = origin
>   merge = refs/heads/master
>   rebase = true
> ```
> 
> Based on my understanding, this should effectively force my local 
> `main` branch to track against `upstream/main`, but push to 
> `origin/main`. I notice some odd behavior when fetching, primarily that 
> FETCH_HEAD doesn't resolve to `upstream/main` as I would expect:
> 
> ```
> ➜ git fetch --all
> Fetching origin
> Fetching upstream
> remote: Enumerating objects: 23, done.
> remote: Counting objects: 100% (23/23), done.
> remote: Total 32 (delta 23), reused 23 (delta 23), pack-reused 9
> Unpacking objects: 100% (32/32), 12.97 KiB | 949.00 KiB/s, done.
> From https://git.foo.com/them/bar
>    63f7159..e65b80e  main     -> upstream/main
> 
> 
> ➜ git status -sbu
> ## main...upstream/main [behind 9]
> 
> 
> ➜ git rev-parse HEAD upstream/main origin/main FETCH_HEAD
> 63f71597979edb16cb9f80d0431115e22dcb716d
> e65b80edd2a2162f67120a98e84bb489f15fcf97
> 23e6881719f661c37336d9fcf7a9005a7dfce0cf
> 23e6881719f661c37336d9fcf7a9005a7dfce0cf
> ```
> 
> As we see from the output, `FETCH_HEAD` is resolving to the same commit 
> as `origin/main`, when I would instead expect it to resolve to the same 
> commit as `upstream/main`. Here are the contents of `.git/FETCH_HEAD` 
> in its entirety:
> 
> ```
> ➜ cat .git/FETCH_HEAD
> 23e6881719f661c37336d9fcf7a9005a7dfce0cf        not-for-merge   branch 
> 'main' of https://git.foo.com/me/foo
> e65b80edd2a2162f67120a98e84bb489f15fcf97                branch 'main' 
> of https://git.foo.com/them/foo
> ```
> 
> Curiously, `git rebase FETCH_HEAD` seems to think the local branch is 
> up to date (erroneously), however `git-pull --rebase=true` and 
> `git-merge FETCH_HEAD` both work as expected and merge/rebase with 
> `upstream/main`.
> 
> Am I going about this incorrectly? The main purpose behind configuring 
> my "mostly just a fork" repository is that it simplifies tracking 
> against an upstream remote for projects which I do not work on 
> actively. Of course, you might argue that I don't need to keep my 
> remote around for this purpose and can just use a straightforward 
> `git-clone` here -- but I'd rather not, and would prefer responses 
> addressing the perceived bug rather than suggesting this particular 
> alternative workflow.
> 
> -- 
>   Ben Denhartog
>   ben@sudoforge.com

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04 21:00       ` Jeff King
@ 2020-12-04 22:20         ` Ben Denhartog
  0 siblings, 0 replies; 11+ messages in thread
From: Ben Denhartog @ 2020-12-04 22:20 UTC (permalink / raw)
  To: Jeff King, Junio C Hamano; +Cc: git

I guess from my perspective, for these repositories, my fork _is_ the "origin"; I tend to mirror the repositories I contribute to (e.g. use the "fork" feature on Git{Hub,Lab}/etc), then clone my mirror, which lends itself to that mental model (origin is "mine"). 

-- 
  Ben Denhartog
  ben@sudoforge.com

On Fri, Dec 4, 2020, at 14:00, Jeff King wrote:
> On Fri, Dec 04, 2020 at 11:57:23AM -0800, Junio C Hamano wrote:
> 
> > > * Refactor away from usage of FETCH_HEAD
> > 
> > Yes, "fetch --all" is about updating the remote-tracking branches
> > and in retrospect, perhaps we might have avoided confusion if we
> > made it not to touch FETCH_HEAD, but it is not going to change now.
> 
> I think its behavior of appending all of the entries is sensible (or at
> least is the least-surprising thing). The only weird part is that it
> does not keep the "make sure heads for merging come before not-for-merge
> entries" property that individual ones have.
> 
> It could take a final pass after all of the sub-fetches have run and do
> that. I don't have any plans to work on it, but I'm tempted to call it a
> #leftoverbits candidate.
> 
> > > * Set `remote.pushdefault = origin`
> > > * Set `push.default = current` (instead of `simple`, and is what
> > > my global config sets this to)
> > 
> > I have a feeling that simple vs current does not make a difference
> > if you are pusing main to main, and if so, push.default could be
> > left to the default settings of 'simple'.  But the key to successful
> > use of the triangular workflow is to configure so that "fetch/pull"
> > goes to one place (i.e. your upstream) and "push" goes to another
> > (i.e. your publishing repository), and "remote.pushdefault" is a
> > good ingredient to do so.
> 
> I think my advice is just out-of-date (by quite a lot). In the early
> days, I remember being bitten by (or at least confused by) simple and
> how its use of upstream could work with multiple remotes. But we long
> ago fixed that, with ed2b18292b (push: change `simple` to accommodate
> triangular workflows, 2013-06-19), and these days it is explicitly
> documented to work the same as "current" when pushing to another remote.
> 
> > It is however more common to use 'origin' as the name of your
> > upstream repository (so that "git fetch" and "git pull" would grab
> > things from there by default) and set remote.pushdefault to the
> > remote you push into, though (iow, I found remote.pushdefault
> > pointing at 'origin' a bit unusual).  Doing so may make your
> > triangular workflow work smoother.
> 
> Yeah, I wasn't going to nitpick his remote names, but that's the same
> convention I use. :) If people have custom forks of a repository that I
> access, I usually just name the remote for them after their username
> (including my own).
> 
> -Peff
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04 16:44   ` Ben Denhartog
@ 2020-12-04 21:29     ` Felipe Contreras
  0 siblings, 0 replies; 11+ messages in thread
From: Felipe Contreras @ 2020-12-04 21:29 UTC (permalink / raw)
  To: Ben Denhartog; +Cc: Git

On Fri, Dec 4, 2020 at 10:45 AM Ben Denhartog <ben@sudoforge.com> wrote:
>
> I'm just now hearing the terminology "triangular workflow" (I may live under a rock), but that aptly and succintly describes the workflow I was attempting to simplify with my initial configuration.
>
> I read the article on your blog, and the solution you propose makes sense to me, at least in the context of triangular workflows. I don't see any public feedback on your patch; bummer to see. Is it something you've brought up since 2014?

It did receive some positive public feedback. At least v3 of the series:

https://lore.kernel.org/git/xmqq38hkx4lr.fsf@gitster.dls.corp.google.com/

But my patches have a tendency to enact resistance. Perhaps more so back then.

I have not brought it up since then. I currently have like 4 patch
series stuck, doesn't seem wise to add another one. But I likely will
at some point.

It is the single most important feature I think Git is lacking. Having
accustomed myself to the publish branch, I now feel like missing a
finger without it on git vanilla.

Cheers.

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04 19:57     ` Junio C Hamano
@ 2020-12-04 21:00       ` Jeff King
  2020-12-04 22:20         ` Ben Denhartog
  0 siblings, 1 reply; 11+ messages in thread
From: Jeff King @ 2020-12-04 21:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ben Denhartog, git

On Fri, Dec 04, 2020 at 11:57:23AM -0800, Junio C Hamano wrote:

> > * Refactor away from usage of FETCH_HEAD
> 
> Yes, "fetch --all" is about updating the remote-tracking branches
> and in retrospect, perhaps we might have avoided confusion if we
> made it not to touch FETCH_HEAD, but it is not going to change now.

I think its behavior of appending all of the entries is sensible (or at
least is the least-surprising thing). The only weird part is that it
does not keep the "make sure heads for merging come before not-for-merge
entries" property that individual ones have.

It could take a final pass after all of the sub-fetches have run and do
that. I don't have any plans to work on it, but I'm tempted to call it a
#leftoverbits candidate.

> > * Set `remote.pushdefault = origin`
> > * Set `push.default = current` (instead of `simple`, and is what
> > my global config sets this to)
> 
> I have a feeling that simple vs current does not make a difference
> if you are pusing main to main, and if so, push.default could be
> left to the default settings of 'simple'.  But the key to successful
> use of the triangular workflow is to configure so that "fetch/pull"
> goes to one place (i.e. your upstream) and "push" goes to another
> (i.e. your publishing repository), and "remote.pushdefault" is a
> good ingredient to do so.

I think my advice is just out-of-date (by quite a lot). In the early
days, I remember being bitten by (or at least confused by) simple and
how its use of upstream could work with multiple remotes. But we long
ago fixed that, with ed2b18292b (push: change `simple` to accommodate
triangular workflows, 2013-06-19), and these days it is explicitly
documented to work the same as "current" when pushing to another remote.

> It is however more common to use 'origin' as the name of your
> upstream repository (so that "git fetch" and "git pull" would grab
> things from there by default) and set remote.pushdefault to the
> remote you push into, though (iow, I found remote.pushdefault
> pointing at 'origin' a bit unusual).  Doing so may make your
> triangular workflow work smoother.

Yeah, I wasn't going to nitpick his remote names, but that's the same
convention I use. :) If people have custom forks of a repository that I
access, I usually just name the remote for them after their username
(including my own).

-Peff

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04 16:45   ` Ben Denhartog
@ 2020-12-04 19:57     ` Junio C Hamano
  2020-12-04 21:00       ` Jeff King
  0 siblings, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2020-12-04 19:57 UTC (permalink / raw)
  To: Ben Denhartog; +Cc: Jeff King, git

"Ben Denhartog" <ben@sudoforge.com> writes:

> To the rest of your message:
>
> I did not understand the behavior of `fetch --all`; what you've
> described makes sense to me (although I would agree that it sounds
> like there's a bug). Between the following steps I'll take, I
> think my fork workflow is "fixed":
>
> * Refactor away from usage of FETCH_HEAD

Yes, "fetch --all" is about updating the remote-tracking branches
and in retrospect, perhaps we might have avoided confusion if we
made it not to touch FETCH_HEAD, but it is not going to change now.

> * Set `remote.pushdefault = origin`
> * Set `push.default = current` (instead of `simple`, and is what
> my global config sets this to)

I have a feeling that simple vs current does not make a difference
if you are pusing main to main, and if so, push.default could be
left to the default settings of 'simple'.  But the key to successful
use of the triangular workflow is to configure so that "fetch/pull"
goes to one place (i.e. your upstream) and "push" goes to another
(i.e. your publishing repository), and "remote.pushdefault" is a
good ingredient to do so.

It is however more common to use 'origin' as the name of your
upstream repository (so that "git fetch" and "git pull" would grab
things from there by default) and set remote.pushdefault to the
remote you push into, though (iow, I found remote.pushdefault
pointing at 'origin' a bit unusual).  Doing so may make your
triangular workflow work smoother.

> I appreciate your insight and the identification of the problem.

Thanks for a concluding comment to report a success.  It's
reassuring to hear a positive feedback.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04 10:13 ` Jeff King
@ 2020-12-04 16:45   ` Ben Denhartog
  2020-12-04 19:57     ` Junio C Hamano
  0 siblings, 1 reply; 11+ messages in thread
From: Ben Denhartog @ 2020-12-04 16:45 UTC (permalink / raw)
  To: Jeff King; +Cc: git

> I assume you mean "merge = refs/heads/main" from your examples

You're correct; I manually rewrote `master` to `main` in my initial email and missed that one instance.

>   - instead of branch.*.pushremote, I usually set remote.pushdefault, so
>     that it covers all branches (i.e., I'd never want to push to the
>     upstream remote, which I do not even have access to).
> 
>   - in a workflow like this, I generally have push.default set to
>     "current"
> 
>   - I make frequent use of @{push} to refer to the matching branch on my
>     remote (e.g., after doing some local work, I might use "git
>     range-diff upstream @{push} HEAD" to examine the changes before
>     pushing them up).

This makes sense. I rarely actually write `FETCH_HEAD` manually; in my email, I expanded the value of aliases I use. Specifically, my "get everything up-to-date" alias [0] uses `FETCH_HEAD` -- changing this to `@{u}` makes a lot of sense.

[0]: https://github.com/sudoforge/dotfiles/blob/trunk/git/.config/git/config#L104

> > however `git-pull --rebase=true` [works]
> 
> That is running a new git-fetch under the hood, which will overwrite
> FETCH_HEAD (and will only fetch from upstream, since that's what's in
> branch.main.remote).

D'oh -- I wasn't focusing on the fact that this would only fetch the branch's configured remote; I blame it on tunnel vision.

To the rest of your message:

I did not understand the behavior of `fetch --all`; what you've described makes sense to me (although I would agree that it sounds like there's a bug). Between the following steps I'll take, I think my fork workflow is "fixed":

* Refactor away from usage of FETCH_HEAD
* Set `remote.pushdefault = origin`
* Set `push.default = current` (instead of `simple`, and is what my global config sets this to)

I appreciate your insight and the identification of the problem.


-- 
  Ben Denhartog
  ben@sudoforge.com

On Fri, Dec 4, 2020, at 03:13, Jeff King wrote:
> On Thu, Dec 03, 2020 at 06:26:15PM -0700, Ben Denhartog wrote:
> 
> > I have a few repositories on my system that exist primarily as local copies of remote repositories, in that I normally just want to track and follow the upstream project (however, I periodically contribute back upstream so they are technically forks -- origin is my remote, upstream is theirs).
> > 
> > In these repositories, I set the following configuration:
> > 
> > ```
> > [remote "origin"]
> >   url = https://git.foo.com/me/bar.git
> >   fetch = +refs/heads/*:refs/remotes/origin/*
> > [remote "upstream"]
> >   url = https://git.foo.com/them/bar.git
> >   fetch = +refs/heads/main:refs/remotes/upstream/main
> >   tagopt = --no-tags
> > [branch "main"]
> >   remote = upstream
> >   pushRemote = origin
> >   merge = refs/heads/master
> >   rebase = true
> > ```
> 
> I use a similar setup myself, and it works well for this kind of
> triangular flow. A few notes:
> 
>   - I assume you mean "merge = refs/heads/main" from your examples
> 
>   - instead of branch.*.pushremote, I usually set remote.pushdefault, so
>     that it covers all branches (i.e., I'd never want to push to the
>     upstream remote, which I do not even have access to).
> 
>   - in a workflow like this, I generally have push.default set to
>     "current"
> 
>   - I make frequent use of @{push} to refer to the matching branch on my
>     remote (e.g., after doing some local work, I might use "git
>     range-diff upstream @{push} HEAD" to examine the changes before
>     pushing them up).
> 
> > Based on my understanding of the branch configuration options, this
> > should effectively force my local `main` branch to track against
> > `upstream/main`, but push to `origin/main`. I notice what I believe to
> > be odd behavior when fetching: that FETCH_HEAD doesn't resolve to
> > `upstream/main` as I would expect:
> > 
> > ➜ git fetch --all
> > Fetching origin
> > Fetching upstream
> > remote: Enumerating objects: 23, done.
> > remote: Counting objects: 100% (23/23), done.
> > remote: Total 32 (delta 23), reused 23 (delta 23), pack-reused 9
> > Unpacking objects: 100% (32/32), 12.97 KiB | 949.00 KiB/s, done.
> > From https://git.foo.com/them/bar
> >    63f7159..e65b80e  main     -> upstream/main
> 
> The culprit here is using "git fetch --all". It triggers the sub-fetches
> with --append, so they'll each add to FETCH_HEAD instead of overwriting
> it.  We do truncate it before the first one, so after this completes it
> should have the complete set of refs fetched from both remotes (even if
> it was a noop to fetch one of them, anything mentioned in the refspecs
> shows up in FETCH_HEAD).
> 
> Which is what you're seeing here:
> 
> > ➜ cat .git/FETCH_HEAD
> > 23e6881719f661c37336d9fcf7a9005a7dfce0cf        not-for-merge   branch 'main' of https://git.foo.com/me/foo
> > e65b80edd2a2162f67120a98e84bb489f15fcf97                branch 'main' of https://git.foo.com/them/foo
> 
> FETCH_HEAD is not just a ref, but contains some magic instructions that
> get interpreted by git-pull. But when programs besides git-pull try to
> resolve it to a single object, they just pick whichever value is first.
> 
> So I do think there's a bug there, or at least something not
> well-thought-out in the way that "fetch --all" appends. Normally fetch
> tries to put the merge branch (or branches) first in the file, exactly
> so this naive "take the first one" lookup will do the most sensible
> thing. But when running multiple fetches that all append, we get their
> individual outputs in the order of fetch (which in turn is the same as
> the order in the config file).
> 
> Perhaps the parent "git fetch --all" triggering the sub-fetches should
> reorder FETCH_HEAD after they've all finished (to pull any merge heads
> up to the top, regardless of which remote they came from).
> 
> But an obvious workaround, if you know you'll always be merging from
> upstream, is to just reorder your config stanzas so that it's fetched
> first (likewise you can run "git fetch --multiple upstream origin" to
> specify the order manually, or define a group with remotes.<group>).
> 
> One reason I suspect nobody has come across this before is that there's
> not much reason to use FETCH_HEAD in this setting. If you know you want
> to rebase again upstream/main, then there are a number of ways you can
> refer to that:
> 
>   - upstream/main
> 
>   - upstream, if your refs/remotes/upstream/HEAD is set up (if you
>     didn't clone, you may want to run "git remote set-head upstream -a")
> 
>   - @{upstream}, if it's configured as your upstream (which it is here)
> 
>   - @{u}, a shorter synonym
> 
>   - nothing at all, since rebasing against the upstream is the default
>     for git-rebase these days
> 
>   - you can say that (or even just "upstream", if your
>     refs/remotes/origin/HEAD is set up)
> 
> > Curiously, `git rebase FETCH_HEAD` seems to think the local branch is
> > up to date (erroneously),
> 
> That's what I'd expect, since it is doing the naive "what is the first
> one in the file" lookup.
> 
> > however `git-pull --rebase=true` [works]
> 
> That is running a new git-fetch under the hood, which will overwrite
> FETCH_HEAD (and will only fetch from upstream, since that's what's in
> branch.main.remote).
> 
> > `git-merge FETCH_HEAD` both work as expected and merge/rebase with
> > `upstream/main`.
> 
> I think git-merge, like git-pull, understands the magic FETCH_HEAD
> format.
> 
> > Am I going about this incorrectly? The main purpose behind configuring
> > my "mostly just a fork" repository is that it simplifies tracking
> > against an upstream remote for projects which I do not work on
> > actively. Of course, you might argue that I don't need to keep my
> > remote around for this purpose and can just use a straightforward
> > `git-clone` here -- but I'd rather not, and would prefer responses
> > addressing the perceived bug rather than suggesting this particular
> > alternative workflow.
> 
> I think your workflow is perfectly reasonable.
> 
> -Peff
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04  2:31 ` Felipe Contreras
@ 2020-12-04 16:44   ` Ben Denhartog
  2020-12-04 21:29     ` Felipe Contreras
  0 siblings, 1 reply; 11+ messages in thread
From: Ben Denhartog @ 2020-12-04 16:44 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: Git

I'm just now hearing the terminology "triangular workflow" (I may live under a rock), but that aptly and succintly describes the workflow I was attempting to simplify with my initial configuration.

I read the article on your blog, and the solution you propose makes sense to me, at least in the context of triangular workflows. I don't see any public feedback on your patch; bummer to see. Is it something you've brought up since 2014?

-- 
  Ben Denhartog
  ben@sudoforge.com

On Thu, Dec 3, 2020, at 19:31, Felipe Contreras wrote:
> On Thu, Dec 3, 2020 at 7:29 PM Ben Denhartog <ben@sudoforge.com> wrote:
> >
> > I have a few repositories on my system that exist primarily as local copies of remote repositories, in that I normally just want to track and follow the upstream project (however, I periodically contribute back upstream so they are technically forks -- origin is my remote, upstream is theirs).
> 
> Otherwise known as a triangular workflow, for which in my opinion git
> doesn't have good support.
> 
> I wrote about it in my blog [1], and I wrote the patches to properly
> support that mode [2]. Unfortunately they were not merged.
> 
> Cheers.
> 
> [1] https://felipec.wordpress.com/2014/05/11/git-triangular-workflows/
> [2] 
> https://lore.kernel.org/git/1398023106-25958-1-git-send-email-felipe.contreras@gmail.com/
> 
> -- 
> Felipe Contreras
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04  1:26 Ben Denhartog
  2020-12-04  2:31 ` Felipe Contreras
@ 2020-12-04 10:13 ` Jeff King
  2020-12-04 16:45   ` Ben Denhartog
  1 sibling, 1 reply; 11+ messages in thread
From: Jeff King @ 2020-12-04 10:13 UTC (permalink / raw)
  To: Ben Denhartog; +Cc: git

On Thu, Dec 03, 2020 at 06:26:15PM -0700, Ben Denhartog wrote:

> I have a few repositories on my system that exist primarily as local copies of remote repositories, in that I normally just want to track and follow the upstream project (however, I periodically contribute back upstream so they are technically forks -- origin is my remote, upstream is theirs).
> 
> In these repositories, I set the following configuration:
> 
> ```
> [remote "origin"]
>   url = https://git.foo.com/me/bar.git
>   fetch = +refs/heads/*:refs/remotes/origin/*
> [remote "upstream"]
>   url = https://git.foo.com/them/bar.git
>   fetch = +refs/heads/main:refs/remotes/upstream/main
>   tagopt = --no-tags
> [branch "main"]
>   remote = upstream
>   pushRemote = origin
>   merge = refs/heads/master
>   rebase = true
> ```

I use a similar setup myself, and it works well for this kind of
triangular flow. A few notes:

  - I assume you mean "merge = refs/heads/main" from your examples

  - instead of branch.*.pushremote, I usually set remote.pushdefault, so
    that it covers all branches (i.e., I'd never want to push to the
    upstream remote, which I do not even have access to).

  - in a workflow like this, I generally have push.default set to
    "current"

  - I make frequent use of @{push} to refer to the matching branch on my
    remote (e.g., after doing some local work, I might use "git
    range-diff upstream @{push} HEAD" to examine the changes before
    pushing them up).

> Based on my understanding of the branch configuration options, this
> should effectively force my local `main` branch to track against
> `upstream/main`, but push to `origin/main`. I notice what I believe to
> be odd behavior when fetching: that FETCH_HEAD doesn't resolve to
> `upstream/main` as I would expect:
> 
> ➜ git fetch --all
> Fetching origin
> Fetching upstream
> remote: Enumerating objects: 23, done.
> remote: Counting objects: 100% (23/23), done.
> remote: Total 32 (delta 23), reused 23 (delta 23), pack-reused 9
> Unpacking objects: 100% (32/32), 12.97 KiB | 949.00 KiB/s, done.
> From https://git.foo.com/them/bar
>    63f7159..e65b80e  main     -> upstream/main

The culprit here is using "git fetch --all". It triggers the sub-fetches
with --append, so they'll each add to FETCH_HEAD instead of overwriting
it.  We do truncate it before the first one, so after this completes it
should have the complete set of refs fetched from both remotes (even if
it was a noop to fetch one of them, anything mentioned in the refspecs
shows up in FETCH_HEAD).

Which is what you're seeing here:

> ➜ cat .git/FETCH_HEAD
> 23e6881719f661c37336d9fcf7a9005a7dfce0cf        not-for-merge   branch 'main' of https://git.foo.com/me/foo
> e65b80edd2a2162f67120a98e84bb489f15fcf97                branch 'main' of https://git.foo.com/them/foo

FETCH_HEAD is not just a ref, but contains some magic instructions that
get interpreted by git-pull. But when programs besides git-pull try to
resolve it to a single object, they just pick whichever value is first.

So I do think there's a bug there, or at least something not
well-thought-out in the way that "fetch --all" appends. Normally fetch
tries to put the merge branch (or branches) first in the file, exactly
so this naive "take the first one" lookup will do the most sensible
thing. But when running multiple fetches that all append, we get their
individual outputs in the order of fetch (which in turn is the same as
the order in the config file).

Perhaps the parent "git fetch --all" triggering the sub-fetches should
reorder FETCH_HEAD after they've all finished (to pull any merge heads
up to the top, regardless of which remote they came from).

But an obvious workaround, if you know you'll always be merging from
upstream, is to just reorder your config stanzas so that it's fetched
first (likewise you can run "git fetch --multiple upstream origin" to
specify the order manually, or define a group with remotes.<group>).

One reason I suspect nobody has come across this before is that there's
not much reason to use FETCH_HEAD in this setting. If you know you want
to rebase again upstream/main, then there are a number of ways you can
refer to that:

  - upstream/main

  - upstream, if your refs/remotes/upstream/HEAD is set up (if you
    didn't clone, you may want to run "git remote set-head upstream -a")

  - @{upstream}, if it's configured as your upstream (which it is here)

  - @{u}, a shorter synonym

  - nothing at all, since rebasing against the upstream is the default
    for git-rebase these days

  - you can say that (or even just "upstream", if your
    refs/remotes/origin/HEAD is set up)

> Curiously, `git rebase FETCH_HEAD` seems to think the local branch is
> up to date (erroneously),

That's what I'd expect, since it is doing the naive "what is the first
one in the file" lookup.

> however `git-pull --rebase=true` [works]

That is running a new git-fetch under the hood, which will overwrite
FETCH_HEAD (and will only fetch from upstream, since that's what's in
branch.main.remote).

> `git-merge FETCH_HEAD` both work as expected and merge/rebase with
> `upstream/main`.

I think git-merge, like git-pull, understands the magic FETCH_HEAD
format.

> Am I going about this incorrectly? The main purpose behind configuring
> my "mostly just a fork" repository is that it simplifies tracking
> against an upstream remote for projects which I do not work on
> actively. Of course, you might argue that I don't need to keep my
> remote around for this purpose and can just use a straightforward
> `git-clone` here -- but I'd rather not, and would prefer responses
> addressing the perceived bug rather than suggesting this particular
> alternative workflow.

I think your workflow is perfectly reasonable.

-Peff

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Unexpected behavior with branch.*.{remote,pushremote,merge}
  2020-12-04  1:26 Ben Denhartog
@ 2020-12-04  2:31 ` Felipe Contreras
  2020-12-04 16:44   ` Ben Denhartog
  2020-12-04 10:13 ` Jeff King
  1 sibling, 1 reply; 11+ messages in thread
From: Felipe Contreras @ 2020-12-04  2:31 UTC (permalink / raw)
  To: Ben Denhartog; +Cc: Git

On Thu, Dec 3, 2020 at 7:29 PM Ben Denhartog <ben@sudoforge.com> wrote:
>
> I have a few repositories on my system that exist primarily as local copies of remote repositories, in that I normally just want to track and follow the upstream project (however, I periodically contribute back upstream so they are technically forks -- origin is my remote, upstream is theirs).

Otherwise known as a triangular workflow, for which in my opinion git
doesn't have good support.

I wrote about it in my blog [1], and I wrote the patches to properly
support that mode [2]. Unfortunately they were not merged.

Cheers.

[1] https://felipec.wordpress.com/2014/05/11/git-triangular-workflows/
[2] https://lore.kernel.org/git/1398023106-25958-1-git-send-email-felipe.contreras@gmail.com/

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Unexpected behavior with branch.*.{remote,pushremote,merge}
@ 2020-12-04  1:26 Ben Denhartog
  2020-12-04  2:31 ` Felipe Contreras
  2020-12-04 10:13 ` Jeff King
  0 siblings, 2 replies; 11+ messages in thread
From: Ben Denhartog @ 2020-12-04  1:26 UTC (permalink / raw)
  To: git

I have a few repositories on my system that exist primarily as local copies of remote repositories, in that I normally just want to track and follow the upstream project (however, I periodically contribute back upstream so they are technically forks -- origin is my remote, upstream is theirs).

In these repositories, I set the following configuration:

```
[remote "origin"]
  url = https://git.foo.com/me/bar.git
  fetch = +refs/heads/*:refs/remotes/origin/*
[remote "upstream"]
  url = https://git.foo.com/them/bar.git
  fetch = +refs/heads/main:refs/remotes/upstream/main
  tagopt = --no-tags
[branch "main"]
  remote = upstream
  pushRemote = origin
  merge = refs/heads/master
  rebase = true
```

Based on my understanding of the branch configuration options, this should effectively force my local `main` branch to track against `upstream/main`, but push to `origin/main`. I notice what I believe to be odd behavior when fetching: that FETCH_HEAD doesn't resolve to `upstream/main` as I would expect:

```
➜ git fetch --all
Fetching origin
Fetching upstream
remote: Enumerating objects: 23, done.
remote: Counting objects: 100% (23/23), done.
remote: Total 32 (delta 23), reused 23 (delta 23), pack-reused 9
Unpacking objects: 100% (32/32), 12.97 KiB | 949.00 KiB/s, done.
From https://git.foo.com/them/bar
   63f7159..e65b80e  main     -> upstream/main


➜ git status -sbu
## main...upstream/main [behind 9]


➜ git rev-parse HEAD upstream/main origin/main FETCH_HEAD
63f71597979edb16cb9f80d0431115e22dcb716d
e65b80edd2a2162f67120a98e84bb489f15fcf97
23e6881719f661c37336d9fcf7a9005a7dfce0cf
23e6881719f661c37336d9fcf7a9005a7dfce0cf
```

As we see from the output, `FETCH_HEAD` is resolving to the same commit as `origin/main`, when I would instead expect it to resolve to the same commit as `upstream/main`. Here are the contents of `.git/FETCH_HEAD` in its entirety:

```
➜ cat .git/FETCH_HEAD
23e6881719f661c37336d9fcf7a9005a7dfce0cf        not-for-merge   branch 'main' of https://git.foo.com/me/foo
e65b80edd2a2162f67120a98e84bb489f15fcf97                branch 'main' of https://git.foo.com/them/foo
```

Curiously, `git rebase FETCH_HEAD` seems to think the local branch is up to date (erroneously), however `git-pull --rebase=true` and `git-merge FETCH_HEAD` both work as expected and merge/rebase with `upstream/main`.

Am I going about this incorrectly? The main purpose behind configuring my "mostly just a fork" repository is that it simplifies tracking against an upstream remote for projects which I do not work on actively. Of course, you might argue that I don't need to keep my remote around for this purpose and can just use a straightforward `git-clone` here -- but I'd rather not, and would prefer responses addressing the perceived bug rather than suggesting this particular alternative workflow.

-- 
  Ben Denhartog
  ben@sudoforge.com

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2020-12-04 22:23 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-10 18:27 Unexpected behavior with branch.*.{remote,pushremote,merge} Ben Denhartog
2020-10-10 18:38 ` Ben Denhartog
2020-12-04  1:26 Ben Denhartog
2020-12-04  2:31 ` Felipe Contreras
2020-12-04 16:44   ` Ben Denhartog
2020-12-04 21:29     ` Felipe Contreras
2020-12-04 10:13 ` Jeff King
2020-12-04 16:45   ` Ben Denhartog
2020-12-04 19:57     ` Junio C Hamano
2020-12-04 21:00       ` Jeff King
2020-12-04 22:20         ` Ben Denhartog

Code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).