做一个“混帐出口”(如“SVN出口”)?

我一直在想,是否有一个很好的“git导出”解决scheme可以创build没有.git存储库目录的树的副本。 至less有三种方法我知道:

  1. git clone然后删除.git库目录。
  2. git checkout-index暗示着这个function,但是以“只是把所需的目录树读入索引…”开头,我不完全知道该怎么做。
  3. git-export是一个第三方脚本,基本上把一个git clone到一个临时位置,然后是rsync --exclude='.git'到最终的目的地。

这些解决scheme真的没有让我满意。 离svn export最近的一个可能是option 1,因为这两个都要求目标目录先是空的。 但是,选项2似乎更好,假设我可以弄清楚在索引中读取树是什么意思。

可能最简单的方法是使用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 checkout-index -a -f --prefix=/destination/path/ 

(有关更多详细信息,请参阅Greg的答案 )

我发现了什么select2意味着什么。 从存储库中,您可以执行:

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

path末尾的斜线很重要,否则会导致文件在/目的地前缀为“path”。

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

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

这似乎是我想要的那种“git export”。

git archive也可以与远程仓库一起使用。

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

要导出特定的path回购添加尽可能多的path作为最后一个参数git,例如:

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

在这里输入图像描述

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

只要使用svn export

据我所知Github不允许archive --remote 。 虽然GitHub是svn兼容的 ,他们可以访问所有的git repos svn所以你可以像使用svn export一样对GitHub url进行一些调整。

例如,要导出整个存储库,请注意URL中的trunk如何replacemaster (或任何项目的HEAD分支设置为 ):

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

您可以导出单个文件甚至某个特定的path或文件夹:

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

jQuery JavaScript库示例

HEAD分支或分支将可用于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手册 :

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

前缀的能力基本上使得使用git-checkout-index作为“导出为树”函数是微不足道的。 只要将所需的树读入索引,并执行:

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

我已经写了一个简单的git-checkout-index封装,你可以这样使用:

 git export ~/the/destination/dir 

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

安装简单; 只要将脚本放在PATH某个地方,并确保它是可执行的。

GitHub仓库git-export

看起来,Git的这个问题不如SVN。 Git只会把.git文件夹放在版本库的根目录下,而SVN会在每个子目录下放一个.svn文件夹。 所以“svn导出”避免了recursion的命令行魔术,而使用Gitrecursion是没有必要的。

相当于

 svn export . otherpath 

里面现有的回购是

 git archive branchname | (cd otherpath; tar x) 

相当于

 svn export url otherpath 

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

我广泛使用git-submodules。 这个为我工作:

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

正确的答案是“git checkout”

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

-F
从索引检出path时,不要在未合并的条目上失败; 相反,未被合并的条目被忽略。

-q
避免冗长

此外,你可以得到任何分支或标签,或从一个特定的提交修订版,像在SVN中只是添加SHA1(Git中的SHA1等同于SVN中的修订版本号)

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

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

更新:为了避免被斩首的问题,或者在使用checkout标签,分支或SHA1导出时保留正在工作的存储库,您需要在末尾添加 – ./

双破折号 – 告诉GIT,破折号后面的所有内容都是path或文件,在这种情况下还要告诉git checkout不要更改HEAD

例子:

这个命令将得到刚刚提交的libs目录和readme.txt文件

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

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

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

获得另一个分支的出口

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

注意./是相对于版本库的根目录的

这将复制所有内容,减去.dot文件。 我用这个将git克隆的项目导出到我的web应用程序的git仓库中,没有.git的东西。

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

简单的旧bash作品只是伟大的:)

在寻找一种导出git仓库的方式时,我经常碰到这个页面。 我对这个问题的回答考虑了svn导出的三个属性与git相比,因为svn遵循一个集中的存储库方法:

  • 它通过不导出所有修订版来最小化到远程存储库位置的stream量
  • 它不包含导出目录中的元信息
  • 使用svn导出某个分支是通过指定适当的path来完成的

     git clone --depth 1 --branch master git://git.somewhere destination_path rm -rf destination_path/.git 

在构build某个版本时,克隆一个稳定的分支是很有用的,例如--branch stable或者--branch release/0.9

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

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

我只想指出,如果你是

  1. 导出存储库的一个子文件夹(这就是我以前使用SVN导出function的方式)
  2. 将该文件夹中的所有内容复制到部署目标都可以
  3. 因为你已经有了整个资源库的副本。

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

您可以将任何提交的远程仓库归档为zip文件。

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

对于GitHub用户, git archive --remote方法将不会直接工作,因为导出URL是短暂的 。 你必须问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中随机select):

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

bash执行git-export。

我已经在自己的函数中分割了.empty文件的创build和删除过程,目的是在'git-archive'实现中重新使用它们(稍后会发布)。

我还将'.gitattributes'文件添加到进程中,以便从目标导出文件夹中删除不需要的文件。 在使'git-export'函数更高效的同时,还包括了过程的详细程度。

“空” EMPTY_FILE =;

 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 admin wheel 25445901 11月3日12:57 /tmp/rel-1.0.0.tgz

我现在已经将'git archive'function整合到一个使用'create_empty'function和其他function的过程中。

 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 # eg: 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; } 

如果你想要一些与子模块一起工作的东西,这可能是值得的。

注意:

  • MASTER_DIR =签出您的子模块也签出
  • DEST_DIR =这个输出结束
  • 如果你有rsync,我认为你可以用更less的球疼来做同样的事情。

