如何使用特定的修订版本/变更集克隆git存储库?

529

如何使用特定修订版本克隆Git仓库,类似于我通常在Mercurial中所做的:

hg clone -r 3 /path/to/repository

3
不需要针对变更集或修订版本,但在特定分支克隆最新版本同样有效,例如:git clone -b 10.1 https://github.com/MariaDB/server.git --depth=1 mariadb-server-src - MrMesees
可能是重复的问题:如何使用Git下载指定标签? - SlightlyCuban
你想让历史浅显吗?例如,只包含第三个版本,还是也包括它的父版本? - sschuberth
如果要从另一个存储库内部克隆该存储库,并且您想以特定的sha克隆该内部存储库,则git子模块可以以自动化方式完全实现此操作。 - J0hnG4lt
很抱歉,但整个讨论串都证明了git有多么过于复杂。仅仅为了执行一个简单的基本任务就会遇到如此多的困难和混乱?我永远不会理解git是如何变得如此流行的。 - aquawicket
1
截至2021年最佳答案是Peter Kovac的答案:https://dev59.com/dnA75IYBdhLWcg3wFEsI#51771769 一个单一的git clone命令,只下载那个提交。 - fonini
20个回答

971
$ git clone $URL
$ cd $PROJECT_NAME
$ git reset --hard $SHA1

回到最近的提交,请执行以下操作:

$ git pull

为了将撤销的提交保存在线上(远程),你必须强制推送到 origin:

git push origin -f

18
仅当提交在主分支中时,此操作才有效,否则会破坏本地引用。为什么一开始不使用git checkout而是要用git reset? - Joky
95
对于大型代码库来说,这不是一个好的选择,因为它会拉取所有内容。 - Incognito
39
如果我想要克隆一个特定版本,那么我可能不想要克隆整个仓库,只需要克隆那个特定的版本。对我来说,这就是所问的问题。但是这个解决方案回答了另一个问题:“如何在我的仓库中查看特定的版本? ”。获取整个仓库正是许多人想要避免的。 - Renato
4
在我看来,这并没有回答实际问题,因为在克隆时能够指定修订版本还允许我使用“--depth”,这对于大型仓库非常重要。这种解决方案需要拉取所有对象,然后将其重置为早期的修订版本。这非常耗费时间且浪费网络带宽。 - void.pointer
1
但这会克隆整个存储库,这是无用的。 - Antoniossss
显示剩余2条评论

316

更新2 自从Git 2.5.0版本以来,可以通过配置变量uploadpack.allowReachableSHA1InWant在服务器端启用下述功能,这里是GitHub的功能请求启用该功能的GitHub提交。请注意,一些Git服务器默认激活此选项,例如Bitbucket Server自版本5.5+开始启用。参见Stackexchange上的答案,了解如何激活配置选项的示例。

更新1 对于Git版本1.7 < v < 2.5,请使用git clone和git reset,如Vaibhav Bajpai的回答中所述。

如果您不想获取完整的存储库,则可能不应该使用clone。您始终可以使用fetch来选择要获取的分支。我不是hg专家,因此不知道-r的详细信息,但在git中,您可以执行以下操作。

# make a new blank repository in the current directory
git init

# add a remote
git remote add origin url://to/source/repository

# fetch a commit (or branch or tag) of interest
# Note: the full history up to this commit will be retrieved unless 
#       you limit it with '--depth=...' or '--shallow-since=...'
git fetch origin <sha1-of-commit-of-interest>

# reset this repository's master branch to the commit of interest
git reset --hard FETCH_HEAD

