Subject: Re: Q: how can i find the upstream merge point of a commit?

From: Johan Herland <[email-masked]>
Date: Wed, 15 Jun 2011 01:45:12 +0200

On Tuesday 14 June 2011, Jeff King wrote:
> On Tue, Jun 14, 2011 at 11:56:56AM +0200, Johan Herland wrote:
> > 2. Interpreting/DWIMing refs
> > 
> > Changing the ref mappings require a revised set of rules for
> > interpreting shorthand ref names (expanding them into full ref names),
> > and handling ambiguities when they arise:
> > [...]
> > 
> > - "origin/foo" must continue to work, even if "refs/remotes/origin/foo"
> > has now become "refs/remotes/origin/heads/foo". In other words,
> > "foo/bar" where "foo" is a valid remote, must try to resolve "bar"
> > against the refspecs specified for the "foo" remote.
> 
> What happens if I ask for foo/bar/baz? Should it try to resolve:
> 
>   1. refs/remotes/foo/heads/bar/baz
> 
> or
> 
>   2. refs/remotes/foo/bar/heads/baz
> 
> or both (and if both, in which order)?

I think we want to do both, following these pseudo-bash steps (or something 
similar):

  shorthand=$1  # e.g. "foo/bar/baz"
  for remote in $(git remote)
  do
      case $shorthand in
        "$remote/"*)
          # Found matching remote
          trailer=${shorthand#$remote/}
          # Assert $remote/$trailer == $shorthand
          # DWIM $trailer into
          # - refs/heads/$trailer
          # - refs/tags/$trailer
          # etc.
          for dwimmed_ref in dwim_ref($trailer)
          do
              # Map $dwimmed_ref through refspec to get
              # remote-tracking ref, e.g. mapping
              # "refs/heads/spam" to "refs/remotes/$remote/heads/spam"
              remote_ref=map_through_refspec($dwimmed_ref, $remote)
              if test -n "$remote_ref"
              then
                  # Add $remote_ref to list of candidates
              fi
          done
          ;;
        *)
          # Non-match, ignore
          ;;
      esac
  done

  # We now have a list of (fully qualified) ref candidates
  # for $shorthand.

  # If there are zero candidates, there is no match for $shorthand.

  # If there is only one candidate, we have found an unambiguous
  # match for $shorthand.

  # If our current context demands a SHA1 object name, and all
  # candidates point to the same SHA1, there is no ambiguity.

  # Otherwise, $shorthand is ambiguous.

Using the above (pseudo)code, and assuming remotes "foo" and "foo/bar" exist 
(with remote branches "bar/baz" and "baz", respectively, and default new-
style refspecs), then the "foo/bar/baz" shorthand would resolve to the 
following two remote-tracking branches:

- refs/remotes/foo/heads/bar/baz
- refs/remotes/foo/bar/heads/baz

This would likely result in an "ambiguous shorthand" error, unless the 
current context wants a SHA1 object name, and the two remote-tracking refs 
happen to point to the same SHA1 (in which case the result is unambiguous).

(Obviously, all of the above assumes that "foo/bar/baz" does not match any 
local ref, which would take precedence over the remote-tracking refs listed 
above.)

> I don't know offhand if "git remote" and "git clone" allow slashes in
> remote names, but I don't think we forbid it if somebody configures it
> themselves (and of course, remote names aside, they are free to write
> whatever refspecs they like in the config file).

  git remote add foo/bar .

does not report any errors for me.

> > - If "refs/tags/foo" does not exist, tag name "foo" is unambiguous if
> > it exists in one or more "refs/remotes/*/tags/foo" and they all point
> > to the same SHA1.
> > 
> > - If "refs/tags/foo" does not exist, and more than one
> > "refs/remotes/*/tags/foo" exist, and they do NOT all point to the same
> > SHA1, then there is an ambiguity.
> > 
> > - The user may resolve the ambiguity by creating "refs/tags/foo"
> > pointing to the chosen SHA1 ("refs/tags/foo" takes precedence over
> > "refs/remotes/*/tags/foo").
> > 
> > - The same rules apply to heads, notes, etc.
> 
> I'm not sure we need all of these rules for anything but tags. We
> already keep remote heads in a separate namespace, and we don't
> automagically look them up. And that's OK, because the way tags and
> heads work is fundamentally different. I can peek at your remote heads,
> but if I checkout or merge, I better make a local branch that matches
> your remote one.  Whereas with tags, I don't think that is the case.
> They're really a read-only thing, and you want them to stay in the
> remotes namespace.

I don't really have any strong opinions here. As long as the rules end up 
being fairly intuitive and usable in practice, I'm quite happy to let others 
decide on the details.

FWIW, if I run "git log some_topic" and "some_topic" only exists as a 
remote-tracking ref ("refs/remotes/origin/heads/some_topic"), I do think I 
would prefer git to DWIM it, instead of simply failing with:

  fatal: bad revision: some_topic

This would be somewhat in line with the DWIMing already performed by "git 
checkout some_topic".

Have fun! :)

...Johan

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