ChatGPT解决这个技术问题 Extra ChatGPT

做一个“git export”(比如“svn export”)?

我一直想知道是否有一个好的“git 导出”解决方案可以创建一个没有 .git 存储库目录的树的副本。我知道的方法至少有以下三种:

git clone 然后删除 .git 存储库目录。 git checkout-index 暗示了此功能,但以“只需将所需的树读入索引...”开头,我不完全确定该怎么做。 git-export 是一个第三方脚本,它本质上是将 git 克隆到一个临时位置,然后 rsync --exclude='.git' 到最终目的地。

这些解决方案中没有一个真正让我感到满意。最接近 svn export 的可能是选项 1,因为两者都要求目标目录首先为空。但是选项 2 似乎更好,假设我可以弄清楚将树读入索引意味着什么。

@rnrTom:见索莫夫的回答。 (在 tar 存档中没有“压缩”的内容)。
@mrTom git archive --format zip --output "output.zip" master -0 会给你一个未压缩的存档(-0 是未压缩的标志)。 git-scm.com/docs/git-archive
我同意@mrTom,我不认为存档是压缩还是未压缩是主要问题。使用 SVN,我可以直接从远程存储库export 一个 250 kB 的子目录(否则它的大小可能是 200 MB,不包括修订版) - 我只会访问网络进行 250 kB(左右)的下载传输。使用 git,必须在服务器上启用 archive(所以我不能尝试) - 服务器上的 clone --depth 1 仍可能检索到 25 MB 的存储库,其中仅 .git 子文件夹就需要 15 MB。因此,我仍然会说答案是“不”。
这是一个不错且简单的方法:git archive -o latest.zip HEAD
多年来,我一直将这个问题用作“git export”的手册页,仅供参考。

J
Jean-François Corbett

实现此目的的最简单方法可能是使用 git archive。如果你真的只需要扩展的树,你可以做这样的事情。

git archive master | tar -x -C /somewhere/else

大多数时候,我需要从 git 中“导出”某些东西,无论如何我都想要一个压缩存档,所以我做这样的事情。

git archive master | bzip2 >source-tree.tar.bz2

压缩包:

git archive --format zip --output /full/path/to/zipfile.zip master 

git help archive 有关更多详细信息,它非常灵活。

请注意,即使存档不包含 .git 目录,它也会包含其他隐藏的特定于 git 的文件,如 .gitignore、.gitattributes 等。如果您不希望它们在存档中,请确保您在 .gitattributes 文件中使用 export-ignore 属性并在归档之前提交它。 Read more...

注意:如果你有兴趣导出索引,命令是

git checkout-index -a -f --prefix=/destination/path/

