250

恐怕我找不到像这种特殊情况的任何东西。

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

我知道我想要成为新存储库根的提交。但是,我无法找出正确的 git mojo 来截断 repo 以从该提交开始。我猜一些变种

git filter-branch

涉及移植物是必要的;可能还需要处理我们要单独保留的 200 多个分支中的每一个,然后将 repo 重新修补在一起(我确实知道该怎么做)。

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

4

12 回答 12

187

发布回复可能为时已晚,但由于此页面是谷歌的第一个结果,它可能仍然有帮助。

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

; 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 不支持从浅仓库克隆/推送/拉取。

于 2016-01-16T16:51:42.700 回答
129

您可以将新根提交的父级移植到无父级(或空提交,例如存储库的真正根提交)。例如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它使其永久化。

注意:在执行过滤器分支步骤后,所有提交 ID 都将更改,因此使用旧仓库的任何人都不得与使用新仓库的任何人合并。

于 2011-02-05T19:46:20.287 回答
74

这种方法很容易理解并且效果很好。脚本 ( $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

注意旧标签仍然存在;所以您可能需要手动删除它们

备注:我知道这与@yoyodin 几乎相同,但这里有一些重要的额外命令和信息。我试图编辑答案,但由于这是对@yoyodin 答案的重大更改,我的编辑被拒绝了,所以这里是信息!

于 2014-05-21T15:41:47.400 回答
58

试试这个方法如何截断 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 实现并不完美。如果您使用它,请手动检查结果。

于 2011-07-25T11:17:32.470 回答
36

作为重写历史的替代方法,请考虑使用来自Pro Git书籍的本文中的git replaceas 。讨论的示例涉及替换父提交以模拟树的开始,同时仍将完整历史记录作为单独的分支进行保管。

于 2012-10-26T19:17:06.383 回答
29

如果您想保留上游存储库的完整历史记录,但本地较小的签出,请使用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 后远程拒绝(不允许进行浅层更新)。如果你想这样做,你必须坚持嫁接。

于 2016-05-08T22:21:05.780 回答
23

我需要阅读几个答案和其他一些信息才能了解我在做什么。

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

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

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

于 2017-05-01T06:33:56.873 回答
3

rebasepushhead/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

或者

git rebase --onto temp $1 master
于 2017-01-03T15:55:54.437 回答
2

这里有太多的答案不是最新的,有些没有完全解释后果。以下是我使用最新的 git 2.26 缩减历史记录的方法:

首先创建一个虚拟提交。此提交将显示为您截断的 repo 中的第一个提交。您需要这个,因为此提交将保存您保留的历史记录的所有基本文件。SHA 是您要保留的提交的上一个提交8365366的 ID(在本例中为)。字符串 '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

这将首先将所有文件 as-of commit8365366放入 dummy commitd10f750中。然后它将播放8365366之后d10f750的所有提交。最后master,分支指针将更新为上次播放的提交。

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

需要记住的几件事(这些适用于其他方法以及这一方法):标签不会被转移。虽然保留了提交 ID 和时间戳,但您将看到 GitHub 以一次性标题显示这些提交,例如Commits on XY date.

幸运的是,可以将截断的历史记录保留为“存档”,稍后您可以将修剪后的存储库与存档存储库结合起来。为此,请参阅本指南

于 2020-05-18T09:59:19.373 回答
0

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

git clone --depth=1 ...

做就是了

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

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

于 2021-09-15T06:27:50.620 回答
-3

根据 BFG 工具的 Git 存储库,它“像 git-filter-branch 一样删除大的或麻烦的 blob,但速度更快 - 并且是用 Scala 编写的”。

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

于 2017-08-07T14:11:38.757 回答
-10
  1. 删除 git 数据,rm .git
  2. 混帐初始化
  3. 添加一个 git 远程
  4. 力推
于 2015-01-22T05:26:22.080 回答