git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* resolving a (possibly remote) branch HEAD to a hash
@ 2015-08-07  9:16 Perry Hutchison
  2015-08-07 17:24 ` Junio C Hamano
  0 siblings, 1 reply; 4+ messages in thread
From: Perry Hutchison @ 2015-08-07  9:16 UTC (permalink / raw)
  To: git

Given the name of a branch, which might be in either refs/heads
or refs/remotes, how do I spell a query to obtain the HEAD commit
of the refs/heads instance if it exists, else of the refs/remotes
instance?

If the branch is local, I can get the hash of its HEAD commit using
git rev-parse:

  $ git rev-parse master
  23eba8be773637c1a995a3ffe3aeabe921edef76

However, if I try to do the same thing with a remote branch,
rev-parse barfs:

  $ git rev-parse r5.0.1
  r5.0.1
  fatal: ambiguous argument 'r5.0.1': unknown revision or path not in the working tree.
  Use '--' to separate paths from revisions

It works if I explicitly specify that I want the remote instance:

  $ git rev-parse origin/r5.0.1
  bb193a818fc984adbfd631951f3c89c16d5d3476

and the reference is, in fact, not ambiguous:

  $ git for-each-ref --format "%(refname)" | grep '/r5\.0\.1$'
  refs/remotes/origin/r5.0.1

Interestingly, master -- the one that works -- _is_ ambiguous:

  $ git for-each-ref --format "%(refname)" | grep '/master$'
  refs/heads/master
  refs/remotes/origin/master

Unfortunately grep is not a usable solution in general because
the branch name may contain . characters which grep (and egrep)
treat specially; nor is fgrep because it does _not_ treat $
specially; and I haven't found a for-each-ref pattern that will
select what I want.

One approach would be "git checkout r5.0.1" followed by
"git rev-parse HEAD", but I really don't want to perform a
checkout (e.g. the working tree is not necessarily clean).
I just want the same "resolve this reference" logic that
checkout clearly has to be using, but as a pure query that
does not change the state of either the repo or the working
tree.

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

* Re: resolving a (possibly remote) branch HEAD to a hash
  2015-08-07  9:16 resolving a (possibly remote) branch HEAD to a hash Perry Hutchison
@ 2015-08-07 17:24 ` Junio C Hamano
  2015-08-08  7:05   ` Perry Hutchison
  0 siblings, 1 reply; 4+ messages in thread
From: Junio C Hamano @ 2015-08-07 17:24 UTC (permalink / raw)
  To: Perry Hutchison; +Cc: git

perryh@pluto.rain.com (Perry Hutchison) writes:

>   $ git rev-parse r5.0.1
>   r5.0.1
>   fatal: ambiguous argument 'r5.0.1': unknown revision or path not in the working tree.
>   Use '--' to separate paths from revisions

This is not because of ambiguity among refs.  The message is telling
you:

    r5.0.1 is not interpretable as a revision, and it is not likely
    to be interpretable as a path.  If you meant to use it as a
    revision, put '--' after it, if you meant to use it as a path,
    put '--' before it.

When we try to see if the user meant "r5.0.1" as a revision or a
path on a command line that does not have "--", we make sure that it
can be interpreted only as a revision but not as a path or the other
way around.  You see the above error when it cannot be a revision
and it does not appear in the _current_ working tree.

The "not likely to be" part comes because this is a heuristic to
catch your typo.  "git log r5.0.1" _could_ be a request to show a
simplified history that ends with the current commit (i.e. HEAD)
that touched the path r5.0.1 that used to exist but was removed in
the history, and it is way too expensive to dig the history at that
point to see if a path r5.0.1 ever existed, so we only check the
current working tree.

Now, admittably, if you say "git rev-parse r5.0.1 --" in this
situation, it is not likely that the corrected command line will
succeed.  After all, the error message was issued because we already
know that r5.0.1 is _not_ a correct way to spell any revision.  So
the message _might_ want to be reworded to make it clear that:

 * 'r5.0.1' is not a valid revision name.  Perhaps you misspelt it?

 * 'r5.0.1' does not exist in the current working tree.  Perhaps you
   misspelt it?

 * With 'r5.0.1', you may be trying to refer to a path that (might)
   existed in the past.  If that is the case, please have "--"
   before it to explicitly tell us that you mean a path, not a
   revision.

> It works if I explicitly specify that I want the remote instance:
>
>   $ git rev-parse origin/r5.0.1
>   bb193a818fc984adbfd631951f3c89c16d5d3476

This is the correct behaviour and the expected usage.  When talking
about r5.0.1 that came from origin, origin/r5.0.1 is the shortest
and still valid way to spell it.

> and the reference is, in fact, not ambiguous:
>
>   $ git for-each-ref --format "%(refname)" | grep '/r5\.0\.1$'
>   refs/remotes/origin/r5.0.1

Because it is not about ambiguity among refs, this observation is
irrelevant ;-).

> Interestingly, master -- the one that works -- _is_ ambiguous:
>
>   $ git for-each-ref --format "%(refname)" | grep '/master$'
>   refs/heads/master
>   refs/remotes/origin/master

The thing is, there is no ambiguity among

    refs/heads/master
    refs/remotes/origin/master
    refs/remotes/another/master

because we do not say "append 'refs/remotes/<anything>/' for various
values of <anything> and see if such a ref exists" when resolving an
abbreviated refname 'master'.

Ambiguity among refs exists if you had these

    refs/heads/master
    refs/tags/master
    refs/remotes/master/HEAD

But that is not what we are seeing in your case.

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

* Re: resolving a (possibly remote) branch HEAD to a hash
  2015-08-07 17:24 ` Junio C Hamano
