如何从git存储库中删除旧历史?

恐怕我找不到任何类似的场景。

我有一个有很多历史的git存储库:500多个分支,500多个标签,可以追溯到2007年年中。它包含大约19,500个提交。我们希望删除2010年1月1日之前的所有历史记录,使其更小,更容易处理(我们将在存档存储库中保留历史记录的完整副本)。

我知道我想要成为新存储库根的提交。然而,我不能找出正确的git mojo来截断回购以提交开始。我猜是某种变体

git filter-branch

涉及到移植是必要的;可能还需要分别处理我们想要保留的200多个分支中的每个分支,然后将repo修补到一起(我知道如何做)。

有人做过这样的事吗?我有git 1.7.2.3,如果这很重要的话。

169857 次浏览

注意:这个已经被弃用,而支持git replace

你可以创建一个你的新根提交的父节点的贪污(或者一个空的提交,例如你的存储库的真正的根提交)。例如echo "<NEW-ROOT-SHA1>" > .git/info/grafts

在创建嫁接后,它立即生效;你应该能够查看git log并看到不想要的旧提交已经消失:

$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <your.email@example.com>
Date:   Fri May 24 14:04:10 2013 +0200


Another message
 

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <your.email@example.com>
Date:   Thu May 23 22:27:48 2013 +0200


Some message

如果一切看起来都像预期的那样,你可以使用git filter-branch -- --all使其永久。

请注意:在执行filter-branch步骤之后,所有的提交id都将改变,因此任何使用旧回购的人都不能与使用新回购的人合并。

试试这个方法如何截断git历史:

#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp

这里$1是你想要保留的提交的SHA-1,脚本将创建一个新的分支,其中包含$1master之间的所有提交,所有旧的历史记录将被删除。注意,这个简单的脚本假设您没有名为temp的现有分支。还要注意,这个脚本不会清除旧历史的git数据。在确认你真的想要丢失所有历史记录后运行git gc --prune=all && git repack -a -f -F -d。你可能还需要rebase --preserve-merges,但要注意的是,该功能的git实现并不完美。如果使用该方法,则手动检查结果。

作为重写历史的替代方法,可以考虑像这篇文章来自Pro Git那样使用git replace。讨论的示例涉及替换父提交来模拟树的开始,同时仍然将完整的历史记录作为一个单独的分支进行安全保管。

这个方法很容易理解,工作正常。脚本的参数($1)是一个引用(标签,散列,…),指向你想要保存历史记录的提交。

#!/bin/bash
git checkout --orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch


# The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos

请注意旧标签将仍然存在;因此,您可能需要手动删除它们

备注:我知道这是几乎相同的aswer @yoyodin,但这里有一些重要的额外命令和信息。我试着编辑答案,但由于这是@yoyodin的答案的实质性变化,我的编辑被拒绝了,所以这是信息!

  1. 删除git数据,rm .git
  2. git init
  3. 添加一个git远程
  4. 力推动

也许现在回复已经太迟了,但由于这个页面是第一个谷歌的结果,它可能仍然有帮助。

如果你想在你的git回购中释放一些空间,但不想重新构建所有的提交(rebase或graft),并且仍然能够从拥有完整回购的人那里推/拉/合并,你可以使用git克隆 克隆(——深度参数)。

; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10


; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm origin

你可以通过以下步骤来降低你现有的回购:

; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow


; Manually remove all other branches, tags and remotes that refers to old commits


; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --prune=now     ; Will actually delete your data

如何删除所有git本地标签?< / >

Ps:旧版本的git不支持从/到浅回购的克隆/推/拉。

如果你想用完整的历史 保持 上游存储库,但本地签出较小,用git clone --depth=1 [repo]做一个浅克隆。

在执行提交后,您可以执行

  1. git fetch --depth=1来修剪旧的提交。这使得旧的提交及其对象不可访问。
  2. git reflog expire --expire-unreachable=now --all。使所有旧提交及其对象过期
  3. git gc --aggressive --prune=all来删除旧对象