(有关详细信息,请参阅 Greg's answer


ZIP 存档:git archive --format zip --output /full/path master
请注意,存档将不包含 .git 目录,但将包含其他隐藏的 git 特定文件,如 .gitignore、.gitattributes 等。因此,如果您不想要它们,请确保使用 export-ignore 属性一个 .gitattributes 文件并在归档之前提交它。请参阅feeding.cloud.geek.nz/2010/02/…
要跟进 Streams 的注释:您可以在命令中添加一个 '--prefix=something/' 字符串来控制将打包在 zip 中的目录名称。例如,如果您使用 git archive --format zip --output /path/to/file.zip --prefix=newdir/ master,输出将被称为“file.zip”,但当您解压缩它时,顶级目录将是“newdir”。 (如果省略 --prefix 属性,顶级目录将是“文件”。)
最简单的方法:git archive -o latest.zip HEAD 它创建一个 Zip 存档,其中包含当前分支上最新提交的内容。请注意,输出格式由输出文件的扩展名推断。
它不支持 git 子模块 :(
e
etarion

我发现了选项 2 的含义。从存储库中,您可以执行以下操作:

git checkout-index -a -f --prefix=/destination/path/

路径末尾的斜杠很重要,否则将导致文件位于 /destination 中,前缀为“路径”。

由于在正常情况下索引包含存储库的内容,因此“将所需的树读入索引”没有什么特别的事情要做。它已经在那里了。

-a 标志是检查索引中所有文件所必需的(我不确定在这种情况下省略这个标志意味着什么,因为它不符合我的要求)。 -f 标志强制覆盖输出中的任何现有文件,此命令通常不会这样做。

这似乎是我正在寻找的那种“git export”。


...并且不要忘记最后的斜线,否则您将无法获得预期的效果;)
git add 命令更改索引中的内容,因此无论 git status 显示为“待提交”的内容都是 HEAD 和索引内容之间的差异
@conny:阅读您的评论,忘记了它并在没有尾部斜杠的情况下运行命令。提示:听从康尼的建议 -.-
+1 康尼的建议。另外,不要尝试创建“~/dest/”,因为这会在您的工作目录中创建一个名为“~”的目录,而不是您真正想要的。猜猜当你盲目地输入 rm -rf 会发生什么~
@KyleHeironimus - 如果您在前缀路径周围使用引号告诉shell不要执行波浪号扩展,那么您关于使用'~/dest/`的警告是正确的。一个名为 ~ 的目录(不是 '~' !)将在您的工作目录中创建。在这方面,git checkout-index 没有什么特别之处:mkdir '~/dest' 也是如此(不要那样做!)。避免需要引用的文件名的另一个很好的理由(例如,其中有空格):-)
A
Alexander Somov

git archive 也适用于远程存储库。

git archive --format=tar \
--remote=ssh://remote_server/remote_repository master | tar -xf -

要在 repo 中导出特定路径,请添加任意数量的路径作为 git 的最后一个参数,例如:

git archive --format=tar \
--remote=ssh://remote_server/remote_repository master path1/ path2/ | tar -xv

这是我最喜欢的选项。它还有一个额外的好处,那就是它也适用于裸存储库。
改进的版本是:git archive --format=tar --prefix=PROJECT_NAME/ --remote=USER@SERVER:PROJECT_NAME.git master | tar -xf -(确保您的存档位于文件夹中)
注意:服务器必须启用此功能。
我试过了:git archive --format=zip --output foo.zip --remote=https://github.com/xxx.git master 并得到了致命的:协议不支持操作。命令流意外结束。
@andyf GitHub 有自己的方式:curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | tar xzf -docs
A
Anthony Hatzopoulos

https://i.stack.imgur.com/p73W2.png

如果存储库托管在 GitHub 上,则为特殊情况的答案。

只需使用 svn export

据我所知 Github 不允许archive --remote。尽管 GitHub 是 svn compatible 并且他们确实可以访问所有 git repos svn,因此您可以像往常一样使用 svn export,只需对您的 GitHub url 进行一些调整。

例如,要导出整个存储库,请注意 URL 中的 trunk 如何替换 master(或任何 project's HEAD branch is set to):

svn export https://github.com/username/repo-name/trunk/

您可以导出单个文件甚至某个路径或文件夹:

svn export https://github.com/username/repo-name/trunk/src/lib/folder

使用 jQuery JavaScript 库的示例

HEAD 分支或 master 分支将可使用 trunk

svn ls https://github.com/jquery/jquery/trunk

HEAD 分支 可在 /branches/ 下访问:

svn ls https://github.com/jquery/jquery/branches/2.1-stable

/tags/ 下的所有 标签 以相同的方式:

svn ls https://github.com/jquery/jquery/tags/2.1.3

git archive 在 GitHub 上运行良好,只要您使用 git 协议,只需将 URL 中的 https:// 替换为 git:// 即可。我不知道为什么 GitHub 不宣传这个隐藏功能。
@NeilMayhew 这对我不起作用,我得到 fatal: The remote end hung up unexpectedly。使用 jQuery github repo 在两个不同的服务器上进行了尝试。
你是对的。我忘记了我使用 git config url.<base>.insteadOf 来缓存远程存储库。因此,我实际上使用的是 file:// URL。我怀疑 git archive 是否可以与 git:// URL 一起使用,因为它需要能够在远程端运行 git-upload-archive。应该可以使用 ssh 协议,但 github 不允许它 (Invalid command: 'git-upload-archive')。
如果我想在内部托管的 git 存储库上进行操作,有什么方法可以使用表现得像 github 的本地服务器工具?
赞成——Git 没有这个功能真是太奇怪了,我们不得不求助于 svn
j
jperras

Git Manual

使用 git-checkout-index “导出整棵树”

前缀功能基本上使得使用 git-checkout-index 作为“导出为树”功能变得微不足道。只需将所需的树读入索引,然后执行以下操作:

$ git checkout-index --prefix=git-export-dir/ -a


我认为混淆是“将所需的树读入索引”这句话。
如果你想在分支栏中导出目录 foo,那么这将是 git read-tree bar:foo 然后 git checkout-index --prefix=export_dir/ -a 之后也许你应该做 git update-index master
@JohnWeldon 是否需要您先克隆存储库?如果是这样,那么我不会接受它,因为子目录的“svn export”的全部意义在于直接获取该子目录的副本;如果有人有一个 1GB 的 Git 存储库,而我想要的只是一个 10kB 的子目录,那么要求我克隆整个东西是很疯狂的。
此外,我会回显@davetron5000,并附上评论“将所需的树读入索引”,我不知道这意味着什么。
D
Daniel Schierbeck

我围绕 git-checkout-index 编写了一个简单的包装器,您可以像这样使用它:

git export ~/the/destination/dir

如果目标目录已存在,则需要添加 -f--force

安装简单;只需将脚本放在 PATH 中的某个位置,并确保它是可执行的。

The github repository for git-export


这个包装器不是平台无关的;它依赖于/bin/sh。因此,如果您使用的是 Windows,则此解决方案可能不适合您。
呃,这个脚本有 57 行文档、空白、设置、参数解析,只有一行实际做了一些事情......
k
kostmo

与 SVN 相比,这似乎对 Git 来说不是问题。 Git 只在存储库根目录中放置一个 .git 文件夹,而 SVN 在每个子目录中放置一个 .svn 文件夹。所以“svn export”避免了递归命令行魔法,而使用 Git 递归是不必要的。


从 SVN 1.7 开始,也只有一个 .svn 文件夹:subversion.apache.org/docs/release-notes/1.7.html#single-db
这不会摆脱 svn export 删除的任何其他构建文件。所以这绝对不是答案。
a
aredridel

相当于

svn export . otherpath

在现有的回购中是

git archive branchname | (cd otherpath; tar x)

相当于

svn export url otherpath

git archive --remote=url branchname | (cd otherpath; tar x)

谢谢,这就是我所缺少的...另外,要检查导出的时间戳(它们不会像在文件上那样保留),请使用 git archive --format=tar --prefix=junk/ HEAD | (tar -t -v --full-time -f -) ...但是,使用时间戳归档并不是一件容易的事,所以我发布了 example below
您可以对 tar 使用 C 选项而不是子 shell,如下所示:git archive branchname | tar xC otherpath
注意 tar 的 C 选项仅是 GNU Tar。
4
4 revs, 2 users 75%

如果您没有使用 .gitattributes export-ignore 排除文件,请尝试 git checkout

mkdir /path/to/checkout/
git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout -f -q

-f 从索引中检出路径时,不要在未合并的条目上失败;相反,未合并的条目将被忽略。

-q 避免冗长

此外,您可以获取任何分支或标签或从特定的提交修订版,如在 SVN 中只需添加 SHA1(Git 中的 SHA1 等同于 SVN 中的修订号)

mkdir /path/to/checkout/
git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout 2ef2e1f2de5f3d4f5e87df7d8 -f -q -- ./

/path/to/checkout/ 必须为空,Git 不会删除任何文件,但会覆盖同名文件而不会发出任何警告

更新:为避免斩首问题或在使用带有标签、分支或 SHA1 的签出导出时保持工作存储库完整,您需要在末尾添加 -- ./

双破折号 -- 告诉 git,破折号之后的所有内容都是路径或文件,在这种情况下,也告诉 git checkout 不要更改 HEAD

例子:

此命令将仅获取 libs 目录以及来自该确切提交的 readme.txt 文件

git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout fef2e1f2de5f3d4f5e87df7d8 -f -q -- ./libs ./docs/readme.txt

这将创建(覆盖)my_file_2_behind_HEAD.txt 头部后面的两个提交 HEAD^2

git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout HEAD^2 -f -q -- ./my_file_2_behind_HEAD.txt

获得另一个分支的出口

git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout myotherbranch -f -q -- ./

请注意,./ 相对于存储库的根目录


实际上,在众多其他和赞成票中,这对我来说效果最好,没有任何压缩,与裸存储库(gitolite)一起工作得很好。
请注意,SHA1 签出将在存储库中创建“斩首”问题
实际上@ITGabs,这不会下载“.git”文件夹。所以下载的文件夹不是git仓库,所以技术上不算“斩首”
@FabioMarreco 斩首问题出在存储库上,不在导出/下载的文件中,我正在更新答案以获取更多详细信息
这对我很有用。但起初我收到“不是 git 存储库”错误消息。然后我发现“/path/to/repo/”必须指向.git文件夹。所以这有效:--git-dir=/path/to/repo/.git
s
slatvick

我广泛使用 git-submodules。这个对我有用:

rsync -a ./FROM/ ./TO --exclude='.*'

这不会遗漏名称以点开头的文件,例如 .htaccess
一个好的解决方案,我会将 --exclude='.*' 更改为 --exclude='.git*'
--exclude-vcs 如果你打算采取这种机智
./FROM/ 可以是远程仓库吗?
作为仅供参考,我的 rsync 副本将参数列为 --cvs-exclude。此外,它仍会复制 .gitattributes.gitignore
L
Lars Schillingmann

在寻找导出 git 存储库的方法时,我经常点击此页面。我对这个问题的回答考虑了 svn export 与 git 相比在设计上具有的三个属性,因为 svn 遵循集中存储库方法:

它通过不导出所有修订来最小化到远程存储库位置的流量

它不包含导出目录中的元信息

使用 svn 导出某个分支是通过指定适当的路径来完成的 git clone --depth 1 --branch master git://git.somewhere destination_path rm -rf destination_path/.git

在构建某个版本时,克隆一个稳定的分支(例如 --branch stable--branch release/0.9)很有用。


如果目标存在且非空,则此方法不起作用。
唯一正确答案: 它来自深处。 git archive | tar 方法不适用于与 POSIX 不兼容的 shell 环境(例如,AppVeyor 的基于 CMD 或 PowerShell 的 CI) ,这是不理想的。 git checkout 方法修改了主工作树的索引,这很糟糕。 git checkout-index 方法需要预先修改主工作树的索引,这更糟糕。传统的 git clone 方法会在删除该历史记录之前克隆整个存储库的历史记录,这是一种浪费。这是剩下的唯一合理的解决方案。
要在本地导出,请注意要从中克隆的 Git 工作树的绝对路径应以 file:// 协议为前缀(例如,git clone --depth 1 --branch v3.14.15 file:///home/me/src_repo trg_repo)。否则将发出 "warning: --depth is ignored in local clones; use file:// instead." 并执行标准而不是浅层克隆,从而破坏了此答案的全部目的。 祝你好运!
H
Harmon

这将复制所有内容,减去 .dot 文件。我使用它来将 git 克隆的项目导出到我的 web 应用程序的 git repo 中,而不需要 .git 的东西。

cp -R ./path-to-git-repo /path/to/destination/

普通的旧 bash 效果很好:)


为什么不直接推送到远程?甚至比 bash 更简单。
作为 Web 应用程序的一部分并且其名称以点开头的文件呢? :) 想想 .htaccess
有时您还想忽略 .gitignore 中的内容,这不会。
t
teleme.io