@ 2015-08-08  7:05   ` Perry Hutchison
  2015-08-10 18:41     ` Junio C Hamano
  0 siblings, 1 reply; 4+ messages in thread
From: Perry Hutchison @ 2015-08-08  7:05 UTC (permalink / raw)
  To: gitster; +Cc: git

Junio C Hamano <gitster@pobox.com> wrote:

> perryh@pluto.rain.com (Perry Hutchison) writes:
>
> >   $ git rev-parse r5.0.1
> >   r5.0.1
> >   fatal: ambiguous argument 'r5.0.1': unknown revision or path not in the working tree.
> >   Use '--' to separate paths from revisions
>
> This is not because of ambiguity among refs.  The message is telling
> you:
>
>     r5.0.1 is not interpretable as a revision, and it is not likely
>     to be interpretable as a path.  If you meant to use it as a
>     revision, put '--' after it, if you meant to use it as a path,
>     put '--' before it.
> ...
> Now, admittably, if you say "git rev-parse r5.0.1 --" in this
> situation, it is not likely that the corrected command line will
> succeed.

Yes, I tried that also.  It didn't work.

> ... we do not say "append 'refs/remotes/<anything>/' for various
> values of <anything> and see if such a ref exists" when resolving
> an abbreviated refname 'master'.

checkout appears to.

If I enter "git checkout foo"
   and the local branch refs/heads/foo exists
checkout will:
1. recognize that foo refers to a local branch
2. check out that local branch (refs/heads/foo).

OTOH, if I enter "git checkout foo"
   and there is no foo in refs/heads or refs/tags
   and refs/remotes/origin/foo _does_ exist
checkout will:
1. recognize that foo refers to a remote branch
2. set up a local branch (refs/heads/foo) that tracks
   the remote branch (refs/remotes/origin/foo)
3. check out the (now) local branch (refs/heads/foo).

However I don't think the command I want is spelled "checkout"
because, in either case, I want just step 1 -- with the result
being returned as the HEAD commit id of the selected branch.
I don't want the whole checkout experience, just the identification
of the commit that is the HEAD of the specified branch, which may
be either local (in refs/heads) or remote (in refs/heads/origin).

It is certainly possible that the command I am looking for is
not spelled "rev-parse" either -- and it does not matter whether
it is considered plumbing or porcelain.  It just needs to produce
the correct result, for either the local or the remote case.

(In the situation at hand I happen to know that the name will
always refer to a branch, not a tag, so a solution that looks
only in refs/heads and refs/remotes -- and not in refs/tags --
would be fine.  A solution that, like checkout, also looks in
refs/tags would also be OK.)

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

* Re: resolving a (possibly remote) branch HEAD to a hash
  2015-08-08  7:05   ` Perry Hutchison
@ 2015-08-10 18:41     ` Junio C Hamano
  0 siblings, 0 replies; 4+ messages in thread
From: Junio C Hamano @ 2015-08-10 18:41 UTC (permalink / raw)
  To: Perry Hutchison; +Cc: git

perryh@pluto.rain.com (Perry Hutchison) writes:

>> ... we do not say "append 'refs/remotes/<anything>/' for various
>> values of <anything> and see if such a ref exists" when resolving
>> an abbreviated refname 'master'.
>
> checkout appears to.

You are referring to this part of the documentation:

    If <branch> is not found but there does exist a tracking branch in
    exactly one remote (call it <remote>) with a matching name, treat as
    equivalent to

        $ git checkout -b <branch> --track <remote>/<branch>

A reader needs to read this part of the documentation a bit more
carefully in order to notice that it never says it is equivalent to:

        $ git checkout -b <branch> -t <branch> ;# NOT CORRECT

This behaviour was brought in by somebody who thought that, in the
context of "checkout" (and only in that context), it is clear that
missing <branch> that can only mean the sole <remote>/<branch> and
make that signal something more than what the user told "checkout"
to do: "If you want to check out a <branch>, and it does not exist
yet, you must wanted to create your own <branch> and start it at the
same commit as somebody else has at the tip of his <branch>".

This "clever" dwim is very specific to the way you interact with
"checkout" and generally does not apply when you want to run
anything other than "checkout", e.g. "rev-parse" or "log".

But it is _so_ convenient a short-cut, that it lets new people form
into an illusion that <branch> could be naming <remote>/<branch>.
That is an incorrect perception.

The rationale behind "signal something more" above goes like this:
if the user wanted to detach the head at the same commit as somebody
else's <branch>, she would explicitly have written

	$ git checkout <remote>/<branch>

to do so.  Because <remote>/<branch> is the shortest valid way to
name that remote-tracking branch (i.e. exactly because <branch> is
not a valid abbreviation for <remote>/<branch>), we can treat

	$ git checkout <branch>

when <branch> is not a local branch name specially.

It is sad and ironic that this checkout-specific DWIM works only
because <branch> does not mean <remote>/<branch>, but presence of
the DWIM gives a wrong illusion that it does X-<.

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

end of thread, other threads:[~2015-08-10 18:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-07  9:16 resolving a (possibly remote) branch HEAD to a hash Perry Hutchison
2015-08-07 17:24 ` Junio C Hamano
2015-08-08  7:05   ` Perry Hutchison
2015-08-10 18:41     ` Junio C Hamano

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 NNTP newsgroup(s).