假设:

  • 您需要从MASTER_DIR的父目录(即从MASTER_DIR CD ..)
  • 假定已经创build了DEST_DIR。 如果你愿意,这很容易修改,包括创build一个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

这会将提交范围(C到G)中的文件复制到tar文件中。 注意:这只会得到提交的文件。 不是整个存储库。 从这里略微修改

示例提交历史

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手册页

-r – >recursion到子树中

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

– 仅限名称 – >仅显示已更改文件的名称。

–diff-filter = ACMRT – >只select这些文件。 在这里看到完整的文件列表

C..G – >这个提交范围内的文件

C〜 – >包含来自Commit C的文件,而不仅仅是来自Commit C的文件。

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

我的偏好实际上是在你的Makefile(或其他构build系统)中有一个dist目标,它可以导出代码的可分发档案(.tar.bz2,.zip,.jar或其他适当的东西)。 如果你碰巧使用GNU autotools或Perl的MakeMaker系统,我认为这是自动存在的。 如果没有,我强烈build议添加它。

ETA(2012-09-06):哇,苛刻的降价。 我仍然认为最好是使用构build工具而不是源代码控制工具来构build发行版。 我相信用构build工具来构build工件。 在我目前的工作中,我们的主要产品是用一个ant的目标。 我们正在切换源代码控制系统,而这个ant目标的存在意味着在移植过程中更less的麻烦。

是的, 这是一个干净而整洁的命令来存档你的代码,而没有任何git包含在档案中,并且很好地传递而不用担心任何git提交历史。

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

我需要这个部署脚本,我不能使用任何上述方法。 相反,我想出了一个不同的解决scheme:

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

这样做简单,这是一个.bash_profile的函数,它直接解压当前位置的压缩文件,首先configuration你的平常的[url:path]。 注:使用此function可以避免克隆操作,它直接从远程回购。

 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的别名,需要相同的configuration(TAKE CARE在.git项目中执行命令,它总是跳转到前面所述的基本目录,直到这是固定的,我个人更喜欢函数

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

我认为@Aredridel的post是最接近的,但还有一点 – 所以我会在这里添加这个; 在svn ,如果你在一个回购的子文件夹中,你就可以这样做:

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

那么svn会导出所有在修订版本控制下的文件(它们也可能是新增加的;或修改后的状态) – 如果你在那个目录中有其他的“垃圾”(我不是在这里计数.svn子文件夹,而是可见的东西如.o文件),它不会被导出; 只有通过SVN回购注册的文件才会被导出。 For me, one nice thing is that this export also includes files with local changes that have not been committed yet; and another nice thing is that the timestamps of the exported files are the same as the original ones. Or, as svn help export puts it:

  1. Exports a clean directory tree from the working copy specified by PATH1, at revision REV if it is given, otherwise at WORKING, into PATH2. … If REV is not specified, all local changes will be preserved. Files not under version control will not be copied.

To realize that git will not preserve the timestamps, compare the output of these commands (in a subfolder of a git repo of your choice):

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

… and:

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

… and I, in any case, notice that git archive causes all the timestamps of the archived file to be the same! git help archive says:

给定一个树ID与提供一个提交ID或标签ID时,git归档的行为是不同的。 在第一种情况下,当前时间被用作档案中每个文件的修改时间。 In the latter case the commit time as recorded in the referenced commit object is used instead.

… but apparently both cases set the "modification time of each file"; thereby not preserving the actual timestamps of those files!

So, in order to also preserve the timestamps, here is a bash script, which is actually a "one-liner", albeit somewhat complicated – so below it is posted in multiple lines:

 /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 \ ) 

Note that it is assumed that you're exporting the contents in "current" directory (above, /media/disk/git_svn/subdir ) – and the destination you're exporting into is somewhat inconveniently placed, but it is in DEST environment variable. Note that with this script; you must create the DEST directory manually yourself, before running the above script.

After the script is ran, you should be able to compare:

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

… and hopefully see the same timestamps (for those files that were under version control).

Hope this helps someone,
干杯!

By far the easiest way i've seen to do it (and works on windows as well) is git bundle :

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

See this answer for more details: How can I copy my git repository from my windows machine to a linux machine via usb drive?

If you need submodules as well, this should do the trick: https://github.com/meitar/git-archive-all.sh/wiki

i have the following utility function in my .bashrc file: it creates an archive of the current branch in a git repository.

 function garchive() { if [[ "x$1" == "xh" || "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 } 

The option 1 sounds not too efficient. What if there is no space in the client to do a clone and then remove the .git folder?

Today I found myself trying to do this, where the client is a Raspberry Pi with almost no space left. Furthermore, I also want to exclude some heavy folder from the repository.

Option 2 and others answers here do not help in this scenario. Neither git archive (because require to commit a .gitattributes file, and I don't want to save this exclusion in the repository).

Here I share my solution, similar to option 3, but without the need of git clone :

 tmp=`mktemp` git ls-tree --name-only -r HEAD > $tmp rsync -avz --files-from=$tmp --exclude='fonts/*' . raspberry: 

Changing the rsync line for an equivalent line for compress will also work as a git archive but with a sort of exclusion option (as is asked here ).

This is my favorite solution:

 git diff <old-commit> <new-commit> --name-only | xargs tar -zcvf myTarFile.tar.gz 

This will compare two different commits and export changed files as a tar.gz file

干杯!