Git克隆:只要文件,可以吗?

177
我想克隆一个GIT仓库,但不要得到.git目录。换句话说,我只想要文件。有方法可以做到这一点吗? git clone --no-checkout完全相反,它给了我.git目录。
我正在尝试为一个远程仓库做到这一点,而不是本地仓库,这意味着这不是“如何执行“git export”(如“svn export”)”的重复(即使解决方案可能最终相同)。

6
可能是重复的问题:如何执行“git export”(类似于“svn export”)? - Greg Hewgill
@Greg Hewgill 我正在尝试从远程存储库中完成这个操作。虽然我不确定这是否使得这个问题独特。 - Dan Rosenstark
1
虽然这是一个较新的问题,但我认为这值得一看:https://dev59.com/wmgu5IYBdhLWcg3wMkbF - eonil
1
看看我的更新答案git archive现在(2017年第四季度)更加精确,不会包括空文件夹。 - VonC
1
不客气。显然,您的评论已被标记并删除... https://chat.stackoverflow.com/transcript/134259?m=39402889#39402889 - VonC
8个回答

90
从您的需求来看,最接近的git命令应该是git archive。请参考备份使用git的项目:它将在一个归档文件中包含所有文件(如果您使用git-archive-all脚本,则包括子模块)。
然后您可以在任何地方使用该归档文件,只返回文件,没有.git目录。
git archive --remote=<repository URL> | tar -t

如果您只需要来自第一层级的文件夹和文件:
git archive --remote=<repository URL> | tar -t --exclude="*/*"

仅列出远程仓库的一级文件夹:

git archive --remote=<repository URL> | tar -t --exclude="*/*" | grep "/"

注意: 这个方法在GitHub上不适用(不支持) 因此,你需要克隆(浅克隆以加快克隆速度),然后在本地存档
git clone --depth=1 git@github.com:xxx/yyy.git
cd yyy
git archive --format=tar aTag -o aTag.tar

另一种选择是进行浅克隆(如下所述),但将.git文件夹放置在其他位置。
git --git-dir=/path/to/another/folder.git clone --depth=1 /url/to/repo

存储库文件夹仅包含文件,不包括.git
注意: git --git-dir命令git的选项,而非git clone
更新到Git 2.14.X/2.15(2017年第四季度):它将确保避免添加空文件夹。

"git archive",尤其是在使用pathspec时,在其输出中存储了一个空目录,即使Git本身从不这样做。
已经修复了此问题。

