Subject: Re: [1.8.0] Provide proper remote ref namespaces

From: Johan Herland <[email-masked]>
Date: Mon, 14 Feb 2011 10:18:46 +0100

On Monday 14 February 2011, Junio C Hamano wrote:
> Johan Herland <[email-masked]> writes:
> > Yes, replicating existing behavior w/explicit refspecs would look like: 
> >   [remote "origin"]
> >   
> >         url = git://git.example.com/repo.git
> >         fetch = +HEAD:refs/remotes/origin/HEAD
> >         fetch = +refs/heads/*:refs/remotes/origin/*
> >         fetch = ~refs/tags/*:refs/tags/*
> 
> While this is fine, I am not sure about the "HEAD" part.  Most of the
> protocol do not convey which branch HEAD points at (instead "clone" has
> to guess), which eventually needs to be fixed.  Incremental updates via
> "fetch" does not touch "HEAD" at all by design; unlike the real branch
> heads "remotes/origin/$branch" that are used to keep copies of what are
> at the remote, "remotes/origin/HEAD" is meant to be used by the local
> repository to designate which of the remote branch is considered the
> primary branch from local repository owner's point of view, primarily so
> that you can say "origin" locally to mean "origin/next" by setting the
> symref origin/HEAD to point at it.  In that sense, the guess made by
> "clone" is only used to give an initial value.

Ah, ok. I've misunderstood the purpose of "remotes/origin/HEAD" then. Feel 
free to remove that refspec line from my proposal, and leave it as a 
special-purpose thing set up by clone (and maintained by the user 
thereafter).

Still (as I think was recently discussed in another thread), the existence 
of remotes/origin/HEAD _does_ cause problems if the origin remote also has a 
branch called "refs/heads/HEAD" (which would collide when fetched into the 
local repo).

> > FTR, my new/proposed refspecs would look like this:
> >   [remote "origin"]
> >   
> >         url = git://git.example.com/repo.git
> >         fetch = +HEAD:refs/remotes/origin/HEAD
> >         fetch = +refs/heads/*:refs/remotes/origin/heads*
> >         fetch = ~+refs/tags/*:refs/remotes/origin/tags/*
> >       
> >       ( fetch = +refs/notes/*:refs/remotes/origin/notes/* )
> >       ( fetch = +refs/replace/*:refs/remotes/origin/replace/* )
> 
> I think you meant "refs/remotes/origin/heads/*" (note the slash) on the
> RHS of the branch refspecs.

Indeed. Thanks for pointing out the typo.

> How's that different from refs/*:refs/remotes/origin/* by the way?

It's not, except that "refs/*:refs/remotes/origin/*" would fetch a too-large 
superset. E.g. it would fetch "refs/remotes/third-party/heads/foo" into 
"refs/remotes/origin/remotes/third-party/heads/foo", which we probably don't 
want.

> Also
> if you give tags a totally separate namespace, I don't see much reason to
> still give it the "auto-follow" semantics.  It is far simpler to explain
> if you just fetch all of them and be done with it, no?

Agreed. Also, to quote Peff in http://thread.gmane.org/gmane.comp.version-
control.git/160503/focus=160726 :

"Now you could argue that auto-follow is not worth the effort. It is
somewhat confusing, and I can't think of a time when it ever actually
reduced the set of objects I was fetching (as opposed to just fetching
all tags). But maybe others have use cases where it matters."

So if nobody disagree, I would have no problem with dropping the leading "~" 
from the refspec, thus disabling auto-following (tracking all tags 
explicitly instead).

> > Yes, to me it seems intuitive that when you specify <URL> (even if
> > <URL> corresponds to an existing remote) you do NOT update
> > remote-tracking refs, but if you use <remote>, you should ALWAYS
> > update remote-tracking refs. Others may disagree.
> 
> One argument for disagreement used to be that an explicit fetch of a
> single branch was a deliberate way to avoid updating the tracking branch
> before examining what has been changed (i.e. "git fetch origin master"
> followed by "git log origin/master..FETCH_HEAD" and hopefully followed by
> "git pull origin" or somesuch).  But that was before reflog was adopted
> as a reliable safety measure, and I don't think people should rely on
> that behaviour anymore.
> 
> I would actually make an even stronger point that people should not base
> their workflow on keeping remote tracking refs deliberately stale, which
> is exactly the "feature" was meant to be used for.  What has happened on
> the other end has already happened whether you like it or not, and there
> is no point in your locally pretending that it didn't happen.  If you
> don't like the latest update there, you go talk to the party that control
> the remote and rectify the situation with them.  It may involve pushing a
> reverting or correcting commit over there, or in the worst case you
> simply stop pulling from them, forking the project in effect at that
> point.

Agreed.

> > It may seem so, but in my experience it doesn't really work perfectly:
> > Even if I fully control the repo I push to, I still want precise
> > control over what I push there. Sometimes I may working on 'next' and
> > 'master' in parallel, and I might have finished and tested some
> > bugfixes on 'master', while I still have unfinished/untested stuff on
> > 'next'.
> 
> Yes, I do this all the time and I often say "git push ko maint master"
> (ko is my name for the k.org repo).  I however don't feel inconvenienced
> by it precisely because when I make such a push, I _know_ that I want to
> push only these two branches.  Saying "only these two branches"
> explicitly from the command line, and seeing only these two branches go
> out, are very assuring to me.  I usually try to be much more organized
> to make sure all four integration branches satisfy certain preconditions
> before pushing, and I say "git push ko" only after I make sure they do.
> 
> I consider it is a good UI design to force myself to type more when I am
> doing something un(der)disciplined and to let me type less when I am
> following a good project hygiene.

I don't doubt that the current behavior works well for you (otherwise I 
expect you would have changed it). However, what I've seen at $dayjob is 
that more inexperienced users will often push right after committing, and at 
that time they're still very much in the "working-on-one-branch" state of 
mind (as opposed to the "administering-multiple-branches" state of mind), so 
when they follow up a "git commit" with a "git push" they're surprised (or 
worse: oblivious) to the fact that "git push" can push multiple branches.

I guess it comes down to whether you fundamentally consider "git push" 
something that pushes multiple _branches_, or something that pushes multiple 
_commits_. And for the latter of those groups push.default == "matching" is 
inherently more "dangerous" than for the former. (Granted, me telling 
everyone to use push.default == "tracking" probably doesn't help them in 
discovering "git push"'s ability to update multiple branches.)

Have fun! :)

...Johan

-- 
Johan Herland, <[email-masked]>
www.herland.net