做一个"混帐出口"(如"svn出口")?
-
03-07-2019 - |
题
我一直想知道是否有一个良好的"混帐出口"方案,创造一个复制的一棵树不 .git
储存库的目录。至少有三个方法我知道:
git clone
随后通过删除.git
储存库的目录。git checkout-index
暗示了这一功能,但开始,"只是读取想要树入索引..."我不完全确定如何做。git-export
是一个第三方脚本,基本上不会git clone
进入一个临时位置,随后是rsync --exclude='.git'
进入最终目的地。
没有这些解决方案的真正打击我作为是令人满意的。最近的一个 svn export
可能备选案文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目录,它也会包含其他隐藏的git特定文件,如.gitignore,.gitattributes等。如果您不想在存档中使用它们,确保在.gitattributes文件中使用export-ignore属性并在进行存档之前提交此属性。 阅读更多内容......
注意:如果您对导出索引感兴趣,则命令为
git checkout-index -a -f --prefix=/destination/path/
(有关更多详情,请参阅 Greg的回答)
其他提示
我发现了2选项意味着什么。从存储库中,您可以执行以下操作:
git checkout-index -a -f --prefix=/destination/path/
路径末尾的斜杠非常重要,否则会导致文件位于/ destination中,前缀为“path”。
由于在正常情况下索引包含存储库的内容,因此“将所需的树读入索引”没有什么特别之处。它已经存在了。
-a
标志是检查索引中的所有文件所必需的(我不确定在这种情况下省略此标志意味着什么,因为它不能做我想要的)。 -f
标志强制覆盖输出中的任何现有文件,此命令通常不会这样做。
这似乎是“git export”的类型。我在找。
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
如果存储库托管在GitHub上,则为特殊情况。
只需使用 svn export
。
据我所知,Github不允许 archive --remote
。虽然GitHub是 svn兼容,但他们确实拥有所有git repos svn
access,所以你可以像通常那样使用 svn export
对你的GitHub网址进行一些调整。
例如,要导出整个存储库,请注意URL中的 trunk
如何替换 master
(或者项目的HEAD分支设置为):
svn export https://github.com/username/repo-name/trunk/
您可以导出单个文件甚至某个路径或文件夹:
svn export https://github.com/username/repo-name/trunk/src/lib/folder
jQuery JavaScript Library
的示例使用 trunk
可以使用 HEAD
分支或 master 分支:
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作为“export as tree”使用变得微不足道。功能。只需将所需的树读入索引,然后执行:
$ git checkout-index --prefix = git-export-dir / -a
我已经编写了一个围绕 git-checkout-index
的简单包装器,您可以这样使用:
git export ~/the/destination/dir
如果目标目录已存在,则需要添加 -f
或 - force
。
安装简单;只需将脚本放在 PATH
中的某个位置,并确保它是可执行的。
对于Git而言,这似乎不是SVN的问题。 Git只在存储库根目录中放置一个.git文件夹,而SVN在每个子目录中放置一个.svn文件夹。所以“svn export”避免递归命令行魔术,而Git递归则不是必需的。
相当于
svn export . otherpath
现有仓库中的是
git archive branchname | (cd otherpath; tar x)
相当于
svn export url otherpath
是
git archive --remote=url branchname | (cd otherpath; tar x)
如果您没有使用 .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不会删除任何文件,但会覆盖具有相同名称的文件而不会发出任何警告
更新:
为了避免斩首问题或在使用checkout导出标签,分支或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 -- ./
请注意, ./
是相对于存储库的根
我广泛使用git-submodules。 这个对我有用:
rsync -a ./FROM/ ./TO --exclude='.*'
我已经打这个网页经常当寻找一种方式出口的一个仓库的内容。我回答这个问题,认为这三个属性,svn出口的设计相比件,因为svn下一个中央储存库方法:
- 它最大限度地减少交通一个远程仓库位置的不导出所有的修订
- 它不包括meta的信息中出口目录
出口的某个分支svn是通过指定适当的路径
git clone --depth 1 --branch master git://git.somewhere destination_path rm -rf destination_path/.git
当建立一个某些释放也是有用的克隆一个稳定的支作为例 --branch stable
或 --branch release/0.9
.
这将复制所有内容,减去.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&amp;&amp; rm -rf path_to_export / .git 代码>
是的,这是一个干净利落的命令,用于存档您的代码,而不会在存档中包含任何git并且很好在不担心任何git提交历史的情况下传播。
git archive --format zip --output /full/path/to/zipfile.zip master
我只想指出,在这种情况下你是
- 出口的一个子文件夹中的储存库(这就是我如何用来使用SVN出口特征)
- 是确定与复制一切从那个文件夹部署的目的地
- 因为你已经有一个复制的整个储存库中的地方。
然后你就可以使用 cp foo [destination]
而不是提及 git-archive master foo | -x -C [destination]
.
对于GitHub用户, git archive --remote
方法不能直接使用,因为导出网址是短暂的。您必须向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
您可以将任何提交的远程仓库存档为zip文件。
git archive --format=zip --output=archive.zip --remote=USERNAME@HOSTNAME:PROJECTNAME.git HASHOFGITCOMMIT
git-export的Bash实现。
我已经根据自己的功能对.empty文件创建和删除过程进行了细分,目的是在“git-archive”实现中重新使用它们(稍后将发布)。
我还将'.gitattributes'文件添加到进程中,以便从目标导出文件夹中删除不需要的文件。 在使'git-export'功能更有效的同时包括流程的冗长。
EMPTY_FILE = QUOT; .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 admin wheel 25445901 3月12日12:57 /tmp/rel-1.0.0.tgz
我现在已将'git archive'功能合并到一个使用'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;
}
如果你想要的东西,与子模块,这可能是值得一走。
注:
- MASTER_DIR=检出与你的子模块还检查了
- DEST_DIR=这将结束了
- 如果你可以,我想你能够做同样的事情与甚至小球疼痛。
假设:
- 你需要运行这种从父目录的MASTER_DIR(i。e从MASTER_DIR cd..)
- DEST_DIR是假设已经建立。这是很容易修改,以包括建立一个DEST_DIR如果你想
cd MASTER_DIR&&tar-zcvf../DEST_DIR/export.tar.gz --排除='.git*' .&&cd../DEST_DIR/&&焦油的文件夹export.tar.gz &&rm export.tar.gz
我的偏好实际上是在Makefile(或其他构建系统)中有一个 dist 目标,该目标导出代码的可分发存档(.tar.bz2,.zip,.jar或什么是合适的)。如果您碰巧使用GNU autotools或Perl的MakeMaker系统,我认为这是自动存在的。如果没有,我强烈建议添加它。
ETA(2012-09-06):哇,苛刻的downvotes。我仍然认为使用构建工具而不是源代码控制工具构建发行版会更好。我相信使用构建工具构建工件。在我目前的工作中,我们的主要产品是用蚂蚁目标建造的。我们正处于切换源代码控制系统的过程中,这个蚂蚁目标的存在意味着迁移的麻烦减少了。
这会将提交范围(C到G)中的文件复制到tar文件中。注意:这只会提交文件。不是整个存储库。稍微修改自此处
示例提交历史记录
A - &gt; B - &gt; C - &gt; D - &gt; E - &gt; F - &gt; G - &gt; H - &gt;我
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT C~..G | xargs tar -rf myTarFile.tar
-r - &gt;递归到子树
- no-commit-id - &gt; git diff-tree在适用时输出带有提交ID的行。此标志禁止提交ID输出。
- 仅限名称 - &gt;仅显示已更改文件的名称。
- diff-filter = ACMRT - &gt;仅选择这些文件。 请点击此处查看完整的文件列表
C..G - &gt;此提交范围内的文件
C~ - &gt;包括Commit C中的文件。不仅仅是自Commit C。
以来的文件| xargs tar -rf myTarFile - &gt;输出到tar
我需要这个用于部署脚本,我不能使用任何上述方法。相反,我想出了一个不同的解决方案:
#!/bin/sh
[ $# -eq 2 ] || echo "USAGE <*> 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的一个函数,它直接在当前位置解压缩存档,首先配置你平常的[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的别名,需要相同的配置(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' -
因为我了解这个问题,它更多关于下载只是某些国家从服务器中,无历史,并且没有数据的其他分支,而不是一个抽取国家从一个地方储存库(因为许多anwsers在这里做)。
可以做这样的:
git clone -b someBranch --depth 1 --single-branch git://somewhere.com/repo.git \
&& rm -rf repo/.git/
--single-branch
是可用的,因为Git1.7.10(日2012年)。--depth
是(?) 据报道, 错误的,但是对的情况下出口的,所提到的问题不应该的问题。
我认为 @Aredridel 的帖子最接近,但还有更多 - 所以我会添加这个这里;问题是,在 svn
中,如果你在repo的子文件夹中,那么你做了:
/media/disk/repo_svn/subdir$ svn export . /media/disk2/repo_svn_B/subdir
然后 svn
将导出所有受版本控制的文件(它们也可以新添加;或者修改状态) - 如果你有其他“垃圾”在那个目录中(我不在这里计算 .svn
子文件夹,但是 .o
文件之类的可见内容),它将不导出;只导出SVN仓库注册的文件。对我来说,一个好处是这个导出还包括具有 not 尚未提交的本地更改的文件;另一个好处是导出文件的时间戳与原始文件的时间戳相同。或者,正如 svn help export
所说:
- 从指定的工作副本导出干净的目录树 PATH1,修订版REV,如果给出,否则在WORKING,进入 PATH2。 ...如果未指定REV,则全部为本地 变化将被保留。不受版本控制的文件会 不被复制。
醇>
要意识到 git
不会保留时间戳,请比较这些命令的输出(在您选择的 git
repo的子文件夹中):
/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时的行为不同。在第一种情况下 当前时间用作存档中每个文件的修改时间。在后一种情况下,记录的提交时间 而是使用引用的commit对象。
...但显然两种情况都设置了每个文件的修改时间“;从而不保留这些文件的实际时间戳!
因此,为了也保留时间戳,这里有一个 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
...并希望看到相同的时间戳(对于那些受版本控制的文件)。
希望这有助于某人,
干杯!
到目前为止,我所见过的最简单的方法(也适用于Windows)是 git bundle
:
git bundle create /some/bundle/path.bundle --all
有关详细信息,请参阅此答案:如何通过USB驱动器将我的git存储库从我的Windows机器复制到linux机器?
如果您要在要创建导出的计算机上拥有存储库的本地副本,我还有另一种解决方案可以正常工作。在这种情况下,请移至此存储库目录,然后输入以下命令:
GIT_WORK_TREE = outputdirectory git checkout -f
如果您使用git存储库管理网站并希望在 / var / www /
中签出一个干净的版本,这将非常有用。在这种情况下,在 .git / hooks / post-receive
脚本中添加此命令( hooks / post-receive
在裸存储库上,这在这种情况下更合适)
在添加前缀(例如目录名称)时将git导出到zip存档:
git archive master --prefix=directoryWithinZip/ --format=zip -o out.zip
如果您还需要子模块,这应该可以解决问题: https:/ /github.com/meitar/git-archive-all.sh/wiki
我的.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
}