Reputation: 3220
Is there any way to retrieve only one specific commit from a remote Git repo without cloning it on my PC? The structure of remote repo is absolutely same as that of mine and hence there won't be any conflicts but I have no idea how to do this and I don't want to clone that huge repository.
I am new to git, is there any way?
Upvotes: 252
Views: 460100
Reputation: 1323125
Starting with Git version 2.5+ (Q2 2015), fetching a single commit (without cloning the full repo) is actually possible.
See commit 68ee628 by Fredrik Medley (moroten
), 21 May 2015.
(Merged by Junio C Hamano -- gitster
-- in commit a9d3493, 01 Jun 2015)
You now have a new config (on the server side)
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 tofalse
.
If you combine that server-side configuration with a shallow clone (git fetch --depth=1
), you can ask for a single commit (see t/t5516-fetch-push.sh
:
git fetch --depth=1 ../testrepo/.git <full-length SHA1>
You can use the git cat-file
command to see that the commit has been fetched:
git cat-file commit <full-length SHA1>
"
git upload-pack
" that serves "git fetch
" can be told to serve commits that are not at the tip of any ref, as long as they are reachable from a ref, withuploadpack.allowReachableSHA1InWant
configuration variable.
As noted by matt in the comments:
Note that SHA must be the full unabbreviated SHA, otherwise Git will claim it couldn't find the commit
The full documentation is:
upload-pack
: optionally allow fetching reachable sha1
With
uploadpack.allowReachableSHA1InWant
configuration option set on the server side, "git fetch
" can make a request with a "want" line that names an object that has not been advertised (likely to have been obtained out of band or from a submodule pointer).
Only objects reachable from the branch tips, i.e. the union of advertised branches and branches hidden bytransfer.hideRefs
, will be processed.
Note that there is an associated cost of having to walk back the history to check the reachability.This feature can be used when obtaining the content of a certain commit, for which the sha1 is known, without the need of cloning the whole repository, especially if a shallow fetch is used.
Useful cases are e.g.
- repositories containing large files in the history,
- fetching only the needed data for a submodule checkout,
- when sharing a sha1 without telling which exact branch it belongs to and in Gerrit, if you think in terms of commits instead of change numbers.
(The Gerrit case has already been solved throughallowTipSHA1InWant
as every Gerrit change has a ref.)
Git 2.6 (Q3 2015) will improve that model.
See commit 2bc31d1, commit cc118a6 (28 Jul 2015) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit 824a0be, 19 Aug 2015)
refs
: support negativetransfer.hideRefs
If you hide a hierarchy of refs using the
transfer.hideRefs
config, there is no way to later override that config to "unhide" it.
This patch implements a "negative" hide which causes matches to immediately be marked as unhidden, even if another match would hide it.
We take care to apply the matches in reverse-order from how they are fed to us by the config machinery, as that lets our usual "last one wins" config precedence work (and entries in.git/config
, for example, will override/etc/gitconfig
).So you can now do:
git config --system transfer.hideRefs refs/secret git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide
refs/secret
in all repos, except for one public bit in one specific repo.
Git 2.7 (Nov/Dec 2015) will improve again:
See commit 948bfa2, commit 00b293e (05 Nov 2015), commit 78a766a, commit 92cab49, commit 92cab49, commit 92cab49 (03 Nov 2015), commit 00b293e, commit 00b293e (05 Nov 2015), and commit 92cab49, commit 92cab49, commit 92cab49, commit 92cab49 (03 Nov 2015) by Lukas Fleischer (lfos
).
Helped-by: Eric Sunshine (sunshineco
).
(Merged by Jeff King -- peff
-- in commit dbba85e, 20 Nov 2015)
config.txt
: document the semantics ofhideRefs
with namespaces
Right now, there is no clear definition of how
transfer.hideRefs
should behave when a namespace is set.
Explain thathideRefs
prefixes match stripped names in that case. This is howhideRefs
patterns are currently handled in receive-pack.
hideRefs: add support for matching full refs
In addition to matching stripped refs, one can now add
hideRefs
patterns that the full (unstripped) ref is matched against.
To distinguish between stripped and full matches, those new patterns must be prefixed with a circumflex (^
).
Hence, the new documentation:
transfer.hideRefs:
If a namespace is in use, the namespace prefix is stripped from each reference before it is matched against
transfer.hiderefs
patterns.
For example, ifrefs/heads/master
is specified intransfer.hideRefs
and the current namespace isfoo
, thenrefs/namespaces/foo/refs/heads/master
is omitted from the advertisements, butrefs/heads/master
andrefs/namespaces/bar/refs/heads/master
are still advertised as so-called "have" lines.
In order to match refs before stripping, add a^
in front of the ref name. If you combine!
and^
,!
must be specified first.
R.. mentions in the comments the config uploadpack.allowAnySHA1InWant
, which allows upload-pack
to accept a fetch
request that asks for any object at all. (Defaults to false
).
See commit f8edeaa (Nov. 2016, Git v2.11.1) by David "novalis" Turner (novalis
):
upload-pack
: optionally allow fetching any sha1
It seems a little silly to do a reachabilty check in the case where we trust the user to access absolutely everything in the repository.
Also, it's racy in a distributed system -- perhaps one server advertises a ref, but another has since had a force-push to that ref, and perhaps the two HTTP requests end up directed to these different servers.
Regarding that setting, Git 2.48 (Q1 2025), batch 7, updates its documentation to clarify that 'uploadpack.allowAnySHA1InWant
' implies both 'allowTipSHA1InWant
' and 'allowReachableSHA1InWant
'.
See commit bddfcce (19 Oct 2024) by Piotr Szlazak (pszlazak
).
(Merged by Taylor Blau -- ttaylorr
-- in commit aabbcf2, 01 Nov 2024)
doc
: document howuploadpack.allowAnySHA1InWant
impact other allow optionsSigned-off-by: Piotr Szlazak
Signed-off-by: Taylor Blau
Document how setting of
uploadpack.allowAnySHA1InWant
influences otheruploadpack
options -allowTipSHA1InWant
andallowReachableSHA1InWant
.
git config
now includes in its man page:
It implies
uploadpack.allowTipSHA1InWant
anduploadpack.allowReachableSHA1InWant
. If set totrue
it will enable both of them, it set tofalse
it will disable both of them. By default not set.
With Git 2.34 (Q4 2021), "git upload-pack
"(man) which runs on the other side of git fetch
(man) forgot to take the ref namespaces into account when handling want-ref requests.
See commit 53a66ec, commit 3955140, commit bac01c6 (13 Aug 2021) by Kim Altintop (kim
).
(Merged by Junio C Hamano -- gitster
-- in commit 1ab13eb, 10 Sep 2021)
docs
: clarify the interaction of transfer.hideRefs and namespacesSigned-off-by: Kim Altintop
Reviewed-by: Jonathan Tan
Expand the section about namespaces in the documentation of
transfer.hideRefs
to point out the subtle differences betweenupload-pack
andreceive-pack
.3955140 ("
upload-pack.c
: treat want-ref relative to namespace", 2021-07-30, Git v2.34.0 -- merge listed in batch #5) taughtupload-pack
to rejectwant-ref
s for hidden refs, which is now mentioned.
It is clarified that at no point the name of a hidden ref is revealed, but the object id it points to may.
git config
now includes in its man page:
reference before it is matched against
transfer.hiderefs
patterns. In order to match refs before stripping, add a^
in front of the ref name. If you combine!
and^
,!
must be specified first.
git config
now includes in its man page:
is omitted from the advertisements. If
uploadpack.allowRefInWant
is set,upload-pack
will treatwant-ref refs/heads/master
in a protocol v2fetch
command as ifrefs/namespaces/foo/refs/heads/master
did not exist.receive-pack
, on the other hand, will still advertise the object id the ref is pointing to without mentioning its name (a so-called ".hav
e" line).
With Git 2.39 (Q4 2022), "git receive-pack
"(man) used to use all the local refs as the boundary for checking connectivity of the data git push
(man) sent, but now it uses only the refs that it advertised to the pusher.
In a repository with the .hideRefs
configuration, this reduces the resources needed to perform the check.
See commit bcec678, commit 5ff36c9, commit 8c1bc2a, commit 1e9f273, commit 05b9425, commit 9b67eb6, commit 5eeb9aa (17 Nov 2022) by Patrick Steinhardt (pks-t
).
(Merged by Junio C Hamano -- gitster
-- in commit f8828f9, 23 Nov 2022)
revision
: add new parameter to exclude hidden refsSigned-off-by: Patrick Steinhardt
Signed-off-by: Taylor Blau
Users can optionally hide refs from remote users in git-upload-pack(1), git-receive-pack(1) and others via the
transfer.hideRefs
, but there is not an easy way to obtain the list of all visible or hidden refs right now.
We'll require just that though for a performance improvement in our connectivity check.Add a new option
--exclude-hidden=
that excludes any hidden refs from the next pseudo-ref like--all
or--branches
.
rev-list-options
now includes in its man page:
--exclude-hidden=[receive|uploadpack]
Do not include refs that would be hidden by
git-receive-pack
orgit-upload-pack
by consulting the appropriatereceive.hideRefs
oruploadpack.hideRefs
configuration along withtransfer.hideRefs
(seegit config
). This option affects the next pseudo-ref option--all
or--glob
and is cleared after processing them.
And:
rev-parse
: add--exclude-hidden=
optionSigned-off-by: Patrick Steinhardt
Signed-off-by: Taylor Blau
Add a new
--exclude-hidden=
option that is similar to the one we just added to git-rev-list(1).
Given a section nameuploadpack
orreceive
as argument, it causes us to exclude all references that would be hidden by the respective$section.hideRefs
configuration.
git rev-parse
now includes in its man page:
--exclude-hidden=[receive|uploadpack]
Do not include refs that would be hidden by
git-receive-pack
orgit-upload-pack
by consulting the appropriatereceive.hideRefs
oruploadpack.hideRefs
configuration along withtransfer.hideRefs
(seegit config
). This option affects the next pseudo-ref option--all
or--glob
and is cleared after processing them.
Upvotes: 148
Reputation: 399
# Create a directory for a repository
$ mkdir depthRepo
# Make a working directory
$ cd depthRepo
# Create an empty git repository
$ git -c init.defaultBranch=master init
# pull the objects of the required commit
# In the example:
# Remote repository - https://git.savannah.gnu.org/git/guix.git,
# Commit for download - 425cf1fbe2fff25bda1b5eb948ce01170d23ab6a
# !!! Do not forget to put a colon in the specification (425cf1fbe2fff25bda1b5eb948ce01170d23ab6a:)
$ git fetch --depth=1 https://git.savannah.gnu.org/git/guix.git 425cf1fbe2fff25bda1b5eb948ce01170d23ab6a:
# Check out the commit to the working directory
$ git -c advice.detachedHead=false checkout 425cf1fbe2fff25bda1b5eb948ce01170d23ab6a
# Delete Git repository files
$ rm -rf .git
Upvotes: 0
Reputation: 11
# make sure you fetch from the origin first
$ git fetch origin
# view code at COMMIT_ID (abc123)
$ git checkout abc123
# bring only COMMIT_ID (abc123)
# to your branch
# assuming your branch is master
$ git checkout master
$ git cherry-pick abc123
# bring all changes up to
# COMMIT_ID (abc123) to your branch
# assuming your branch is master
$ git checkout master
$ git merge abc123
reference - https://unfuddle.com/stack/tips-tricks/git-pull-specific-commit/
Upvotes: 0
Reputation: 90276
You only clone once, so if you already have a clone of the remote repository, pulling from it won't download everything again. Just indicate what branch you want to pull, or fetch the changes and checkout the commit you want.
Fetching from a new repository is very cheap in bandwidth, as it will only download the changes you don't have. Think in terms of Git making the right thing, with minimum load.
Git stores everything in .git
folder. A commit can't be fetched and stored in isolation, it needs all its ancestors. They are interrelated.
To reduce download size you can however ask git to fetch only objects related to a specific branch or commit:
git fetch origin refs/heads/branch:refs/remotes/origin/branch
This will download only commits contained in remote branch branch
(and only the ones that you miss), and store it in origin/branch
. You can then merge or checkout.
You can also specify only a SHA1 commit id -- but note you must use the full 40-character unabbreviated identifier:
git fetch origin 96de5297df87096de5297df87096de5297df8709:refs/remotes/origin/foo-commit
This will download only the commit of the specified SHA-1 96de5297df87096de5297df87096de5297df8709 (and its ancestors that you miss), and store it as (non-existing) remote branch origin/foo-commit
.
Upvotes: 111
Reputation: 24043
You can simply fetch a single commit of a remote repo with
git fetch <repo> <commit-id>
where,
<repo>
can be a remote repo name (e.g. origin
) or even a remote repo URL (e.g. https://git.foo.com/myrepo.git
)<commit-id>
is the commit's IDfor example
git fetch https://git.foo.com/myrepo.git 0a071603d87e0b89738599c160583a19a6d95545
after you fetched the commit (and the missing ancestors) you can simply checkout it with
git checkout FETCH_HEAD
Note that this will bring you in the "detached head" state.
Upvotes: 82
Reputation: 21
In a project we had a problem so that we had to revert back to a certain commit. We made it with the following command successfully:
git reset --hard <commitID>
Upvotes: -3
Reputation: 16971
If the requested commit is in the pull requests of the remote repo, you can get it by its ID:
# Add the remote repo path, let's call it 'upstream':
git remote add upstream https://github.com/repo/project.git
# checkout the pull ID, for example ID '60':
git fetch upstream pull/60/head && git checkout FETCH_HEAD
Upvotes: 0
Reputation: 99960
This works best:
git fetch origin specific_commit
git checkout -b temp FETCH_HEAD
name "temp" whatever you want...this branch might be orphaned though
Upvotes: 11
Reputation: 7249
You can simply fetch the remote repo with:
git fetch <repo>
where,
<repo>
can be a remote repo name (e.g. origin
) or even a remote repo URL (e.g. https://git.foo.com/myrepo.git
)for example:
git fetch https://git.foo.com/myrepo.git
after you fetched the repos you may merge the commits that you want (since the question is about retrieve one commit, instead merge you may use cherry-pick to pick just one commit):
git merge <commit>
<commit>
can be the SHA1 commitfor example:
git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545
or
git merge 0a071603d87e0b89738599c160583a19a6d95545
if is the latest commit that you want to merge, you also may use FETCH_HEAD variable :
git cherry-pick (or merge) FETCH_HEAD
Upvotes: 21
Reputation: 29
Finally i found a way to clone specific commit using git cherry-pick. Assuming you don't have any repository in local and you are pulling specific commit from remote,
1) create empty repository in local and git init
2) git remote add origin "url-of-repository"
3) git fetch origin [this will not move your files to your local workspace unless you merge]
4) git cherry-pick "Enter-long-commit-hash-that-you-need"
Done.This way, you will only have the files from that specific commit in your local.
You can get this using -> git log --pretty=oneline
Upvotes: 1
Reputation: 5349
I think 'git ls-remote' ( http://git-scm.com/docs/git-ls-remote ) should do what you want. Without force fetch or pull.
Upvotes: 1
Reputation: 739
I did a pull on my git repo:
git pull --rebase <repo> <branch>
Allowing git to pull in all the code for the branch and then I went to do a reset over to the commit that interested me.
git reset --hard <commit-hash>
Hope this helps.
Upvotes: 67