Update: since Git 2.5 (mid-2015 or so), servers now may expose raw hashes. This is a server-side configuration item. You must have sufficient control on the server to set it, and you should consider potential security issues before you do set it. See this answer to Retrieve specific commit from a remote Git repository for details. (Original answer, which applies to pre-2.5 or if the configuration knob is not set, below.)
The git fetch
command delivers references (names, not raw commit-IDs) to the remote, more or less. (More specifically, use git ls-remote remotename
to see what the remote is willing to give you in terms of names. This produces a list of SHA-1s on the left with names on the right, and the only thing your fetch
can ask for is the names-on-the-right. At which point you'll get the ID-on-the-left if the name on the remote still points to that ID, so it depends on how actively that remote gets updated.)
It is possible, in various ways, to deliver raw commit-IDs to a remote and ask that remote what is visible starting from that point, and sometimes working backwards through history as well, but not via git fetch
. (You can use git archive
but the remote can decide whether or not to allow you to access via raw commit-IDs; or with remotes that have web server access, including to specific commits, you can often just view the top-level contents of a commit, and use that to "drill down", as they say, to the various pieces. But that is a very slow way to do it.)
If you'd like to use git fetch
to get some particular commit, probably the easiest way to do that is to have someone with access to the remote attach a name—most likely a tag—to that commit ID. Then you can have your git fetch
bring over that refspec, and put it under any other refspec you like. For instance, suppose you can ssh directly to whatever hosts origin
:
$ ssh our.origin.host 'cd /repos/repo.git; git tag temporary f1e32e1'
[enter password, etc; observe tag created]
$ git fetch origin refs/tags/temporary:refs/heads/newbranch
[observe fetch happen; now you have local branch 'newbranch']
$ ssh our.origin.host 'cd /repos/repo.git; git tag -d temporary'
Note that the name need not be a branch, it need only be a reference you can pull over with git fetch
and see with git ls-remote
. You then use a name that will match that on the left-hand-side of your refspec when fetching. The name created in your repo is controlled by the right-hand-side of the refspec (refs/heads/newbranch
in the example above).
This is also the answer to your last paragraph question: you can only name things that have names on the remote (this is partly intended to avoid "leaking" unnamed commits that remain in a repository before garbage-collection, so it's considered a feature rather than a bug). These names go on the LHS of the refspec. Your own names go on the right.
Your name on the right is assumed to be a branch or tag name (based on what the name on the left matches, though you can explicitly spell out refs/heads/
or refs/tags/
to override it), so even though f1e32e1...
is a valid SHA-1, it's treated as a branch name here—the missing name on the left translates to HEAD
, as missing names almost always do—and git fetch
creates a branch whose name is disturbingly SHA-1-ish. (Incidentally I once created a branch name that looked like an SHA-1, and later confused myself. I forget exactly what the name was, something like de-bead
without the hyphen. I renamed it to the hyphenated version just to make it clear I didn't mean a raw commit ID! :-) )