{"thread":{"id":"40031","subject":"resolving a (possibly remote) branch HEAD to a hash","startedAt":"2015-08-07T09:16:22Z","lastAt":"2015-08-10T18:41:45Z","messageCount":4,"participants":["Perry Hutchison","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"267609","messageId":"55c47766.dDi8LaxdDqOeptUd%perryh@pluto.rain.com","threadId":"40031","inReplyTo":null,"subject":"resolving a (possibly remote) branch HEAD to a hash","fromName":"Perry Hutchison","fromEmail":"perryh@pluto.rain.com","sentAt":"2015-08-07T09:16:22Z","receivedAt":"2015-08-07T09:16:22Z","isPatch":false,"sender":{"key":"perryh@pluto.rain.com","avatar":null},"body":"Given the name of a branch, which might be in either refs/heads\nor refs/remotes, how do I spell a query to obtain the HEAD commit\nof the refs/heads instance if it exists, else of the refs/remotes\ninstance?\n\nIf the branch is local, I can get the hash of its HEAD commit using\ngit rev-parse:\n\n  $ git rev-parse master\n  23eba8be773637c1a995a3ffe3aeabe921edef76\n\nHowever, if I try to do the same thing with a remote branch,\nrev-parse barfs:\n\n  $ git rev-parse r5.0.1\n  r5.0.1\n  fatal: ambiguous argument 'r5.0.1': unknown revision or path not in the working tree.\n  Use '--' to separate paths from revisions\n\nIt works if I explicitly specify that I want the remote instance:\n\n  $ git rev-parse origin/r5.0.1\n  bb193a818fc984adbfd631951f3c89c16d5d3476\n\nand the reference is, in fact, not ambiguous:\n\n  $ git for-each-ref --format \"%(refname)\" | grep '/r5\\.0\\.1$'\n  refs/remotes/origin/r5.0.1\n\nInterestingly, master -- the one that works -- _is_ ambiguous:\n\n  $ git for-each-ref --format \"%(refname)\" | grep '/master$'\n  refs/heads/master\n  refs/remotes/origin/master\n\nUnfortunately grep is not a usable solution in general because\nthe branch name may contain . characters which grep (and egrep)\ntreat specially; nor is fgrep because it does _not_ treat $\nspecially; and I haven't found a for-each-ref pattern that will\nselect what I want.\n\nOne approach would be \"git checkout r5.0.1\" followed by\n\"git rev-parse HEAD\", but I really don't want to perform a\ncheckout (e.g. the working tree is not necessarily clean).\nI just want the same \"resolve this reference\" logic that\ncheckout clearly has to be using, but as a pure query that\ndoes not change the state of either the repo or the working\ntree.\n"},{"id":"267620","messageId":"xmqq8u9nyptx.fsf@gitster.dls.corp.google.com","threadId":"40031","inReplyTo":"55c47766.dDi8LaxdDqOeptUd%perryh@pluto.rain.com","subject":"Re: resolving a (possibly remote) branch HEAD to a hash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-08-07T17:24:10Z","receivedAt":"2015-08-07T17:24:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"perryh@pluto.rain.com (Perry Hutchison) writes:\n\n>   $ git rev-parse r5.0.1\n>   r5.0.1\n>   fatal: ambiguous argument 'r5.0.1': unknown revision or path not in the working tree.\n>   Use '--' to separate paths from revisions\n\nThis is not because of ambiguity among refs.  The message is telling\nyou:\n\n    r5.0.1 is not interpretable as a revision, and it is not likely\n    to be interpretable as a path.  If you meant to use it as a\n    revision, put '--' after it, if you meant to use it as a path,\n    put '--' before it.\n\nWhen we try to see if the user meant \"r5.0.1\" as a revision or a\npath on a command line that does not have \"--\", we make sure that it\ncan be interpreted only as a revision but not as a path or the other\nway around.  You see the above error when it cannot be a revision\nand it does not appear in the _current_ working tree.\n\nThe \"not likely to be\" part comes because this is a heuristic to\ncatch your typo.  \"git log r5.0.1\" _could_ be a request to show a\nsimplified history that ends with the current commit (i.e. HEAD)\nthat touched the path r5.0.1 that used to exist but was removed in\nthe history, and it is way too expensive to dig the history at that\npoint to see if a path r5.0.1 ever existed, so we only check the\ncurrent working tree.\n\nNow, admittably, if you say \"git rev-parse r5.0.1 --\" in this\nsituation, it is not likely that the corrected command line will\nsucceed.  After all, the error message was issued because we already\nknow that r5.0.1 is _not_ a correct way to spell any revision.  So\nthe message _might_ want to be reworded to make it clear that:\n\n * 'r5.0.1' is not a valid revision name.  Perhaps you misspelt it?\n\n * 'r5.0.1' does not exist in the current working tree.  Perhaps you\n   misspelt it?\n\n * With 'r5.0.1', you may be trying to refer to a path that (might)\n   existed in the past.  If that is the case, please have \"--\"\n   before it to explicitly tell us that you mean a path, not a\n   revision.\n\n> It works if I explicitly specify that I want the remote instance:\n>\n>   $ git rev-parse origin/r5.0.1\n>   bb193a818fc984adbfd631951f3c89c16d5d3476\n\nThis is the correct behaviour and the expected usage.  When talking\nabout r5.0.1 that came from origin, origin/r5.0.1 is the shortest\nand still valid way to spell it.\n\n> and the reference is, in fact, not ambiguous:\n>\n>   $ git for-each-ref --format \"%(refname)\" | grep '/r5\\.0\\.1$'\n>   refs/remotes/origin/r5.0.1\n\nBecause it is not about ambiguity among refs, this observation is\nirrelevant ;-).\n\n> Interestingly, master -- the one that works -- _is_ ambiguous:\n>\n>   $ git for-each-ref --format \"%(refname)\" | grep '/master$'\n>   refs/heads/master\n>   refs/remotes/origin/master\n\nThe thing is, there is no ambiguity among\n\n    refs/heads/master\n    refs/remotes/origin/master\n    refs/remotes/another/master\n\nbecause we do not say \"append 'refs/remotes/<anything>/' for various\nvalues of <anything> and see if such a ref exists\" when resolving an\nabbreviated refname 'master'.\n\nAmbiguity among refs exists if you had these\n\n    refs/heads/master\n    refs/tags/master\n    refs/remotes/master/HEAD\n\nBut that is not what we are seeing in your case.\n"},{"id":"267653","messageId":"55c5aa2a.sK/tpvXyORYGVq0d%perryh@pluto.rain.com","threadId":"40031","inReplyTo":"xmqq8u9nyptx.fsf@gitster.dls.corp.google.com","subject":"Re: resolving a (possibly remote) branch HEAD to a hash","fromName":"Perry Hutchison","fromEmail":"perryh@pluto.rain.com","sentAt":"2015-08-08T07:05:14Z","receivedAt":"2015-08-08T07:05:14Z","isPatch":false,"sender":{"key":"perryh@pluto.rain.com","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n\n> perryh@pluto.rain.com (Perry Hutchison) writes:\n>\n> >   $ git rev-parse r5.0.1\n> >   r5.0.1\n> >   fatal: ambiguous argument 'r5.0.1': unknown revision or path not in the working tree.\n> >   Use '--' to separate paths from revisions\n>\n> This is not because of ambiguity among refs.  The message is telling\n> you:\n>\n>     r5.0.1 is not interpretable as a revision, and it is not likely\n>     to be interpretable as a path.  If you meant to use it as a\n>     revision, put '--' after it, if you meant to use it as a path,\n>     put '--' before it.\n> ...\n> Now, admittably, if you say \"git rev-parse r5.0.1 --\" in this\n> situation, it is not likely that the corrected command line will\n> succeed.\n\nYes, I tried that also.  It didn't work.\n\n> ... we do not say \"append 'refs/remotes/<anything>/' for various\n> values of <anything> and see if such a ref exists\" when resolving\n> an abbreviated refname 'master'.\n\ncheckout appears to.\n\nIf I enter \"git checkout foo\"\n   and the local branch refs/heads/foo exists\ncheckout will:\n1. recognize that foo refers to a local branch\n2. check out that local branch (refs/heads/foo).\n\nOTOH, if I enter \"git checkout foo\"\n   and there is no foo in refs/heads or refs/tags\n   and refs/remotes/origin/foo _does_ exist\ncheckout will:\n1. recognize that foo refers to a remote branch\n2. set up a local branch (refs/heads/foo) that tracks\n   the remote branch (refs/remotes/origin/foo)\n3. check out the (now) local branch (refs/heads/foo).\n\nHowever I don't think the command I want is spelled \"checkout\"\nbecause, in either case, I want just step 1 -- with the result\nbeing returned as the HEAD commit id of the selected branch.\nI don't want the whole checkout experience, just the identification\nof the commit that is the HEAD of the specified branch, which may\nbe either local (in refs/heads) or remote (in refs/heads/origin).\n\nIt is certainly possible that the command I am looking for is\nnot spelled \"rev-parse\" either -- and it does not matter whether\nit is considered plumbing or porcelain.  It just needs to produce\nthe correct result, for either the local or the remote case.\n\n(In the situation at hand I happen to know that the name will\nalways refer to a branch, not a tag, so a solution that looks\nonly in refs/heads and refs/remotes -- and not in refs/tags --\nwould be fine.  A solution that, like checkout, also looks in\nrefs/tags would also be OK.)\n"},{"id":"267787","messageId":"xmqq614nugt2.fsf@gitster.dls.corp.google.com","threadId":"40031","inReplyTo":"55c5aa2a.sK/tpvXyORYGVq0d%perryh@pluto.rain.com","subject":"Re: resolving a (possibly remote) branch HEAD to a hash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-08-10T18:41:45Z","receivedAt":"2015-08-10T18:41:45Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"perryh@pluto.rain.com (Perry Hutchison) writes:\n\n>> ... we do not say \"append 'refs/remotes/<anything>/' for various\n>> values of <anything> and see if such a ref exists\" when resolving\n>> an abbreviated refname 'master'.\n>\n> checkout appears to.\n\nYou are referring to this part of the documentation:\n\n    If <branch> is not found but there does exist a tracking branch in\n    exactly one remote (call it <remote>) with a matching name, treat as\n    equivalent to\n\n        $ git checkout -b <branch> --track <remote>/<branch>\n\nA reader needs to read this part of the documentation a bit more\ncarefully in order to notice that it never says it is equivalent to:\n\n        $ git checkout -b <branch> -t <branch> ;# NOT CORRECT\n\nThis behaviour was brought in by somebody who thought that, in the\ncontext of \"checkout\" (and only in that context), it is clear that\nmissing <branch> that can only mean the sole <remote>/<branch> and\nmake that signal something more than what the user told \"checkout\"\nto do: \"If you want to check out a <branch>, and it does not exist\nyet, you must wanted to create your own <branch> and start it at the\nsame commit as somebody else has at the tip of his <branch>\".\n\nThis \"clever\" dwim is very specific to the way you interact with\n\"checkout\" and generally does not apply when you want to run\nanything other than \"checkout\", e.g. \"rev-parse\" or \"log\".\n\nBut it is _so_ convenient a short-cut, that it lets new people form\ninto an illusion that <branch> could be naming <remote>/<branch>.\nThat is an incorrect perception.\n\nThe rationale behind \"signal something more\" above goes like this:\nif the user wanted to detach the head at the same commit as somebody\nelse's <branch>, she would explicitly have written\n\n\t$ git checkout <remote>/<branch>\n\nto do so.  Because <remote>/<branch> is the shortest valid way to\nname that remote-tracking branch (i.e. exactly because <branch> is\nnot a valid abbreviation for <remote>/<branch>), we can treat\n\n\t$ git checkout <branch>\n\nwhen <branch> is not a local branch name specially.\n\nIt is sad and ironic that this checkout-specific DWIM works only\nbecause <branch> does not mean <remote>/<branch>, but presence of\nthe DWIM gives a wrong illusion that it does X-<.\n"}]}