32
我认为 git fetch origin <sha1> 不起作用;似乎需要传递一个具有名称的引用,如标签或分支名称。参见 http://kerneltrap.org/mailarchive/git/2009/1/13/4707444 - artur
59
你觉得这个方法行不通,还是你已经尝试过但它没起作用? - CB Bailey
42
在使用 git 1.4 版本时,我发现在从远程仓库获取主分支并执行reset --hard来将分支实例化到本地后,可以通过 git fetch origin <SHA1> 切换到任何我想要的修订版本。但我无法直接获取单个修订版本。而在 git 1.7 版本中,如@artur 所述,git fetch origin <SHA1> 不起作用,需要使用git checkout <SHA1>后跟reset --hard - Joe McMahon
7
仅支持使用http和rsync协议的SHA-1检索。详情请参见http://kerneltrap.org/mailarchive/git/2009/1/14/4716044/thread#mid-4716044。 - CharlesB
23
这个答案已经过时了。这不适用于git 1.7或1.8,也不适用于https://或ssh协议。(“找不到远程参考df44398762393c67af487edeb0831ad9579df4aa”-它不是一个参考,而是一个提交。) - Paŭlo Ebermann
显示剩余20条评论

143

如果想在特定的分支或标签上仅克隆一个单独的特定提交,请使用以下命令:

git clone --depth=1 --branch NAME https://github.com/your/repo.git

很遗憾,“NAME”只能是分支名称或标签名称(不能是提交 SHA)。

省略--depth标志以下载整个历史记录,然后检出该分支或标签:

git clone --branch NAME https://github.com/your/repo.git

这适用于最近版本的Git(我使用的是版本2.18.0)。


1
但不适用于旧版本2.17.1。 - RzR
22
需要更多人点赞。这比其他过时的回答好得多。 - Étienne
1
@ Sz。是的,--branch选项用于分支和标签。 - Peter Kovac
2
你在哪里指定提交? - Nike
1
@ajskateboarder,无法在特定提交时克隆存储库吗? - Nike
显示剩余5条评论

59

克隆git存储库会完整地克隆整个存储库:没有一种方法可以选择仅克隆一个修订版本。但是,一旦执行了git clone,您可以通过执行checkout <rev>来检出特定的修订版本。


6
我不想仅复制一个版本。我只想指定克隆的限制。换句话说,我想将所有东西克隆到指定的版本。 - John
7
你无法这样做。git clone会获取整个存储库,一旦你拥有它,就可以检出特定的版本。 - user113292
6
请注意:Git 通常在存储历史记录方面非常高效,因此仅克隆一半的版本并不能节省大量空间。 - Amber
@BrainSlugs83:在克隆历史记录后,您可以使用重置/分支/检出来回到旧版本,并忘记更新的版本。 - Paŭlo Ebermann
1
“没有办法只选择一个版本进行克隆” - 是的,有:git clone --single-branch ... - morxa
显示剩余2条评论

38
你可以简单地使用 git checkout <commit hash>
按照以下顺序: bash git clone [URLTORepository] git checkout [commit hash] 提交哈希看起来像这样 "45ef55ac20ce2389c9180658fdba35f4a663d204"。

6
为什么要克隆后再检出呢?一旦你克隆,就在本地仓库中拥有了整个历史记录。为什么这个答案的赞数太多了? - Dmitry Perfilyev
对我有用,谢谢 ❤ - hexhad
这对我有用,非常清晰明了。你只需要在检出命令之前进入克隆的项目目录即可。 - Vikas saini
@DmitryPerfilyev,你似乎没有理解问题的关键:git checkout使修订内容成为当前内容。如果你只是克隆,你将处于默认(或指定)分支的顶部,这通常不是你想要的。 - Ruslan

31
如果你想获取从一开始到特定点的所有内容,Charles Bailey的回答是完美的。如果你想反向检索自当前日期起历史记录的子集,则可以使用git clone --depth [N],其中N是您想要的历史版本数。然而:

--depth

创建一个浅层克隆,其中历史记录被截断为指定数量的修订版本。浅层存储库有一些限制(您无法从中克隆或提取,也无法将其推送到其中,也无法从其中推送),但如果您只对具有悠久历史的大型项目的最近历史感兴趣,并且希望作为补丁发送修复,则足够。


4
Git的新版本改进了浅克隆功能,您可以使用它进行拉取和推送操作。 - orion78fr

26

简单总结一下(git 版本 1.7.2.1):

  1. 在想要的存储库位置执行普通的 git clone 命令(获取到目前为止所有的东西 - 我知道,这不是想要的内容,我们正在接近答案)
  2. git checkout <sha1 rev> 到你想要的版本
  3. git reset --hard
  4. git checkout -b master

