新复制,仅复制当前跟踪文件的历史记录

时间:2013-07-27 19:22:24

标签: git

我们当前的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页面,但这仅适用于删除单个文件。

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

5 个答案:

答案 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?

使用$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

remove all stashes

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)

仅运行一次git filter分支

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

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

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接受的答案中(因为我没有足够的声誉来评论答案):

如果您想保留更多的知识,那就可以

  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