就像克隆一样简单,然后删除 .git 文件夹:

git clone url_of_your_repo path_to_export && rm -rf path_to_export/.git


老实说——这个答案,也是问题中的第一名——是你 99% 的时间要做的事情。这些答案中的大多数都过于复杂了。
-1。首先,问题中已经提到这种方式不满足问题作者。其次,以这种方式将下载整个历史,这可能比有用的部分大得多。请至少包括 --depth 1。第三,即使这个答案得到了改进,它也不会对 the Lars Schillingmann's answer 增加任何内容。
b
bishop

对于 GitHub 用户,git archive --remote 方法不会像 the export URL is ephemeral 那样直接工作。您必须向 GitHub 询问 URL,然后下载该 URL。 curl 让这变得简单:

curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | tar xzf -

这将为您提供本地目录中的导出代码。例子:

$ curl -L https://api.github.com/repos/jpic/bashworks/tarball | tar xzf -
$ ls jpic-bashworks-34f4441/
break  conf  docs  hack  LICENSE  mlog  module  mpd  mtests  os  README.rst  remote  todo  vcs  vps  wepcrack

编辑如果您希望将代码放入特定的现有目录(而不是来自 github 的随机目录):

curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | \
tar xzC /path/you/want --strip 1

C
Community

是的,this 是一个干净整洁的命令,用于存档您的代码,存档中不包含任何 git,并且可以很好地传递而无需担心任何 git 提交历史记录。

