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

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

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

git filter-branch

涉及到移植是必要的;可能还需要分别处理我们想要保留的200多个分支中的每个分支,然后将回购补丁重新组合在一起(我确实知道如何做)。

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


当前回答

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

这就是解决方案:

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

其他回答

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

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

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

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

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

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

当rebase或push到head/master时,可能会发生此错误

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仪表板中的这个问题,应该从“受保护的分支”中删除主分支

然后可以执行该命令

git push -f origin master

or

git rebase --onto temp $1 master

如何截断git历史记录:

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

Here $1 is SHA-1 of the commit you want to keep and the script will create new branch that contains all commits between $1 and master and all the older history is dropped. Note that this simple script assumes that you do not have existing branch called temp. Also note that this script does not clear the git data for old history. Run git gc --prune=all && git repack -a -f -F -d after you've verified that you truly want to lose all history. You may also need rebase --preserve-merges but be warned that the git implementation of that feature is not perfect. Inspect the results manually if you use that.

这里有太多不是当前的答案,有些并不能完全解释后果。下面是我使用最新的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之后的所有提交。最后,主分支指针将被更新到最后一次提交回放。

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

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

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