从.git目录中删除可以重新下载的所有信息

时间:2017-02-08 20:55:54

标签: git

我有一个git存储库,当刚签出时,即使在最浅的配置中也需要大约2.3 GiB,其中1.9 GiB在.git/objects/pack内。工作树文件只有.5 GiB。

考虑到我有一个遥控器,我可以根据需要重新获取所有对象,问题是:

  • 我可以从.git内部删除什么(以及如何),然后使用简单的git命令从遥控器安全地重新获取所有内容?

稍微测试一下,我发现如果我删除.git/objects/pack/下的所有内容,则会使用简单的git fetch从遥控器重新下载。

有一些抱怨如下:

error: refs/heads/master does not point to a valid object!
error: refs/remotes/origin/master does not point to a valid object!
error: refs/remotes/origin/HEAD does not point to a valid object!

但随后.git/objects/pack重新填充,并且git fetch的进一步电话不再抱怨。

像这样核对.git/objects/pack*这样安全吗?

假设:

  • repo中没有本地提交或任何形式的git操作(比如在舞台上添加/删除对象),只是以浅模式检出特定分支。
  • 遥控器不会重写已检出分支的历史记录。
  • 我无法控制远程存储库本身的内容。它是我的项目的依赖项,但是一个快速更改的项目只能作为git使用,我希望在持续集成设置中自动使用指令。关于如何修改存储库本身以减少占用空间的提示并没有帮助。
  • 正如我前面提到的,1.9 GiB是我感兴趣的一个分支的浅层克隆。由于它的历史悠久(开源项目已超过10年),它比非浅的时候要大得多。
  • 在同一个连续集成管道中检查了其他存储库,我希望在所有存储库中应用相同的冗余远程信息减少。

目的是尽可能地减少工件从连续集成管道中占用的空间量,但保留足够的信息,以便可以在开发人员工作站中将这些工件下载并恢复到正常工作状态(和正常情况下)命令。

3 个答案:

答案 0 :(得分:0)

删除.git/中的内容只是打破了局面。它包含项目的完整历史记录,这些包文件是git如何节省空间的。有很多,更好的方法来减少你的回购的大小。

首先是运行垃圾收集,git gc。这将做很多事情来减少磁盘上存储库的大小。你不应该这样,它会定期运行,但它可能会有所帮助。

如果没有,请尝试浅层克隆,只能获得历史记录的一部分。这只会克隆来自master的最新100次提交。

git clone --depth=100 <remote>

同样,你可以克隆一个分支。

git clone --single-branch --branch master <remote>

稍后可以使用git-fetch“深化”这些内容。

但最好的办法是减少回购的大小。 Git在太空中非常有效,而且演出非常多。它表明存储库中有很多非常大的二进制文件,图像,视频,电子表格和压缩文件...... git无法有效压缩。要处理此问题,有两个工具git-lfs(大文件支持)和BFG Repo Cleaner

git-lfs允许您将旧版本的大型文件存储在云存储中,而不是存储在每个人的.git目录中。这可以极大地减少存储库的大小......继续。

BFG Repo Cleaner可让您轻松重写历史记录,包括删除大文件的选项。

将它们组合在一起,您可以使用BFG Repo Cleaner将现有的大文件更改为使用git-lfs。这可以极大地减少存储库的大小。例如,这会将所有*.mp4更改为使用git-lfs。

$ java -jar ~/bfg-1.12.15.jar --convert-to-git-lfs '*.mp4' --no-blob-protection

Instructions for that can be found here

另一个重要的事情是 不压缩文件 。你提到了持续集成工件,我愿意打赌它们是压缩的。 Git将自己进行更高效的压缩,并且它可以确保历史记录中只有一个文件的副本,但它只能在文本上执行。在提交之前解压缩tarball和zipfiles。

如果绝对无法减小存储库的大小,那么剩下的选择就是让每个人共享一个.git目录。您可以使用--git-dir选项或设置GIT_DIR

来执行此操作
git --git-dir=/path/to/the/.git log

这是 糟糕的 主意。虽然每个人都可以拥有自己的结账,但他们都将共享相同的存储库状态。如果一个开发者进行了更改,其他开发人员将会看到它,但现在使用不同的工作目录。

例如,dev1添加了一个文件。