git archive --format zip --output /full/path/to/zipfile.zip master 

这很棒,只需要删除 gitignore 即可完成并准备分享。
已接受的答案评论中提到了删除 .gitgnore 等:使用 .gitattributes 文件,请参阅 feeding.cloud.geek.nz/posts/excluding-files-from-git-archive
d
dkinzer

我只想指出,如果你是

导出存储库的子文件夹(这就是我过去使用 SVN 导出功能的方式)可以将所有内容从该文件夹复制到部署目标,因为您已经拥有整个存储库的副本。

然后您可以只使用 cp foo [destination] 而不是提到的 git-archive master foo | -x -C [destination]


o
orkoden

您可以在任何提交时将远程存储库归档为 zip 文件。

git archive --format=zip --output=archive.zip --remote=USERNAME@HOSTNAME:PROJECTNAME.git HASHOFGITCOMMIT

R
Rob Jensen

如果您想要与子模块一起使用的东西,这可能值得一试。

笔记:

MASTER_DIR = 结帐,同时签出您的子模块

DEST_DIR = 此导出将结束的位置

如果你有 rsync,我认为你可以做同样的事情,而且球痛更少。

假设:

您需要从 MASTER_DIR 的父目录运行它(即从 MASTER_DIR cd ..)

假定已创建 DEST_DIR。如果您愿意,这很容易修改以包括创建 DEST_DIR

