【问题标题】:Do a "git export" (like "svn export")?做一个“git export”(比如“svn export”)?
【发布时间】:2010-09-14 16:58:29
【问题描述】:

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

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

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

【问题讨论】:

  • @rnrTom:见 Somov 的回答。 (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”的手册页,仅供参考。

标签: git export git-archive svn-export


【解决方案1】:

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

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

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

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

ZIP 存档:

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 等。因此,如果您不想要它们,请确保您在 .gitattributes 文件中使用 export-ignore 属性并在归档之前提交它。见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 属性,顶级目录将是 'file'。)
  • 最简单的方法:git archive -o latest.zip HEAD 它创建一个 Zip 存档,其中包含当前分支上最新提交的内容。请注意,输出格式由输出文件的扩展名推断。
  • 它不支持 git 子模块 :(
【解决方案2】:

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

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

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

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

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

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

【讨论】:

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

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 - per docs
【解决方案4】:

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

只需使用svn export

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

例如要导出整个存储库,请注意 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 Library 为例

HEAD 分支或 ma​​ster 分支将可用 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
【解决方案5】:

来自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,并附上评论“将所需的树读入索引”,我不知道这是什么意思。
【解决方案6】:

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

git export ~/the/destination/dir

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

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

The github repository for git-export

【讨论】:

  • 这个包装器不是平台无关的;它依赖于/bin/sh。因此,如果您使用的是 Windows,则此解决方案可能不适合您。
  • 呃,这个脚本有 57 行文档、空格、设置、参数解析,而实际上只有一行......
【解决方案7】:

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

【讨论】:

【解决方案8】:

相当于

svn export . otherpath

在现有的 repo 中是

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
  • 你可以使用 C 选项来代替 subshel​​l,像这样:git archive branchname | tar xC otherpath
  • 注意C tar 选项仅适用于 GNU Tar。
【解决方案9】:

如果您不排除带有.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
【解决方案10】:

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

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

【讨论】:

  • 不会错过名称以点开头的文件,例如.htaccess
  • 一个很好的解决方案,我会把 --exclude='.*' 改成 --exclude='.git*'
  • --exclude-vcs 如果你打算采取这种策略
  • ./FROM/ 可以是远程仓库吗?
  • 作为仅供参考,我的rsync 副本将参数列为--cvs-exclude。此外,它仍然会复制 .gitattributes.gitignore
【解决方案11】:

在寻找导出 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." 并执行标准而不是浅克隆,从而违背了此答案的全部目的。 祝你好运!
【解决方案12】:

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

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

普通的旧 bash 效果很好:)

【讨论】:

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

简单的克隆然后删除.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 增加任何东西。
【解决方案14】:

对于 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

【讨论】:

    【解决方案15】:

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

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

    【讨论】:

    【解决方案16】:

    我只想指出,如果你是

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

    那么你可以只使用cp foo [destination] 而不是上面提到的git-archive master foo | -x -C [destination]

    【讨论】:

      【解决方案17】:

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

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

      【讨论】:

        【解决方案18】:

        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
        【解决方案19】:

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

        注意:

        • 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

        【讨论】:

          【解决方案20】:

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

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

          【讨论】:

          • 我想到的项目不是代码项目;它恰好更像是一个网站项目。
          • 没有解决问题。
          • 是的,这样的答案可能无法满足每个人的需求,但反对票很奇怪。它一个完全有效的答案,而且确实,在许多情况下,它是唯一正确的答案。非常有道理的一点是,将此问题视为“vc 工具问题”通常会完全走错路。
          【解决方案21】:

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

          可以这样做:

          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 是(曾经?)reportedly 有问题,但对于导出的情况,上述问题应该无关紧要。

          【讨论】:

          • 注意:我刚刚注意到有两页的anwsers,我在发帖前只看了一页。有一个类似的anwser,只有--depth,这意味着--single-branch,除非给出--no-single-branch,这意味着这可能具有相同的效果。不过不确定,有专家可能会证实?
          【解决方案22】:

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

          提交历史示例

          A --> 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

          【讨论】:

            【解决方案23】:

            到目前为止,我见过的最简单的方法(也适用于 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
            【解决方案24】:

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

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

            【讨论】:

              【解决方案25】:

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

              #!/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
              

              【讨论】:

              • read-tree/checkout-index 或归档解决方案有什么问题?据我所知,您已经完成了类似于 mkdir -p "$2" &amp;&amp; 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 环境变量集
              【解决方案26】:

              做起来很简单,这是.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' -
              

              【讨论】:

                【解决方案27】:

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

                GIT_WORK_TREE=outputdirectory git checkout -f

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

                【讨论】:

                  【解决方案28】:

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

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

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

                  1. 从指定的工作副本中导出干净的目录树 PATH1,如果给出,则在修订版 REV 中,否则在 WORKING 中,进入 路径 2。 ... 如果未指定 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 归档在给定树 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
                  

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

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

                  【讨论】:

                    【解决方案29】:

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

                    【讨论】:

                    • 实际上,它似乎有一些小问题,所以它可能还没有准备好黄金时段。
                    【解决方案30】:

                    我的 .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
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2010-11-12
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-08-27
                      • 1970-01-01
                      • 2018-11-29
                      • 2016-02-10
                      相关资源
                      最近更新 更多