32

我们当前的 repo 有数以万计的提交,并且一个新的克隆传输了近一个 gig 的数据(有很多 jar 文件在历史上已经被删除)。我们想通过创建一个新的 repo 来减少这个大小,该 repo 只保留 repo 中当前活动文件的完整历史记录,或者可能只是修改当前 repo 以清除已删除的文件历史记录。但我不确定如何在实际庄园中做到这一点。

我已经尝试过从 git 历史记录中删除已删除文件中的脚本:

for del in `cat deleted.txt`
do
    git filter-branch --index-filter "git rm --cached --ignore-unmatch $del" --prune-empty -- --all
    # The following seems to be necessary every time
    # because otherwise git won't overwrite refs/original
    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now
done;

但是鉴于我们在历史记录中有数万个已删除文件和数万次提交,运行脚本将需要很长时间。我在 2 小时前开始只为一个已删除的文件运行此命令,并且 filter-branch 命令仍在运行,它一次通过 40,000 多个提交中的每一个,这是在带有 SSD 驱动器的新 Macbook pro 上。

我还阅读了页面https://help.github.com/articles/remove-sensitive-data但这仅适用于删除单个文件。

有没有人能够做到这一点?我真的很想保留当前跟踪文件的历史记录,如果我们不能保留历史记录,我不确定节省空间的好处是否值得创建一个新的存储库。

4

5 回答 5

45

删除所有内容并恢复您想要的内容

与其一次删除一个文件列表,不如执行几乎相反的操作:删除所有内容并恢复您想要保留的文件。

像这样:

# for unix

$ git checkout master
$ git ls-files > keep-these.txt
$ git filter-branch --force --index-filter \
  "git rm  --ignore-unmatch --cached -qr . ; \
  cat $PWD/keep-these.txt | tr '\n' '\0' | xargs -d '\0' git reset -q \$GIT_COMMIT --" \
  --prune-empty --tag-name-filter cat -- --all
# for macOS

$ git checkout master
$ git ls-files > keep-these.txt
$ git filter-branch --force --index-filter \
  "git rm  --ignore-unmatch --cached -qr . ; \
  cat $PWD/keep-these.txt | tr '\n' '\0' | xargs -0 git reset -q \$GIT_COMMIT --" \
  --prune-empty --tag-name-filter cat -- --all

执行起来可能会更快。

清理步骤

一旦整个过程完成,然后清理:

$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now

# optional extra gc. Slow and may not further-reduce the repo size
$ git gc --aggressive --prune=now

比较之前和之后的存储库大小,应该表明有相当大的减少,当然只有触及保留文件的提交,加上合并提交——即使是空的(因为这就是 --prune-empty 的工作方式),将在历史记录中。

$GIT_COMMIT?

使用$GIT_COMMIT似乎引起了一些混乱,来自 git filter-branch 文档(强调添加):

始终使用 eval 命令在 shell 上下文中评估参数(由于技术原因,提交过滤器除外)。在此之前,$GIT_COMMIT 环境变量将被设置为包含正在重写的提交的 id

这意味着git filter-branch将在运行时提供变量,而不是您事先提供的。如果使用此 no-op filter 分支命令有任何疑问,可以证明这一点:

$ git filter-branch --index-filter "echo current commit is \$GIT_COMMIT"
Rewrite d832800a85be9ef4ee6fda2fe4b3b6715c8bb860 (1/xxxxx)current commit is d832800a85be9ef4ee6fda2fe4b3b6715c8bb860
Rewrite cd86555549ac17aeaa28abecaf450b49ce5ae663 (2/xxxxx)current commit is cd86555549ac17aeaa28abecaf450b49ce5ae663
...
于 2013-07-28T14:59:27.723 回答
20

基于 AD7six,保留重命名的文件历史记录。(您可以跳过初步可选部分)

可选的

删除所有遥控器:

git remote | while read -r line; do (git remote rm "$line"); done

删除所有标签:

git tag | xargs git tag -d

删除所有其他分支:

git branch | grep -v \* | xargs git branch -D

删除所有藏匿处

git stash clear

删除所有子模块配置和缓存

git config --local -l | grep submodule | sed -e 's/^\(submodule\.[^.]*\)\(.*\)/\1/g' | while read -r line; do (git config --local --remove-section "$line"); done
rm -rf .git/modules/

修剪未跟踪的文件历史记录,保留跟踪的文件历史记录和重命名

