1

This works:

 git clone --branch='master' \
    --single-branch \
    git@bitbucket.org:teros/datavana.git 

but this doesn't:

commit='fda0b49f81d0b67ad8a1413eca129498b9eb61db'

git clone --branch="$commit" \
    --single-branch  \
    git@bitbucket.org:teros/datavana.git 

the error I get is:

Cloning into 'datavana'...
Warning: Permanently added the RSA host key for IP address 'xx.xxx.93.1' to the list of known hosts.
warning: Could not find remote branch fda0b49f81d0b67ad8a1413eca129498b9eb61db to clone.
fatal: Remote branch fda0b49f81d0b67ad8a1413eca129498b9eb61db not found in upstream origin

I know for sure that the git commit id is in the remote - so is there some reason this won't work? Is there a way to clone a single commit (instead of by branch name)?

3 Answers3

1

This was intentional, to allow some control over what could be fetched from repos, especially in setups where a single object db serves multiple repos.

You can enable fetching any arbitrary sha by setting uploadpack.allowanysha1inwant in the upstream repo, after which you'll be able to fetch arbitrary sha's from it by giving the full hash, but git clone isn't set up for arbitrary refspecs, you'll have to e.g.

git init .
git remote add origin git@bitbucket.org:teros/datavana.git
git fetch origin fda0b49f81d0b67ad8a1413eca129498b9eb61db:refs/heads/newbranch

where the init and remote add are basically everything extra the clone does for you anyway.

I don't know whether anything but Git itself supports this.

jthill
  • 55,082
  • 5
  • 77
  • 137
0

At a technical level, it doesn't work because the SHA is not a branch or tag name. As documented on the git clone page (https://git-scm.com/docs/git-clone) the --branch option takes a branch name or a tag name, not an arbitrary expression that resolves to any old commit.

But that's different from most commands that want you to specify a commit.... so why?

The motivation for this is kinda-sorta-security; the idea is a commit that's rewritten out of the remote's refs' history cannot be accessed by someone who knows its SHA even if it hasn't been physically deleted.

More complex rules (like taking any expression but having the server verify that the resulting commit is reachable) could accomplish that too... but they're more complicated, and at least as of now that's not something the git devs have felt the need to support.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • well, I mean a git-tag is just a name/pointer to a commit id right, so it seems like a bug or lack of a feature? –  Aug 01 '19 at 21:28
  • @MrCholo - I don't follow your thinking. There is intent behind allowing the named commits to be downloaded, but not arbitrary commits by SHA. It is intended, so it is not a bug. It was left out deliberately, so it is not "missing". It may be a poor design choice, but frankly that's subjective. – Mark Adelsberger Aug 12 '19 at 13:15
0

As Mark Adelsberger said, this is a pseudo-security1 feature. However, it's not actually limited to clone's --single-branch / --branch options. It also applies to git fetch.

There is a configuration knob you can set in a repository to enable things. In fact, there are two of them, both listed in the git config documentation:

uploadpack.allowReachableSHA1InWant

      Allow upload-pack to accept a fetch request that asks for an object that is reachable from any ref tip. However, note that calculating object reachability is computationally expensive. Defaults to false. Even if this is false, a client may be able to steal objects via the techniques described in the "SECURITY" section of the gitnamespaces[7] man page; it’s best to keep private data in a separate repository.

uploadpack.allowAnySHA1InWant

      Allow upload-pack to accept a fetch request that asks for any object at all. Defaults to false.

Since these both default tofalse, your standard Git server won't let you do what you want. If you have access to the server itself, though, you can configure one (or both but that's redundant) to true. Once you do that, you can:

  • git clone --depth 1 any branch at all
  • git fetch the one commit you want, by hash ID (create a branch name if you like, or let the old FETCH_HEAD compatibility stuff remember the hash ID)
  • check out the one commit you want (now HEAD holds the hash ID as a detached HEAD)

and then optionally delete the one branch you cloned shallowly. (You still have a shallow repository, which you can't really deepen properly since you don't know which branch name(s) to key off of from your origin. But no doubt the reason you're doing all this is to get just the one commit.)

(Or, see jthill's answer: git init, etc., then fetch.)

Consider also / instead, using git archive to make a non-Git-repo archive out of the (single) commit you want.


1Pseudo-security: because it superficially resembles security, but may not actually be secure. See Pseudo-.

torek
  • 448,244
  • 59
  • 642
  • 775
  • ok thanks, so is `git clone --depth=1 master` equivalent to `git clone --single-branch --branch=master`? or do I need to add `--depth=1` to the latter command? –  Aug 01 '19 at 22:12
  • `--depth` implies `--single-branch` (which can be canceled with `--no-single-branch`, then you have a depth 1 clone of each branch). You don't need to pick a branch name with `-b` since you don't care which branch actually winds up as the single branch. – torek Aug 01 '19 at 22:15