cd MASTER_DIR && tar -zcvf ../DEST_DIR/export.tar.gz --exclude='.git*' 。 && cd ../DEST_DIR/ && tar xvfz export.tar.gz && rm export.tar.gz


s
skiphoppy

我的偏好实际上是在您的 Makefile(或其他构建系统)中有一个 dist 目标,该目标导出您的代码的可分发存档(.tar.bz2、.zip、.jar 或任何合适的)。如果您碰巧使用 GNU 自动工具或 Perl 的 MakeMaker 系统,我认为这会自动为您提供。如果没有,我强烈建议添加它。

ETA (2012-09-06):哇,严厉的反对票。我仍然认为使用构建工具而不是源代码控制工具来构建发行版会更好。我相信使用构建工具构建工件。在我目前的工作中,我们的主要产品是使用 ant 目标构建的。我们正在切换源代码控制系统,而这个 ant 目标的存在意味着迁移的麻烦减少了。


我想到的项目不是代码项目。它恰好更像是一个网站项目。
不解决问题。
是的,这样的答案可能不适合每个人的需求,但反对票很奇怪。这是一个完全有效的答案,实际上,在许多情况下,它是唯一正确的答案。非常有道理的一点是,将此问题视为“vc 工具问题”通常会完全走上错误的道路。
O
Ondra Žižka

据我了解这个问题,它更多的是从服务器下载某些状态,没有历史记录,也没有其他分支的数据,而不是从本地存储库中提取状态(就像这里的许多回答者所做的那样)。

可以这样做:

git clone -b someBranch --depth 1 --single-branch git://somewhere.com/repo.git \
&& rm -rf repo/.git/

--single-branch 从 Git 1.7.10(2012 年 4 月)开始可用。

--depth 是(曾经?)据报道有问题,但对于出口的情况,上述问题应该无关紧要。


注意:我刚刚注意到有 2 页 anwsers,我在发布前只看了一个。有一个类似的只有 --depth 的 anwser,这意味着 --single-branch 除非给出 --no-single-branch,这意味着这可能具有相同的效果。不过不确定,有专家可能会证实?
1
13 revs

git-export 的 Bash 实现。

我已经根据自己的功能对 .empty 文件的创建和删除过程进行了分段,目的是在“git-archive”实现中重新使用它们(稍后将发布)。

我还在进程中添加了“.gitattributes”文件,以便从目标导出文件夹中删除不需要的文件。在使“git-export”功能更高效的同时,包括对流程的详细说明。

EMPTY_FILE=".empty";

function create_empty () {
## Processing path (target-dir):
    TRG_PATH="${1}";
## Component(s):
    EXCLUDE_DIR=".git";
echo -en "\nAdding '${EMPTY_FILE}' files to empty folder(s): ...";
    find ${TRG_PATH} -not -path "*/${EXCLUDE_DIR}/*" -type d -empty -exec touch {}/${EMPTY_FILE} \;
#echo "done.";
## Purging SRC/TRG_DIRs variable(s):
    unset TRG_PATH EMPTY_FILE EXCLUDE_DIR;
    return 0;
  }

declare -a GIT_EXCLUDE;
function load_exclude () {
    SRC_PATH="${1}";
    ITEMS=0; while read LINE; do
#      echo -e "Line [${ITEMS}]: '${LINE%%\ *}'";
      GIT_EXCLUDE[((ITEMS++))]=${LINE%%\ *};
    done < ${SRC_PATH}/.gitattributes;
    GIT_EXCLUDE[${ITEMS}]="${EMPTY_FILE}";
## Purging variable(s):
    unset SRC_PATH ITEMS;
    return 0;
  }

function purge_empty () {
## Processing path (Source/Target-dir):
    SRC_PATH="${1}";
    TRG_PATH="${2}";
echo -e "\nPurging Git-Specific component(s): ... ";
    find ${SRC_PATH} -type f -name ${EMPTY_FILE} -exec /bin/rm '{}' \;
    for xRULE in ${GIT_EXCLUDE[@]}; do
echo -en "    '${TRG_PATH}/{${xRULE}}' files ... ";
      find ${TRG_PATH} -type f -name "${xRULE}" -exec /bin/rm -rf '{}' \;
echo "done.'";
    done;
echo -e "done.\n"
## Purging SRC/TRG_PATHs variable(s):
    unset SRC_PATH; unset TRG_PATH;
    return 0;
  }