参见由René Scharfe (``)于2017年9月12日提交的提交4318094
建议者:Jeff King (peff)
(由Junio C Hamano -- gitster --于2017年9月25日合并为提交62b1cb7

archive:不将空目录添加到存档文件中

虽然Git不跟踪空目录,但git archive可以欺骗将其放入归档文件中。
虽然对象数据库支持此类操作,但无法在索引中表示,因此不太可能在实际使用中出现。

由于Git不支持空目录,因此也不应将其写入归档文件中。
如果确实需要空目录,则可以通过在其中放置一个空的.gitignore文件来跟踪和存档。


6
这对我很有效。我想再补充一点。如果你完全不关心.git文件夹,那么可以使用这个命令:git --git-dir=/dev/null clone --depth=1 /url/to/repo - HumanSky
2
这不会删除 .git 文件夹。 - aliasav
1
@andrew,“在git版本2.11中,clone命令没有--git-dir选项。只有--separate-git-dir选项。”:--git-dir不是git clone的选项,而是git的选项。 - VonC
1
运行 git archive --remote=https://github.com/pornel/dssim.git @ | tar -t 我得到了 tar: This does not look like a tar archive。它在 GitHub 上不起作用吗?另外,@ 是什么意思? - André Werlang
2
@AndréWerlang七年后...我不太确定@:我已将其删除。此外,GitHub不支持远程git archive:我已编辑答案以提供替代方案。 - VonC
显示剩余5条评论

67
git archive --format=tar --remote=<repository URL> HEAD | tar xf -

这里提供的内容取自这里


2
“--format=tar” 是不必要的。 “tar” 是默认输出,无需指定。 - VasiliNovikov
1
将数据导入 tar x 中即可。 - Jens Bannmann
1
在 GitHub 上,这会导致“致命错误:协议不支持该操作”。 - lurscher

50

您可以创建一个浅克隆来仅获取最近的几个修订版本:

 git clone --depth 1 git://url

然后,您可以简单地删除 .git 目录或使用git archive导出您的树。


当我通过重复克隆来更新时,如何防止出现以下错误:" fatal: 目标路径 'XYZ' 已经存在且不是一个空目录。" - Sohail Si
1
@SohailSi:要么克隆到新位置,要么删除旧目录。如果您想更新本地副本,最好只获取新版本。 - knittl
@Sohail 第一次使用 clone 获取文件后,每次更新都需要使用 pull - MMJ

8
为什么不执行克隆,然后删除.git目录,这样你就只有一个裸的工作副本呢?
编辑:实际上为什么要使用克隆?当你说你想要一个没有.git目录的git仓库时,这有点令人困惑。如果你是指你只想要树的某个状态的副本,那么为什么不在shell中使用cp -R而不是git克隆,然后再删除.git呢?

@Amoss 发布一个tarball,这样人们就不会不断地获取最新的代码了吗? - alternative
3
@mathepic:发布tarball需要一种从存储库中获取干净的工作副本的方法,这正是提出的问题。 - Andrew
@Amoss,听起来他想让用户使用git下载它,然后删除.git存储库。无论如何,这都是错误的做法。例如,我不会将./configure检入源代码控制,因为它是由configure.ac自动生成的,但您希望使用./configure进行分发。 - alternative
2
.git文件夹非常大。我不需要花费额外的磁盘空间来复制.git。我只需要文件。我相信有一种方法可以做到这一点... - trusktr
1
阅读下面的答案并进行一些谷歌搜索后,引发了另一个问题:https://dev59.com/03VC5IYBdhLWcg3w21Iq 如果您查看git archive --remote的用法,则正好可以实现您(和原始发布者)正在寻找的功能。编辑:这就是Jon在下面回答的内容。 - Andrew
显示剩余2条评论

6

git checkout -f

还有一种方法可以通过将仓库与工作树分离来完成。

如果您需要定期更新这些没有Git的Git文件,则此方法非常有用。例如,当我需要检出源文件并构建一个工件时,然后将工件复制到另一个仓库中以便部署到服务器时,我使用它,当我想将源代码推送到服务器时,我也会使用它,这样源代码就会检出并构建到www目录中。

我们将创建两个文件夹,一个用于Git,另一个用于工作文件:

mkdir workingfiles
mkdir barerepo.git

初始化一个空的Git仓库:

cd barerepo.git
git --bare init 

然后创建一个 post-receive 钩子:
touch hooks/post-receive
chmod ug+x hooks/post-receive

打开你喜欢的编辑器并编辑 post-receive 文件:

GIT_WORK_TREE=/path/to/workingfiles git checkout -f
# optional stuff:
cd down/to/some/directory
[do some stuff]

将此添加为远程:

git remote add myserver ssh://user@host:/path/to/barerepo.git

现在每次你推送到这个裸库,它都会将工作树切换到 /workingfiles/。但是 /workingfiles/ 本身不受版本控制;在 /workingfiles/ 中运行 git status 将出现错误提示:fatal: Not a git repository (or any parent up to mount point /data)。它只是普通的文件。
与其他解决方案不同,无需使用 rm -r .git 命令,因此如果 /workingfiles/ 是其他Git存储库,您不必担心所使用的命令会删除其他存储库的Git文件。

1
同意。在裸仓库上使用post-receive hook是可行的。+1 - VonC
如果你只是想获取这些文件,那么这会是很多工作。 - Rainb
2
如果您只是想定期获取文件,那么这并不需要太多的工作。 - Slam

4
无需使用git,只需将“/zipball/master/”添加到URL末尾(来源)。

下载

这个解决方案最接近github页面上的“Download ZIP”按钮。一个优点是没有.git目录。另一个优点是它可以下载单个ZIP文件,而不是逐个下载每个文件,这可能会带来巨大的差异。可以通过wget命令行完成:wget -O "$(basename $REPO_URL)".zip "$REPO_URL"/zipball/master/。唯一的问题是,有些存储库可能根本没有主分支。如果是这种情况,“URL中的master应该替换为适当的分支。

解压缩

一旦ZIP文件下载完成,最终未压缩的目录名称可能仍然非常奇怪和意外。要修复这个问题,可以通过此脚本提取名称,并将其移动到URL的basename中。最终的script.sh可能如下所示(用evals处理空格):

#Script for downloading from github. If no BRANCH_NAME is given, default is "master".
#usage: script.sh URL [BRANCH_NAME]
__repo_name__='basename "$1"'
__repo_name__="$(eval $__repo_name__)"
__branch__="${2:-master}"
#downloading
if [ ! -e ./"$__repo_name__"".zip" ] ; then
wget -O "$__repo_name__"".zip" "$1""/zipball/$__branch__/"
fi
#unpacking and renaming
if [ ! -e ./"$__repo_name__" ] ; then
unzip "$__repo_name__"".zip" && 
__dir_name__="$(unzip -qql $__repo_name__.zip | sed -r '1 {s/([ ]+[^ ]+){3}\s+//;q}')" &&
rm "$__repo_name__"".zip" &&
mv "$__dir_name__" "$__repo_name__"
fi

维护

这种方法适用于"仅文件"的情况,对于小型代码库的快速临时访问非常有效。

然而,如果源码库相当大,更新的唯一可能性是下载并重建所有内容,那么(据我所知)就无法更新缺少.git目录的内容,所以必须重新下载整个代码库。在这种情况下,最好的解决方案是使用浅层克隆 git clone --depth 1 $REPO_URL,如 VonC 已经解释的那样。但接下来怎么做呢?可以参考此链接进行"检查更新",并参考这个很棒的类似维基百科的答案进行更新。


很棒的答案!关于Maintaining部分,不需要重新下载整个仓库。第一次下载时使用clone --depth 1,然后每次需要更新时都使用pull,对吗? - MMJ

2
git --work-tree=/tmp/files_without_dot_git clone --depth=1 \
  https://git.yourgit.your.com/myawesomerepo.git \
  /tmp/deleteme_contents_of_dot_git

/tmp目录中的两个目录都是即时创建的,无需预先创建。


1
你能描述一下这个命令是如何工作的吗? - Roger Lindsjö
它将克隆存储库的内容到文件夹/tmp/files_without_dot_git,除了.git文件夹和.git文件夹内容将进入文件夹/tmp/deleteme_contents_of_dot_git,您稍后可以删除它。因此,您可以在/tmp/files_without_dot_git文件夹中执行git init以创建全新的存储库。 - Manoj

0

听起来你只是想要源代码的副本。如果是这样,为什么不直接复制目录并从复制中排除.git目录?


11
这就是我的问题:如何使用Git来做到这一点...从我得到的答案中,我已经可以确定没有内置的方法来实现它。无论如何,还是谢谢! - Dan Rosenstark

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