11
第三步和第四步是做什么的? - BrainSlugs83
第四步对我没用,但前三步解决了问题 - 谢谢。 - Gene Bo
@BrainSlugs83:第4步创建一个名为master的本地分支,并切换到该分支。 - LarsH
3
为什么要运行 git reset --hard 呢?文档上说“重置索引和工作目录。丢弃自<commit>(默认为 HEAD,现在是 <sha1 rev>)以来工作目录中已跟踪文件的任何更改。”但此时我们自克隆以来还没有进行过任何更改,那么这个命令的目的是什么?它会截断当前分支至 <sha1 rev> 吗? - LarsH

18

TL;DR - 在源代码库中创建一个标签,指向你想要克隆的特定提交,然后在fetch命令中使用这个标签。你可以稍后从原始代码库中删除该标签以进行清理。

好吧,现在是2014年了,Charles Bailey在2010年的回答似乎已经过时了,大多数(全部?)其他答案都涉及克隆,而许多人希望避免这种情况。

以下解决方案实现了OP和许多其他人正在寻找的功能,即创建一个包含历史记录的存储库副本,但只包括到某个特定提交为止。

以下是我在git版本2.1.2下使用的命令,克隆一个本地存储库(即在另一个目录中的存储库),直到某个特定点:

# in the source repository, create a tag against the commit you want to check out
git tag -m "Temporary tag" tmptag <sha1>

# create a new directory and change into that directory
cd somewhere_else;mkdir newdir;cd newdir

# ...and create a new repository
git init

# add the source repository as a remote (this can be a URL or a directory)
git remote add origin /path/to/original/repo

# fetch the tag, which will include the entire repo and history up to that point
git fetch origin refs/tags/tmptag

# reset the head of the repository
git reset --hard FETCH_HEAD

# you can now change back to the original repository and remove the temporary tag
cd original_repo
git tag -d tmptag

希望这个解决方案能再运行几年!:-)


3
如果您是仓库的所有者,这是一个好主意,但不确定它是否适用于您不维护的公共仓库。 - Suhaib

14

无需下载整个历史记录,也不需要调用git init

git clone --depth=1 URL
git fetch --depth=1 origin SHA1
git checkout SHA1
git branch -D @{-1}  # if you want to tidy up the fetched branch

这种方法有一个缺点,与CB Bailey的回答类似,你仍然会下载1个不必要的版本。但从技术上讲,它是一个git clone(OP所需),并且不强制你下载某个分支的整个历史记录。


这对我没用。如果SHA1比--depth旧,或者在不同的分支上而不是默认的分支,则不会被获取,因此检出失败。 - WiringHarness
@WiringHarness 在使用 git 2.40.0 版本时这两种情况都可以正常工作。你能证明相反的情况吗? - Johannes
Gulp,我进行了更好的测试,它确实有效。我打赌我使用了短哈希,但那行不通,似乎我需要完整的SHA1哈希而没有截断,至少对于fetch命令来说是这样的。fatal: Couldn't find remote ref faf2a5a6。所以我点赞你的方法,它很棒。 - WiringHarness

5
我使用git clone --config选项来实现这个目标,这个方法是我从这个答案中学到的:https://dev59.com/WmIj5IYBdhLWcg3wGBfj#43759576 我的场景涉及Azure DevOps管道中的稀疏检出,在这种情况下,我需要使用提交哈希而不是分支名称来克隆库。clone命令不接受提交哈希作为参数。解决方法是设置一个包含refspec的配置变量(-c),因为该refspec可以使用提交哈希而不是分支名称:
git clone -c remote.origin.fetch=+<commit hash>:refs/remotes/origin/<commit hash> <repo_url> --no-checkout --progress --depth 1
git sparse-checkout init --cone
git sparse-checkout set <file list>
git checkout <commit hash>

很酷的方法。这似乎是git clone --branch ...的相反,它只接受SHA,而不适用于分支名称或标签。这样可以避免像我这样的人通过试错来找出问题,所以你知道这对什么有用。 - Tom Saleeba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接