How to shallow clone a specific commit with depth 1?
Asked Answered
E

5

176

Is it possible to shallow clone a specific commit in a repository, i.e. with depth 1? Something like

git clone http://myrepo.git 728a4d --depth 1

to get the repository state as it is at the commit with SHA 728a4d...?

The motivation is to avoid having to clone the whole repository, then check out that specific commit, when we're only interested in the state of the repository at that specific commit.

Exposed answered 7/7, 2015 at 20:43 Comment(1)
Possible duplicate of How to clone git repository with specific revision/changeset?Palmar
O
183

Starting with Git 2.5.0 (which needs to be available at both the client and server side) you can set uploadpack.allowReachableSHA1InWant=true on the server side to enable fetching of specific SHA1s (must be the full SHA1, not an abbreviation):

git init
git remote add origin <url>
git fetch --depth 1 origin <sha1>
git checkout FETCH_HEAD

Note that I did not find a syntax to do this with git clone directly.

Orpah answered 31/3, 2017 at 8:36 Comment(7)
I got an error saying error: pathspec 'FETCH_HEAD' did not match any file(s) known to gitByroad
Playing around with this, I found that it only worked on a very modern version of git client. Even git 2.17 didn't work for me. I rebuilt git at 2.24 and that worked.Cocotte
Worked for me with 2.27.0.windows.1 on github.com/dotnet/aspnetcore @ 62c098bc170f50feca15916e81cb7f321ffc52ffHunsaker
This now does work with GitHub.Confess
It is also possible to specify a branch or tag instead of the sha1 commit idNereus
N.B. you have to provide the entire sha1 commit, not an abbreviation!Zubkoff
command stayed stuck for ~5mn outputting nothing, but finally workedStubblefield
M
45

NOTE: My example doesn't help to clone to by a commit hash but it will help to clone a tag and have a lightweight repository.

If you have to have only one commit in your "clone" and you are going to use commit hash, short answer is NO.

I use this command construction (tested on v2.13.2.windows.1) for tags:

git clone --depth 1 [email protected]:VENDOR/REPO.git --branch 1.23.0 --single-branch

Full example:

$ git clone --depth 1 [email protected]:Seldaek/monolog.git --branch 1.23.0 --single-branch
Cloning into 'monolog'...
remote: Counting objects: 201, done.
remote: Compressing objects: 100% (188/188), done.
remote: Total 201 (delta 42), reused 32 (delta 5), pack-reused 0
Receiving objects: 100% (201/201), 190.30 KiB | 0 bytes/s, done.
Resolving deltas: 100% (42/42), done.
Note: checking out 'fd8c787753b3a2ad11bc60c063cff1358a32a3b4'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

$ cd monolog

.git dir size (267K vs 2.6M by using full clone):

$ du -h --max-depth=0 .git
267K    .git

I'd like to denote, --branch can take a tag/branch.

https://git-scm.com/docs/git-clone#git-clone---branchltnamegt

--branch can also take tags and detaches the HEAD at that commit in the resulting repository.

UPD

In a nutshell, it can take "refs". You may read more here: What does the git error message “Server does not allow request for unadvertised object” mean?

Also, there is no tricks like:

git fetch --depth 1 origin <COMMIT_HASH>

Thanks @BenjiWiebe for pointing me in my mistake.

Monophony answered 12/2, 2018 at 14:19 Comment(10)
A million upvotes for this. Way too many people claim this is not possible.Transparent
--branch does not support commits, only branch names... this doesn't solve it at all. As it not true.Haste
Kirby I'm trying to do this now, and it does not work for commits, as @Haste says. Perhaps it works for tags and branches, but I'm trying to do it with commit hashes and that doesn't work.She
@BenjiWiebe, I got your point guys. Yeah, it seems, GIT cannot determine a specific commit. :-/ I'll dig it deeper.Monophony
@BenjiWiebe, so, unfortunately there is no way to use commit hash. Just double checked.Monophony
@BenjiWiebe, in some repositories, approach from sschuberth can work.Monophony
Are with @Haste --branch does not work with commits. It did work after pushing a tag for the commit I was interested inCarat
In common sense, it doesn't work with pure commit hash.Monophony
For the command git clone --depth 1 [email protected]:Seldaek/monolog.git --branch fd8c787753b3a2ad11bc60c063cff1358a32a3b4 --single-branchMonophony
Result: warning: Could not find remote branch fd8c787753b3a2ad11bc60c063cff1358a32a3b4 to clone.Monophony
H
8

Try using while in bash:

git clone --depth=1 $url
i=1; while ! git show $sha1; do git fetch --depth=$((i+=1)); done

This is pretty slow because it fetches each commit individually; you could increase the increment (to fetch commits in batches and improve performance over a network) but it's still a brute force approach.

Holliehollifield answered 12/9, 2016 at 4:41 Comment(3)
Why the down votes? ...I actually needed to do this and my solution got it done!Holliehollifield
what if you want a commit made years ago or that doesn't exist anymore?Procrastinate
@DaanBakker, if the commit doesn't exist anymore then the only way to determine this is to fetch the entire remote repository and if it's not there and there are no other clones then you are at a dead end unfortunately.Holliehollifield
C
6

The immediate answer is: You can't do it using a git clone directly.
Why? A detailed explanation can be found here: Why Isn't There A Git Clone Specific Commit Option?

What else can you do?

How to clone the repository to a specific commit? (full clone)

# Create empty repository to store your content
git clone <url>
git reset <sha-1> --hard

More info:

How to clone a single branch?

git clone <url> --branch <branch_name> --single-branch <folder_name>

How to clone only latest commit from a given branch?

git clone <url> --depth=1 --branch <branch_name> --single-branch <folder_name>

How to shallow clone a specific commit with depth 1?

As @sschuberth commented out: --depth implies --single-branch.

Instead of clone use the fetch command:

# fetch a commit (or branch or tag) of interest
# In this case you will have the full history of this commit
git fetch origin <sha1>
Canny answered 7/7, 2015 at 21:8 Comment(6)
This does not answer the question. The questioner wants to know how to checkout a single commit even if there is no ref pointing directly to it.Douche
Sorry, missed it, somehow i understood he need the latest commit. Updating the answer. Thank youCanny
--depth implies --single-branch, so you could drop it in that case.Orpah
Does git fetch actually accept an SHA1 for refspec?Interrex
nope. its simply "downloading" the data which match the name of the branchCanny
Using git clone seems more intuitive and less error prone, compared to the higher voted multiple git commands version above. So my question is, how to make a tag on a specific commit without first cloning anything to local? I think if we can make a tag on that commit, then using clone will be possible?Alcheringa
E
-1

The answer from https://mcmap.net/q/12956/-how-to-shallow-clone-a-specific-commit-with-depth-1 works, but it creates a detached head. The following slight modification will avoid this, resulting in a regular single-commit checkout:

# Set those variables:
repourl='https://some.test/somerepo.git'
commit=4e1243bd22c66e76c2ba9eddc1f91394e57f9f83

# Paste the remaining commands unmodified into your shell.       
: ${repourl:?}; reponame=${repourl##*/}; reponame=${reponame%.git}
git init -- "${reponame:?}"
cd -- "${reponame:?}"
git remote add origin "${repourl:?}"
git fetch -q --depth=1 origin "${commit:?}"
git reset --hard FETCH_HEAD

Note that it is imperative that $commit is set to a full commit ID. An abbreviation of the commit ID does not work.

Enedina answered 8/5, 2023 at 19:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.