另见如何删除本地git历史提交后?

注意,您不能将这个“浅”存储库推到其他地方:“不允许浅更新”。看到在更改Git远程URL后,远程拒绝(不允许浅更新)。如果你想做到这一点,你必须坚持嫁接。

变基头/主时,可能会发生此错误

remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@giturl:main/xyz.git'

要解决git仪表板中的这个问题,应该从“保护分支”中删除主分支

enter image description here

然后可以执行该命令

git push -f origin master

git rebase --onto temp $1 master

我需要阅读一些答案和其他信息来理解我在做什么。

1. 忽略超过某个提交时间的所有内容

文件.git/info/grafts可以为提交定义伪父类。只有一个提交id的行表示提交没有父节点。如果我们想说我们只关心最近的2000次提交,我们可以输入:

git rev-parse HEAD~2000 > .git/info/grafts

Git rev-parse给出了当前提交的第2000个父节点的提交id。如果存在,上面的命令将覆盖移植文件。首先检查它是否在那里。

2. 重写Git历史记录(可选)

如果你想把这个嫁接的假父结点变成真父结点,那么运行:

git filter-branch -- --all

它将改变所有提交id。这个存储库的每个副本都需要强制更新。

3.清理磁盘空间

我没有执行第2步,因为我希望我的副本与上游保持兼容。我只是想节省一些磁盘空间。为了忘记所有旧的提交:

git prune
git gc

替代方案:浅拷贝

如果你有另一个存储库的浅拷贝,只是想节省一些磁盘空间,你可以更新.git/shallow。但是要注意没有任何东西指向之前的提交。所以你可以运行这样的程序:

git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc

浅层的进入就像嫁接一样。但要注意不要同时使用移植物和浅层。至少,不要有相同的条目,它会失败。

如果仍然有一些指向旧提交的旧引用(标记、分支、远程头),它们将不会被清理,也不会节省更多的磁盘空间。

根据BFG工具的Git回购,它“像Git -filter-branch一样删除大的或麻烦的斑点,但更快——并且是用scala编写的”。

https://github.com/rtyley/bfg-repo-cleaner

这里有太多不是当前的答案,有些并不能完全解释后果。下面是我使用最新的git 2.26来精简历史的方法:

首先创建一个虚拟提交。此提交将作为截断后的repo中的第一个提交出现。您需要这样做是因为此提交将保存您所保存的历史记录的所有基础文件。SHA是你想要保留的提交的以前的承诺的ID(在本例中为8365366)。字符串'Initial'将作为第一次提交的提交消息显示。如果您使用的是Windows,请从Git Bash命令提示符中输入以下命令。

# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}

上面的命令将输出SHA,例如d10f7503bc1ec9d367da15b540887730db862023

现在只需输入:

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366

这将首先将所有提交8365366的文件放入虚拟提交d10f750中。然后它将在d10f750之上回放所有提交 8365366。最后master分支指针将被更新到最后一次提交回放。

现在,如果你想推这些截断的repo,只需执行git push -f

有几件事要记住(这些适用于其他方法以及这个方法):标签不会被传输。虽然提交id和时间戳被保留,但你会看到GitHub以总括标题显示这些提交,如Commits on XY date

幸运的是,它是可能保持截断历史作为“存档”,以后你可以加入回切回购与存档回购。要做到这一点,请参见本指南

对于以前用--depth克隆的现有存储库

git clone --depth=1 ...

只做

git pull --depth=1 --update-shallow

https://git-scm.com/docs/git-pull

在我的情况下,我想分成两个回购,保持历史记录,但清理日志历史从文件过滤出新的回购。

这就是解决方案:

PATHS=path_a path_b
git filter-branch -f --prune-empty --index-filter "git read-tree --empty
git reset \$GIT_COMMIT -- $PATHS " -- --all -- $PATHS

通过这种方式,我得到了一个具有完整提交日志历史的新回购,但仅用于我想保留的路径;

裁判:https://stackoverflow.com/a/56334887/2397613