function git-export () {
    TRG_DIR="${1}"; SRC_DIR="${2}";
    if [ -z "${SRC_DIR}" ]; then SRC_DIR="${PWD}"; fi
    load_exclude "${SRC_DIR}";
## Dynamically added '.empty' files to the Git-Structure:
    create_empty "${SRC_DIR}";
    GIT_COMMIT="Including '${EMPTY_FILE}' files into Git-Index container."; #echo -e "\n${GIT_COMMIT}";
    git add .; git commit --quiet --all --verbose --message "${GIT_COMMIT}";
    if [ "${?}" -eq 0 ]; then echo " done."; fi
    /bin/rm -rf ${TRG_DIR} && mkdir -p "${TRG_DIR}";
echo -en "\nChecking-Out Index component(s): ... ";
    git checkout-index --prefix=${TRG_DIR}/ -q -f -a
## Reset: --mixed = reset HEAD and index:
    if [ "${?}" -eq 0 ]; then
echo "done."; echo -en "Resetting HEAD and Index: ... ";
        git reset --soft HEAD^;
        if [ "${?}" -eq 0 ]; then
echo "done.";
## Purging Git-specific components and '.empty' files from Target-Dir:
            purge_empty "${SRC_DIR}" "${TRG_DIR}"
          else echo "failed.";
        fi
## Archiving exported-content:
echo -en "Archiving Checked-Out component(s): ... ";
        if [ -f "${TRG_DIR}.tgz" ]; then /bin/rm ${TRG_DIR}.tgz; fi
        cd ${TRG_DIR} && tar -czf ${TRG_DIR}.tgz ./; cd ${SRC_DIR}
echo "done.";
## Listing *.tgz file attributes:
## Warning: Un-TAR this file to a specific directory:
        ls -al ${TRG_DIR}.tgz
      else echo "failed.";
    fi
## Purgin all references to Un-Staged File(s):
   git reset HEAD;
## Purging SRC/TRG_DIRs variable(s):
    unset SRC_DIR; unset TRG_DIR;
    echo "";
    return 0;
  }

输出:$ git-export /tmp/rel-1.0.0 将“.empty”文件添加到空文件夹:...完成。签出索引组件:...完成。重置 HEAD 和索引:...完成。清除特定于 Git 的组件:...'/tmp/rel-1.0.0/{.buildpath}' 文件...完成。' '/tmp/rel-1.0.0/{.project}' 文件...完成。' '/tmp/rel-1.0.0/{.gitignore}' 文件...完成。' '/tmp/rel-1.0.0/{.git}' 文件...完成。' '/tmp/rel-1.0.0/{.gitattributes}' 文件...完成。' '/tmp/rel-1.0.0/{*.mno}' 文件...完成。' '/tmp/rel-1.0.0/{*~}' 文件...完成。' '/tmp/rel-1.0.0/{.*~}' 文件...完成。' '/tmp/rel-1.0.0/{*.swp}' 文件...完成。' '/tmp/rel-1.0.0/{*.swo}' 文件...完成。' '/tmp/rel-1.0.0/{.DS_Store}' 文件...完成。' '/tmp/rel-1.0.0/{.settings}' 文件...完成。' '/tmp/rel-1.0.0/{.empty}' 文件...完成。'完毕。归档签出组件:...完成。 -rw-r--r-- 1 管理轮 25445901 11 月 3 日 12:57 /tmp/rel-1.0.0.tgz 我现在已将“git 存档”功能合并到一个使用“create_empty”功能的进程中和其他功能。

function git-archive () {
    PREFIX="${1}"; ## sudo mkdir -p ${PREFIX}
    REPO_PATH="`echo "${2}"|awk -F: '{print $1}'`";
    RELEASE="`echo "${2}"|awk -F: '{print $2}'`";
    USER_PATH="${PWD}";
echo "$PREFIX $REPO_PATH $RELEASE $USER_PATH";
## Dynamically added '.empty' files to the Git-Structure:
    cd "${REPO_PATH}"; populate_empty .; echo -en "\n";
#    git archive --prefix=git-1.4.0/ -o git-1.4.0.tar.gz v1.4.0
# e.g.: git-archive /var/www/htdocs /repos/domain.name/website:rel-1.0.0 --explode
    OUTPUT_FILE="${USER_PATH}/${RELEASE}.tar.gz";
    git archive --verbose --prefix=${PREFIX}/ -o ${OUTPUT_FILE} ${RELEASE}
    cd "${USER_PATH}";
    if [[ "${3}" =~ [--explode] ]]; then
      if [ -d "./${RELEASE}" ]; then /bin/rm -rf "./${RELEASE}"; fi
      mkdir -p ./${RELEASE}; tar -xzf "${OUTPUT_FILE}" -C ./${RELEASE}
    fi
## Purging SRC/TRG_DIRs variable(s):
    unset PREFIX REPO_PATH RELEASE USER_PATH OUTPUT_FILE;
    return 0;
  }

