我们当前的repo有成千上万的提交,一个新的克隆传输了几乎一堆数据(有很多jar文件已经被删除了历史记录)。我们希望通过创建一个新的repo来缩小此大小,该repo仅保留repo中当前活动的文件的完整历史记录,或者可能只修改当前repo以清除已删除的文件历史记录。但我不确定如何在实际庄园中做到这一点。
我已尝试过Remove deleted files from git history中的脚本:
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多个提交,这是一个新的Macbook pro SSD驱动器。
我还阅读了https://help.github.com/articles/remove-sensitive-data页面,但这仅适用于删除单个文件。
有没有人能够做到这一点?我真的想保留当前跟踪文件的历史记录,如果我们无法保留历史记录,我不确定节省空间的好处是否值得创建新的回购。
答案 0 :(得分:38)
不是一次删除一个文件列表do the almost-opposite,而是删除所有内容,然后只恢复要保留的文件:
# 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
比较之前和之后的存储库大小,应该表明相当大 减少,当然只有提交触摸保存的文件,加上合并 提交 - 即使是空的(because that's how --prune-empty works),也将在历史中。
使用$GIT_COMMIT
似乎引起了一些混淆,from the git filter-branch documentation(强调补充):
始终使用eval命令在shell上下文中评估参数(出于技术原因,提交过滤器有明显的例外)。在此之前, $ GIT_COMMIT环境变量将被设置为包含正在重写的提交的ID 。
这意味着git filter-branch
将在运行时提供变量,而不是您提供的。如果使用此无操作过滤器分支命令有任何疑问,可以证明这一点:
$ 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
...
答案 1 :(得分:12)
基于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
remove all submodules configuration and cache:
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
答案 2 :(得分:5)
问题中的脚本将处理成千上万次提交,数千次 - 并且每次迭代都会执行各种(非常慢)的事情,通常只会在最后执行。这真的需要永远。
而是运行一次脚本,一次删除所有文件:
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
(清理步骤相同)
这与上面的版本相同 - 但删除文件的命令一次只执行一次,而不是一次完成。
答案 3 :(得分:3)
从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
花了大约5分钟时间来运行我的存储库,而git filter-repo
却在不到一秒钟的时间里重新打包了存储库!
可以按照its GitHub page上的说明进行安装。另外,在Mac上,您只需运行brew install git-filter-repo
。
答案 4 :(得分:0)
添加到AD7six接受的答案中(因为我没有足够的声誉来评论答案):
如果您想保留更多的知识,那就可以
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