$ touch this
$ GIT_DIR=~/tmp/foo/.git git add this
$ GIT_DIR=~/tmp/foo/.git git st
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   this

然后dev2突然看到了这一点。

$ GIT_DIR=~/tmp/foo/.git git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   this

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    this

他们共享相同的暂存区域,但不是相同的工作副本。开发者将不断绊倒对方。

如果git clone --depth=1仍然产生太大的回购,那么每次结账时只会有很多数据。你无能为力。如果浅层克隆上的.git是2演出,那么结帐将会更大。

至于在.git上进行手术的想法,也许你可以通过删除一些对象来逃避,并希望git fetch --deepen可以修复它,但是在多个开发者中保持这个...这是一个维护噩梦。

此时,您可以完全删除.git。现在您已经有效地导出了最新的提交。 There's various ways to do this directly

或者只是停止浪费时间和金钱并购买更大的硬盘。花在这上面的每个人小时都是你可以购买的硬盘。

答案 1 :(得分:0)

  

尽可能地减少工件从连续集成管道中占用的空间量,但保留足够的信息,以便可以在开发人员工作站中将这些工件下载并恢复到正常工作状态(和正常情况一样) )命令尽可能

我不完全了解您的情况,但一种经常被遗忘的减少网络数据大小和服务器内存使用的方法是:

  • 分发一些稳定的存储库(其中只包含未重写的分支),然后
  • 在克隆时使用--reference <path>

在正常的开发条件下(文本文件,并非每次提交都更新它们的所有fof),它比使用浅克隆更有效。

至于你提出的问题,我认为尝试保存从存储库中删除任何内容是没有意义的。大多数数据用于包装,这是必需的,休息是无关紧要的。

PS:存储库只能通过git本身在临时存储中初始化:

CACHE_REPO=/tmp/repo
if ![ -d "$CACHE_REPO" ]; then
  git clone --single-branch --no-checkout --branch=_BRANCH_ _REMOTE_ "$CACHE_REPO"
fi

_BRANCH_master或您肯定不会强行推进的其他分支。你可以试着把它变浅,它可能会也可能不会起作用,我不确定。

答案 2 :(得分:0)

  
      
  • 我可以从内部删除什么(以及如何).git我可以使用简单的git命令从遥控器安全地重新获取所有内容吗?
  •   

一切怎么样?

如果您不想担心.git的内部以及某些内容是否可恢复,您可以保存足够的信息以再次检查它,并将工作区恢复到功能类似的状态比在CI中运行时管道

在C.I.管道

在这样的文件中添加(让我们称之为degit.sh

#!/bin/bash
set -ex
GIT_REMOTE=$( git remote get-url origin )
GIT_BRANCH=$( git rev-parse --abbrev-ref HEAD )
GIT_COMMIT=$( git rev-parse HEAD )

# TABs, not spaces, indenting the block below:
cat <<-EOF > .gitrestore
    set -ex
    test ! -e .git
    tmpclone=\$( mktemp -d --tmpdir=. )
    git clone $GIT_REMOTE -n --branch=$GIT_BRANCH \$tmpclone
    ( cd \$tmpclone ; git reset --hard $GIT_COMMIT )
    mv \$tmpclone/.git .
    rm -rf "\$tmpclone"
    rm -f \$0
EOF

rm -rf .git

然后,在您的Continous Integration(C.I。)工作空间的每个git repos的根目录中,调用它以便生成.gitrestore文件。

看起来像这样:

set -ex
test ! -e .git
tmpclone=$( mktemp -d --tmpdir=. )
git clone git@example.com:example/repo.git -n --branch=example-branch $tmpclone
mv $tmpclone/.git .
git reset --hard example-commit-hash
rm -rf "$tmpclone"
rm -f $0

请注意,成功运行后它会自行破坏。你不想运行它两次。

在Developer Machine

现在您的开发人员可以获取C.I.每个存储库中的工件和运行:

bash .gitrestore

它的存储库看起来非常像C.I.管道有,除了遥控器的更新视图,它允许开发人员比较C.I.她拥有的东西。

其他注意事项

这假设只有C.I.机器受空间限制,而不是开发人员机器(她的带宽都没有)。

如果要在开发人员端节省空间/带宽,可以传递--depth=1,它将仅克隆指定的分支(即,它隐含--single-branch并将历史记录限制为单个提交。