Do a “git export” (like “svn export”)?
我一直在想是否有一个好的"git export"解决方案,它创建了一个没有
这些解决方案都没有让我感到满意。 最接近
可能最简单的方法是使用
1 | git archive master | tar -x -C /somewhere/else |
大多数时候我需要从git"导出"某些东西,我想要一个压缩存档,所以我做这样的事情。
1 | git archive master | bzip2 >source-tree.tar.bz2 |
ZIP存档:
1 | git archive --format zip --output /full/path/to/zipfile.zip master |
请注意,即使存档不包含.git目录,它也会包含其他隐藏的git特定文件,如.gitignore,.gitattributes等。如果您不想在存档中使用它们,请确保使用.gitattributes文件中的export-ignore属性并在进行归档之前提交此属性。阅读更多...
注意:如果您对导出索引感兴趣,则命令为
1 | git checkout-index -a -f --prefix=/destination/path/ |
(有关详细信息,请参阅格雷格的答案)
我发现了2选项意味着什么。从存储库中,您可以执行以下操作:
1 | git checkout-index -a -f --prefix=/destination/path/ |
路径末尾的斜杠很重要,否则会导致文件位于/ destination中,前缀为"path"。
由于在正常情况下索引包含存储库的内容,因此"将所需的树读入索引"没有什么特别之处。它已经存在了。
检查索引中的所有文件需要
这似乎是我正在寻找的那种"git export"。
1 2 | git archive --format=tar \ --remote=ssh://remote_server/remote_repository master | tar -xf - |
要在repo中导出特定路径,请添加尽可能多的路径作为git的最后一个参数,例如:
1 2 | git archive --format=tar \ --remote=ssh://remote_server/remote_repository master path1/ path2/ | tar -xv |
如果存储库托管在GitHub上,则应回答特殊情况。
只需使用
据我所知,Github不允许
例如,要导出整个存储库,请注意URL中的
1 | svn export https://github.com/username/repo-name/trunk/ |
您可以导出单个文件甚至某个路径或文件夹:
1 | svn export https://github.com/username/repo-name/trunk/src/lib/folder |
使用jQuery JavaScript库的示例
1 | svn ls https://github.com/jquery/jquery/trunk |
非
1 | svn ls https://github.com/jquery/jquery/branches/2.1-stable |
1 | svn ls https://github.com/jquery/jquery/tags/2.1.3 |
从Git手册:
使用git-checkout-index"导出整个树"
前缀能力基本上使得git-checkout-index用作"export as tree"函数变得微不足道。只需将所需的树读入索引,然后执行:
我写了一个围绕
1 | git export ~/the/destination/dir |
如果目标目录已存在,则需要添加
安装简单;只需将脚本放在
对于Git而言,这似乎不是SVN的问题。 Git只在存储库根目录中放置一个.git文件夹,而SVN在每个子目录中放置一个.svn文件夹。所以"svn export"避免了递归命令行魔术,而Git递归则没有必要。
相当于
1 | svn export . otherpath |
在现有的回购中是
1 | git archive branchname | (cd otherpath; tar x) |
相当于
1 | svn export url otherpath |
是
1 | git archive --remote=url branchname | (cd otherpath; tar x) |
如果您不排除
1 2 | mkdir /path/to/checkout/ git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout -f -q |
-f
When checking out paths from the index, do not fail upon unmerged
entries; instead, unmerged entries are ignored.
和
-q
Avoid verbose
此外,您可以获取任何分支或标记,或者从SVN中的特定提交修订版添加SHA1(Git中的SHA1等同于SVN中的修订号)
1 2 | mkdir /path/to/checkout/ git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout 2ef2e1f2de5f3d4f5e87df7d8 -f -q -- ./ |
更新:
为了避免斩首问题或在使用checkout导出标签,分支或SHA1时保持工作存储库完整,您需要在末尾添加
双破折号
例子:
此命令将只获取libs目录以及完全提交的
1 | git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout fef2e1f2de5f3d4f5e87df7d8 -f -q -- ./libs ./docs/readme.txt |
这将在头
1 | git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout HEAD^2 -f -q -- ./my_file_2_behind_HEAD.txt |
获得另一个分支的导出
1 | git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout myotherbranch -f -q -- ./ |
请注意,
我广泛使用git-submodules。
这个对我有用:
1 | rsync -a ./FROM/ ./TO --exclude='.*' |
在寻找导出git存储库的方法时,我经常点击此页面。我对这个问题的回答考虑了svn export与git相比设计的三个属性,因为svn遵循集中式存储库方法:
- 它通过不导出所有修订版来最小化到远程存储库位置的流量
- 它不包含导出目录中的元信息
-
使用svn导出某个分支是通过指定适当的路径来完成的
1
2git clone --depth 1 --branch master git://git.somewhere destination_path
rm -rf destination_path/.git
在构建特定版本时,克隆稳定分支(例如
这将复制所有内容,减去.dot文件。我使用它来将git克隆的项目导出到我的web应用程序的git repo而没有.git的东西。
cp -R ./path-to-git-repo /path/to/destination/
普通的老bash工作得很好:)
就像克隆一样简单,然后删除.git文件夹:
git clone url_of_your_repo path_to_export && rm -rf path_to_export/.git
是的,这是一个干净利落的命令,用于存档您的代码,而不会在存档中包含任何git,并且可以传递而不必担心任何git提交历史记录。
1 | git archive --format zip --output /full/path/to/zipfile.zip master |
对于GitHub用户,
1 | curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | tar xzf - |
这将为您提供本地目录中的导出代码。例:
1 2 3 | $ 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中的随机目录):
1 2 | curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | \ tar xzC /path/you/want --strip 1 |
我只想指出你的情况
然后你可以使用
您可以将任何提交的远程仓库归档为zip文件。
1 | git archive --format=zip --output=archive.zip --remote=USERNAME@HOSTNAME:PROJECTNAME.git HASHOFGITCOMMIT |
Bash实现git-export。
我已经在自己的函数上对.empty文件创建和删除过程进行了细分,目的是在'git-archive'实现中重用它们(稍后将发布)。
我还将'.gitattributes'文件添加到进程中,以便从目标导出文件夹中删除不需要的文件。
在使'git-export'功能更有效的同时包含了流程的冗长。
"空" EMPTY_FILE =;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | function create_empty () { ## Processing path (target-dir): TRG_PATH="${1}"; ## Component(s): EXCLUDE_DIR=".git"; echo -en" Adding '${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" Purging 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. " ## 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" ${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" Checking-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; } |
Output:
$ git-export /tmp/rel-1.0.0
Adding '.empty' files to empty folder(s): ... done.
Checking-Out Index component(s): ... done.
Resetting HEAD and Index: ... done.
Purging Git-Specific component(s): ...
'/tmp/rel-1.0.0/{.buildpath}' files ... done.'
'/tmp/rel-1.0.0/{.project}' files ... done.'
'/tmp/rel-1.0.0/{.gitignore}' files ... done.'
'/tmp/rel-1.0.0/{.git}' files ... done.'
'/tmp/rel-1.0.0/{.gitattributes}' files ... done.'
'/tmp/rel-1.0.0/{*.mno}' files ... done.'
'/tmp/rel-1.0.0/{*~}' files ... done.'
'/tmp/rel-1.0.0/{.*~}' files ... done.'
'/tmp/rel-1.0.0/{*.swp}' files ... done.'
'/tmp/rel-1.0.0/{*.swo}' files ... done.'
'/tmp/rel-1.0.0/{.DS_Store}' files ... done.'
'/tmp/rel-1.0.0/{.settings}' files ... done.'
'/tmp/rel-1.0.0/{.empty}' files ... done.'
done.
Archiving Checked-Out component(s): ... done.
-rw-r--r-- 1 admin wheel 25445901 3 Nov 12:57 /tmp/rel-1.0.0.tgz
I have now incorporated the 'git archive' functionality into a single process that makes use of 'create_empty' function and other features.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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" "; # 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; } |
如果你想要一些适用于子模块的东西,这可能是值得的。
注意:
- 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
这会将提交范围(C到G)中的文件复制到tar文件中。注意:这只会提交文件。不是整个存储库。从这里略微修改
提交历史示例
A - > B - > C - > D - > E - > F - > G - > H - > I
1 | git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT C~..G | xargs tar -rf myTarFile.tar |
git-diff-tree手册
-r - >递归到子树
--no-commit-id - > git diff-tree在适用时输出带有提交ID的行。此标志禁止提交ID输出。
--name-only - >仅显示已更改文件的名称。
--diff-filter = ACMRT - >仅选择这些文件。请参阅此处获取完整的文件列表
C..G - >此提交范围内的文件
C~ - >包含来自Commit C的文件。不仅仅是自Commit C以来的文件。
| xargs tar -rf myTarFile - >输出到tar
我的偏好实际上是在Makefile(或其他构建系统)中有一个dist目标,它导出代码的可分发存档(.tar.bz2,.zip,.jar或任何合适的东西)。如果您碰巧使用GNU autotools或Perl的MakeMaker系统,我认为这是自动存在的。如果没有,我强烈建议添加它。
ETA(2012-09-06):哇,苛刻的downvotes。我仍然认为使用构建工具而不是源代码控制工具构建发行版会更好。我相信使用构建工具构建工件。在我目前的工作中,我们的主要产品是用蚂蚁目标建造的。我们正在切换源代码控制系统,并且这个蚂蚁目标的存在意味着迁移中的麻烦减少了。
这样做很简单,这是.bash_profile的一个功能,它直接在当前位置解压缩存档,首先配置您通常的[url:path]。注意:使用此功能可以避免克隆操作,它可以直接从远程仓库获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | gitss() { URL=[url:path] TMPFILE="`/bin/tempfile`" if ["$1" ="" ]; then echo -e"Use: gitss repo [tree/commit] " 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" Done " rm $TMPFILE } |
.gitconfig的别名,需要相同的配置(TAKE CARE执行.git项目中的命令,它总是先前跳转到基础目录,如此处所述,直到修复此我个人更喜欢该功能
1 | 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' - |
我需要这个用于部署脚本,我不能使用任何上述方法。相反,我想出了一个不同的解决方案:
1 2 3 4 5 6 7 8 9 10 | #!/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 |
正如我理解这个问题一样,它更多的是从服务器下载某个状态,没有历史记录,没有其他分支的数据,而不是从本地存储库中提取状态(这里有很多答案)。
这可以这样做:
1 2 | 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 据报道是错误的,但对于出口情况,上述问题无关紧要。
在添加前缀(例如目录名称)时将git导出到zip存档:
1 | git archive master --prefix=directoryWithinZip/ --format=zip -o out.zip |
我认为@Aredridel的帖子最接近,但还有更多 - 所以我会在这里添加;问题是,在
1 | /media/disk/repo_svn/subdir$ svn export . /media/disk2/repo_svn_B/subdir |
然后
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.
要意识到
1 | /media/disk/git_svn/subdir$ ls -la . |
......和:
1 | /media/disk/git_svn/subdir$ git archive --format=tar --prefix=junk/ HEAD | (tar -t -v --full-time -f -) |
...在任何情况下,我都注意到
git archive behaves differently when given a tree ID versus when given a commit ID or tag ID. In the first case the
current time is used as the modification time of each file in the archive. In the latter case the commit time as recorded
in the referenced commit object is used instead.
......但显然两种情况都设定了"每个文件的修改时间";从而不保留这些文件的实际时间戳!
所以,为了也保留时间戳,这里是一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /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 \ ) |
请注意,假设您正在导出"当前"目录(上面的
运行脚本后,您应该可以比较:
1 2 | ls -la /media/disk/git_svn/subdir ls -la /media/diskC/tmp/subdirB # DEST |
...并希望看到相同的时间戳(对于那些受版本控制的文件)。
希望这有助于某人,
干杯!
到目前为止,我所见过的最简单的方法(也适用于Windows)是
有关更多详细信息,请参阅此答案:如何通过USB驱动器将我的git存储库从我的Windows机器复制到linux机器?
如果您要在要创建导出的计算机上拥有存储库的本地副本,我还有另一种解决方案可以正常工作。在这种情况下,请移至此存储库目录,然后输入以下命令:
如果您使用git存储库管理网站并希望在
如果你也需要子模块,这应该可以解决这个问题:https://github.com/meitar/git-archive-all.sh/wiki
我的.bashrc文件中有以下实用程序函数:它在git存储库中创建当前分支的存档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function garchive() { if [["x$1" =="x-h" ||"x$1" =="x" ]]; then cat <<EOF Usage: garchive create zip archive of the current branch into 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 } |
选项1听起来效率不高。如果客户端中没有空间来执行克隆然后删除
今天我发现自己试图这样做,客户端是Raspberry Pi,几乎没有空间。此外,我还想从存储库中排除一些繁重的文件夹。
选项2和其他答案在这种情况下无济于事。
在这里,我分享我的解决方案,类似于选项3,但不需要
1 2 3 | tmp=`mktemp` git ls-tree --name-only -r HEAD > $tmp rsync -avz --files-from=$tmp --exclude='fonts/*' . raspberry: |
更改压缩的等效行的