git ls-files | sed -e 's/^/"/g' -e 's/$/"/g' > keep-these.txt
git ls-files | while read -r line; do (git log --follow --raw --diff-filter=R --pretty=format:%H "$line" | while true; do if ! read hash; then break; fi; IFS=$'\t' read mode_etc oldname newname; read blankline; echo $oldname; done); done | sed -e 's/^/"/g' -e 's/$/"/g' >> keep-these.txt
git filter-branch --force --index-filter "git rm --ignore-unmatch --cached -qr .; cat \"$PWD/keep-these.txt\" | xargs git reset -q \$GIT_COMMIT --" --prune-empty --tag-name-filter cat -- --all
rm keep-these.txt
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
  • 前两个命令是列出跟踪文件和跟踪文件的旧名称,使用引号来保留带空格的路径。
  • 第三个命令是仅重写这些文件的提交。
  • 随后的命令是清除历史记录。

可选(不推荐)

重新打包(来自the-woes-of-git-gc-aggressive):

git repack -a -d --depth=250 --window=250
于 2015-11-23T14:18:08.307 回答
7

截至 2020 年 4 月,git使用时会产生以下警告git filter-branch

WARNING: git-filter-branch has a glut of gotchas generating mangled history
         rewrites.  Hit Ctrl-C before proceeding to abort, then use an
         alternative filtering tool such as 'git filter-repo'
         (https://github.com/newren/git-filter-repo/) instead.  See the
         filter-branch manual page for more details; to squelch this warning,
         set FILTER_BRANCH_SQUELCH_WARNING=1.

我确信有一种安全的使用方式git filter-branch,但对于那些(像我自己)不知道如何避免上述问题的git-filter-repo人来说,保留当前跟踪文件的历史记录非常容易:

$ git checkout master
$ git ls-files > /tmp/keep-these.txt
$ git filter-repo --paths-from-file /tmp/keep-these.txt

虽然git filter-branch在我的 repo 上运行了大约 5 分钟,git filter-repo但在不到一秒的时间内运行并重新打包了 repo!

可以按照其 GitHub 页面上的说明进行安装。或者,在 Mac 上,您可以只运行brew install git-filter-repo.

于 2020-04-08T18:39:55.297 回答
5

只运行一次 git filter branch

问题中的脚本将处理数千次提交,数千次 - 它每次迭代都会做各种(非常慢)的事情,通常你只会在最后做。这真的需要永远。

而是运行一次脚本,一次删除所有文件:

del=`cat deleted.txt`
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch $del" \
  --prune-empty --tag-name-filter cat -- --all

一旦该过程完成,然后清理:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now

# optional extra gc. Slow and may not further-reduce the repo size
git gc --aggressive --prune=now 

如果上述由于文件数量而失败

如果 deleted.txt 中有足够多的文件以致上述命令太大而无法运行,则可以将其重写为如下所示:

git filter-branch --force --index-filter \
  'cat /abs/path/to/deleted.txt | xargs git rm --cached --ignore-unmatch' \
  --prune-empty --tag-name-filter cat -- --all

(清理步骤相同)

这与上面的版本相同 - 但删除文件的命令一次删除一个文件,而不是一次删除所有文件。

于 2013-07-27T21:59:12.447 回答
0

添加到 AD7six 接受的答案(因为我没有足够的声誉来评论答案):

如果你想保留的不仅仅是大师,你可以

  1. 删除不再需要的标签和分支
  2. 然后创建要保留的所有这些分支和标签中引用的文件列表:
for tag in `git for-each-ref refs/tags --format='%(refname)' | cut -d / -f 3`
do
    echo $tag; sleep 3 # sleep to avoid: fatal: Unable to create '.git/index.lock': File exists.
    git checkout "$tag"
    git ls-files > ../keep_files_tag_$tag.txt
    git ls-files >> ../keep_files_all.txt
done
for branch in `git for-each-ref refs/heads --format='%(refname)' | cut -d / -f 3`
do
    echo $branch; sleep 3 # sleep to avoid: fatal: Unable to create '.git/index.lock': File exists.
    git checkout "$branch"
    git ls-files > ../keep_files_branch_$branch.txt
    git ls-files >> ../keep_files_all.txt
done
sort ../keep_files_all.txt | uniq > keep_files_uniqe.txt
于 2020-04-02T16:04:18.317 回答