如何减少现有git克隆的深度?

时间:2016-07-03 16:21:09

标签: git

我有一个克隆。我想减少它的历史,而不是从头开始克隆,减少深度。工作的例子:

$ git clone git@github.com:apache/spark.git
# ...
$ cd spark/
$ du -hs .git
193M    .git

好的,所以不是这样,但它会为这次讨论服务。如果我尝试gc它会变小:

$ git gc --aggressive
Counting objects: 380616, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (278136/278136), done.
Writing objects: 100% (380616/380616), done.
Total 380616 (delta 182748), reused 192702 (delta 0)
Checking connectivity: 380616, done.
$ du -hs .git
108M    .git

尽管如此,相当大(git pull表明它仍然可以向遥控器推/拉)。重新包装怎么样?

$ git repack -a -d --depth=5
Counting objects: 380616, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (95388/95388), done.
Writing objects: 100% (380616/380616), done.
Total 380616 (delta 182748), reused 380616 (delta 182748)
Pauls-MBA:spark paul$ du -hs .git
108M    .git
是的,没有变小。 - 重新包装的深度与克隆相同:

$ git clone --depth 1 git@github.com:apache/spark.git
Cloning into 'spark'...
remote: Counting objects: 8520, done.
remote: Compressing objects: 100% (6611/6611), done.
remote: Total 8520 (delta 1448), reused 5101 (delta 710), pack-reused 0
Receiving objects: 100% (8520/8520), 14.82 MiB | 3.63 MiB/s, done.
Resolving deltas: 100% (1448/1448), done.
Checking connectivity... done.
Checking out files: 100% (13386/13386), done.
$ cd spark
$ du -hs .git
17M .git

Git pull说,它仍然与遥控器同步,这让任何人都感到惊讶。

好的 - 那么如何将现有的克隆更改为浅层克隆,而不是将其复制并重新检查?

4 个答案:

答案 0 :(得分:11)

git clone --mirror --depth=5  file://$PWD ../temp
rm -rf .git/objects
mv ../temp/{shallow,objects} .git
rm -rf ../temp

这真的不是“从头开始”克隆,因为它纯粹是本地工作,它实际上只创建了浅层包装文件,可能总计几十千字节。我冒昧地说你不会比这更有效率,你最终将使用脚本和测试工作形式使用更多空间的自定义工作,而不是以几kb的临时仓库开销的形式

答案 1 :(得分:7)

因为至少git版本2.14.1有

git fetch --depth 10

这将从原点获取最新的提交(如果有的话),然后将本地历史记录切断(或延长)到10的深度。

切断提交将无法通过常规方式访问,但仍会在存储库中停留(reflog)。如果没有其他引用持有它们,那么最终将通过自动git gc删除它们。

您也可以立即删除旧的提交。为此,您必须删除可能包含它们的所有引用。这主要是reflog和标签。然后运行git gc

请注意,reflog会在一段时间后自行清除,但标记会永远保留。因此,如果要从旧提交中回收磁盘空间,则必须手动删除标记。

如果您删除了标记,则下一个git fetch将仅重新获取当前存储库中提交的标记。

清除reflog:

git reflog expire --expire=all --all

删除所有代码:

git tag -l | xargs git tag -d

删除所有悬空物体:

git gc --prune=all

答案 2 :(得分:2)

编辑,2017年2月:这个答案现在已经过时/错误。 Git 可以使浅层克隆更浅,至少在内部。 Git 2.11也有--deepen来增加克隆的深度,看起来似乎有最终计划允许负值(尽管现在它们被拒绝)。目前还不清楚它在现实世界中的效果如何,最好的办法仍然是克隆克隆,如jthill's answer中那样。

您只能加深存储库。这主要是因为Git是围绕添加新东西构建的。浅克隆的工作方式是你的(接收)Git让发送者(另一个Git)停止发送"新东西"在达到浅克隆深度参数时,与发送者协调,以便理解为什么他们已经停止在那一点,即使显然需要更多历史记录。然后他们写出"截断的"的ID。提交到一个特殊文件.git/shallow,它将存储库标记为浅,并记录哪些提交被截断。

请注意,在此过程中,您的Git仍然添加新内容。 (此外,当它完成克隆并退出时,Git会忘记深度是什么,随着时间的推移,甚至无法弄清楚它是什么。所有Git都知道这个一个浅层克隆,因为包含提交ID的.git/shallow文件仍然存在。)

Git的其余部分继续以此为基础构建#34;添加新内容"概念,所以你可以加深克隆,但不要增加它的浅薄。 (对此没有好的,同意的动词:深化坑的反面就是填充它,但 fill 有错误的含义。 Diminish 可能工作;我想我会用它。)

理论上,git gc,这是Git中唯一实际抛出任何东西的部分, 1 可能会减少存储库,甚至可以将完整的克隆转换为浅的克隆,但没有人编写代码来做到这一点。有一些棘手的问题,例如,你丢弃标签吗?由于实现原因,Shallow克隆开始没有标记,因此将存储库转换为浅,或者减少现有的浅存储库,可能会要求丢弃至少一些标记。当然,任何指向由减少的动作消除的提交的标签都必须去。

同时,--depth的{​​{1}}参数(从git-pack-objects传来)完全意味着其他内容:它是的最大长度 delta链,当Git在每个pack-file中存储的Git对象上使用其修改后的xdelta compression。这与提交DAG的特定部分的深度无关(从每个分支头部计算)。

1 好吧,git repack最终将事情视为副作用,具体取决于使用的是哪个标志,但是这样通过调用 em> git repackgit gc也是如此。要使这两个命令真正正常工作,首先需要git prune运行。 "普通用户"清理事物序列的结尾是git reflog expire;它处理所有这些。所以我们可以说git gc就是你丢弃积累的新东西"事实证明这毕竟是不受欢迎的。

答案 3 :(得分:0)

好的,这是尝试打击它,忽略非默认分支,并假设遥控器被称为'起源':

#!/bin/sh

set -e

mkdir .git_slimmer

cd $1

changed_lines=$(git status --porcelain | wc -l)
ahead_of_remote=$(git status | grep "Your branch is ahead" | wc -l)
remote_url=$(git remote show origin  | grep Fetch | cut -d' ' -f5)
latest_sha=$(git log | head -n 1 | cut -d' ' -f2)

cd ..

if [ "$changed_lines" -gt "0" ]
then
  echo "Untracked Changes - won't make the clone slimmer in that situation"
  exit 1
fi

if [ "$ahead_of_remote" -gt "0" ]
then
  echo "Local commits not in the remote - won't make the clone slimmer in that situation"
  exit 1
fi

cd .git_slimmer
git clone $remote_url --no-checkout --depth 1 foo
cd foo
latest_sha_for_new=$(git log | head -n 1 | cut -d' ' -f2)
cd ../..

if [ "$latest_sha" == "$latest_sha_for_new" ]
then
  mv "$1/.git" "$1/.gitOLD"
  mv ".git_slimmer/foo/.git" "$1/"
  rm -rf "$1/.gitOLD"
  cd "$1"
  git add .
  cd ..
else
  echo "SHA from head of existing get clone does not match the latest one from the remote: do a git pull first"
  exit 1
fi

rm -rf .git_slimmer

使用:' git-slimmer.sh< folder_containing_git_repo>'