用法:git-archive [/var/www/htdocs] /repos/web.domain/website:rel-1.0.0
C
Community

这会将一系列提交(C 到 G)中的文件复制到 tar 文件中。注意:这只会提交文件。不是整个存储库。对 Here 稍作修改

示例提交历史

--> B --> C --> D --> E --> F --> G --> H --> I

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT C~..G | xargs tar -rf myTarFile.tar

git-diff-tree Manual Page

-r --> 递归到子树

--no-commit-id --> git diff-tree 在适用时输出带有提交 ID 的行。此标志抑制了提交 ID 输出。

--name-only --> 仅显示更改文件的名称。

--diff-filter=ACMRT -->仅选择这些文件。 See here for full list of files

C..G --> 此提交范围内的文件

C~ --> 包括来自 Commit C 的文件。不仅仅是 Commit C 之后的文件。

| xargs tar -rf myTarFile --> 输出到 tar


D
DomTomCat

在添加前缀(例如目录名称)的同时将 git 导出到 zip 存档:

git archive master --prefix=directoryWithinZip/  --format=zip -o out.zip

C
Community

到目前为止,我见过的最简单的方法(也适用于 Windows)是 git bundle

git bundle create /some/bundle/path.bundle --all

有关详细信息,请参阅此答案:How can I copy my git repository from my windows machine to a linux machine via usb drive?


git bundle 包括 .git 文件夹,这是 OP 不想要的; git archive 似乎是更合适的方式
--all 开关的文档在哪里?
@GarretWilson 这很奇怪。 --all 这里不是 git bundle create 的选项,即使它看起来确实如此。 --all 是作为 git-rev-list 传递的合法值,请参阅 git-scm.com/docs/git-rev-list
t
troelskn

我需要将其用于部署脚本,但我无法使用上述任何方法。相反,我想出了一个不同的解决方案:

#!/bin/sh
[ $# -eq 2 ] || echo "USAGE $0 REPOSITORY DESTINATION" && exit 1
REPOSITORY=$1
DESTINATION=$2
TMPNAME="/tmp/$(basename $REPOSITORY).$$"
git clone $REPOSITORY $TMPNAME
rm -rf $TMPNAME/.git
mkdir -p $DESTINATION
cp -r $TMPNAME/* $DESTINATION
rm -rf $TMPNAME

读取树/结帐索引或存档解决方案有什么问题?据我所知,您已经完成了类似 mkdir -p "$2" && git --git-dir="$1" archive HEAD | tar -x -C "$2" 的操作,但有些冗长。
我无法从远程存储库中获取读取树,并且存档解决方案不适用于 github。
Yes with archive get a Invalid command: 'git-upload-archive'... 错误,我没有 core.gitProxy 配置选项和 GIT_PROXY_COMMAND 环境变量集
R
RkG

这样做很简单,这是 .bash_profile 的一个功能,它直接将存档解压缩到当前位置,首先配置您通常的 [url:path]。注意:使用此功能可以避免克隆操作,它直接从远程仓库获取。

gitss() {
    URL=[url:path]

    TMPFILE="`/bin/tempfile`"
    if [ "$1" = "" ]; then
        echo -e "Use: gitss repo [tree/commit]\n"
        return
    fi
    if [ "$2" = "" ]; then
        TREEISH="HEAD"
    else
        TREEISH="$2"
    fi
    echo "Getting $1/$TREEISH..."
    git archive --format=zip --remote=$URL/$1 $TREEISH > $TMPFILE && unzip $TMPFILE && echo -e "\nDone\n"
    rm $TMPFILE
}

.gitconfig 的别名,需要相同的配置(注意在 .git 项目中执行命令,它总是跳转到之前的基本目录 as said here,直到修复此问题我个人更喜欢该功能

ss = !env GIT_TMPFILE="`/bin/tempfile`" sh -c 'git archive --format=zip --remote=[url:path]/$1 $2 \ > $GIT_TMPFILE && unzip $GIT_TMPFILE && rm $GIT_TMPFILE' -

T
Tom

如果您在要创建导出的机器上有存储库的本地副本,我还有另一个解决方案可以正常工作。在这种情况下,移动到此存储库目录,并输入以下命令:

GIT_WORK_TREE=outputdirectory git checkout -f

如果您管理具有 git 存储库的网站并希望在 /var/www/ 中检出一个干净的版本,这将特别有用。在这种情况下,在 .git/hooks/post-receive 脚本中添加此命令(hooks/post-receive 在裸存储库上,更适合这种情况)


C
Community

我认为 @Aredridel 的帖子最接近,但还有更多内容 - 所以我将在此处添加;问题是,在 svn 中,如果您在 repo 的子文件夹中,并且您这样做:

/media/disk/repo_svn/subdir$ svn export . /media/disk2/repo_svn_B/subdir

然后 svn 将导出所有受修订控制的文件(它们也可能是新添加的;或已修改状态) - 如果您在该目录中有其他“垃圾”(我这里不计算 .svn 子文件夹,但像 .o 文件这样可见的东西),它将不会被导出;只有那些由 SVN repo 注册的文件才会被导出。对我来说,一件好事是,此导出还包括具有本地更改但尚未提交 的文件;另一个好处是导出文件的时间戳与原始文件相同。或者,正如 svn help export 所说:

从 PATH1 指定的工作副本中导出一个干净的目录树,如果给出,则在修订版本 REV,否则在 WORKING,到 PATH2。 ... 如果未指定 REV,则将保留所有本地更改。不受版本控制的文件将不会被复制。

要意识到 git 不会保留时间戳,请比较这些命令的输出(在您选择的 git 存储库的子文件夹中):

/media/disk/git_svn/subdir$ ls -la .

... 和:

/media/disk/git_svn/subdir$ git archive --format=tar --prefix=junk/ HEAD | (tar -t -v --full-time -f -)

...而且我在任何情况下都注意到 git archive 导致存档文件的所有时间戳都相同! git help archive 说:

git archive 在给定树 ID 与给定提交 ID 或标签 ID 时的行为不同。在第一种情况下,当前时间用作存档中每个文件的修改时间。在后一种情况下,将使用引用的提交对象中记录的提交时间。

...但显然这两种情况都设置了“每个文件的修改时间”;从而不保留这些文件的实际时间戳!

因此,为了也保留时间戳,这里有一个 bash 脚本,它实际上是一个“单行”,虽然有些复杂 - 所以下面分多行发布:

/media/disk/git_svn/subdir$ git archive --format=tar master | (tar tf -) | (\
  DEST="/media/diskC/tmp/subdirB"; \
  CWD="$PWD"; \
  while read line; do \
    DN=$(dirname "$line"); BN=$(basename "$line"); \
    SRD="$CWD"; TGD="$DEST"; \
    if [ "$DN" != "." ]; then \
      SRD="$SRD/$DN" ; TGD="$TGD/$DN" ; \
      if [ ! -d "$TGD" ] ; then \
        CMD="mkdir \"$TGD\"; touch -r \"$SRD\" \"$TGD\""; \
        echo "$CMD"; \
        eval "$CMD"; \
      fi; \
    fi; \
    CMD="cp -a \"$SRD/$BN\" \"$TGD/\""; \
    echo "$CMD"; \
    eval "$CMD"; \
    done \
)

请注意,假设您正在导出“当前”目录(上方,/media/disk/git_svn/subdir)中的内容 - 并且您导出到的目标位置有些不便,但它位于 DEST 环境变量中。请注意,使用此脚本;在运行上述脚本之前,您必须自己手动创建 DEST 目录。

脚本运行后,您应该能够比较:

ls -la /media/disk/git_svn/subdir
ls -la /media/diskC/tmp/subdirB   # DEST

...并希望看到相同的时间戳(对于那些受版本控制的文件)。

希望这对某人有帮助,干杯!


B
Brandon

如果您还需要子模块,这应该可以解决问题:https://github.com/meitar/git-archive-all.sh/wiki


实际上,看起来它有一些小问题,所以它可能还没有准备好迎接黄金时段。
M
MichaelMoser

我的 .bashrc 文件中有以下实用程序函数:它在 git 存储库中创建当前分支的存档。

function garchive()
{
  if [[ "x$1" == "x-h" || "x$1" == "x" ]]; then
    cat <<EOF
Usage: garchive <archive-name>
create zip archive of the current branch into <archive-name>
EOF
  else
    local oname=$1
    set -x
    local bname=$(git branch | grep -F "*" | sed -e 's#^*##')
    git archive --format zip --output ${oname} ${bname}
    set +x
  fi
}