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

ebn*_*ter 188 git git-filter-branch

我恐怕找不到任何类似这种特殊场景的东西.

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

我知道我想要成为新存储库的根目录的提交.但是,我不能找出正确的git mojo来截断repo以从那个提交开始.我猜的是一些变种

git filter-branch
Run Code Online (Sandbox Code Playgroud)

涉及移植是必要的; 它也可能是必要的对待每一个我们要分别保持在200多个分支机构,然后修补回购重新走到一起(这是我知道该怎么做).

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

ape*_*arr 112

只需创建新根提交的父级的移植到没有父级(或者空提交,例如存储库的真正根提交).例如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
Run Code Online (Sandbox Code Playgroud)

如果所有看起来都是预期的,你可以做一个简单git filter-branch -- --all的使其永久化.

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

  • "只需创建一个新的根提交的父级的移植到没有父级"需要一些详细说明.我试过了,但未能弄清楚"没有父母"的语法.手动页面声明需要父提交ID; 使用全零只是给我一个错误. (9认同)
  • 我不得不做`git filter-branch --tag-name-filter cat - --all`来更新标签.但我也有旧的标签指向我想要删除的旧历史.如何摆脱所有旧标签?如果我不删除它们,那么较旧的历史记录不会消失,我仍然可以使用`gitk --all`看到它. (6认同)
  • 如果其他人想知道它是如何工作的,那很简单:`echo"<NEW-ROOT-HASH>"> .git/info/grafts` (6认同)
  • 我同意,解释什么是移植物将是有用的 (3认同)
  • 这似乎并没有真正删除旧的提交; 他们可以在git-log中查看并签出. (3认同)
  • 引自关于移植物的链接维基页面."从Git 1.6.5开始,添加了更灵活的git替换,它允许您用任何其他对象替换任何对象,并通过refs跟踪关联,可以在repos之间推送和拉动." 因此,对于当前版本的git,这个答案*可能会过时. (3认同)
  • 当你想缩小你的收缩时,你可能想要交叉检查http://stackoverflow.com/questions/7654822/remove-refs-original-heads-master-from-git-repo-after-filter-branch-tree-filte存储库大小. (2认同)
  • 有人可以解释这意味着什么吗?"只需创建一个新的root提交的父级的移植到没有父级(或者是一个空的提交,例如你的repo的真正root提交)." (2认同)
  • 这绝对不再起作用`$ git replace --convert-graft-file``提示:不支持&lt;GIT_DIR&gt; / info / grafts``提示:并且将在以后的Git版本中删除。 :提示:请使用“ git replace --convert-graft-file”提示:将嫁接转换为替换引用。`提示:`提示:通过运行提示关闭此消息。 config advisor.graftFileDeprecated false”,并且不会出现`git replace --convert-graft-file`具有预期的效果。 (2认同)

Ale*_* T. 101

发布回复可能为时已晚,但由于此页面是Google的第一个结果,因此它可能仍然有用.

如果你想在你的git仓库中释放一些空间,但又不想重建你所有的提交(rebase或者贪污),并且仍然可以从拥有完整仓库的人推/拉/合并,你可以使用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
Run Code Online (Sandbox Code Playgroud)

您可以通过以下步骤来浅显现有仓库:

; 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
Run Code Online (Sandbox Code Playgroud)

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

  • +1对于较新版本的Git,这是**正确的答案.(哦,请回到[PPCG](http://codegolf.stackexchange.com)!) (9认同)
  • 你如何`cd`到刚被删除的文件夹?我觉得这里有一些缺失的信息.此外,有没有办法将这些更改应用于远程仓库? (6认同)
  • @Jez这将是另一个获得最高投票的答案。如果您想永久摆脱历史,此答案不适合您。它适用于*具有*悠久的历史。 (3认同)
  • 回答我自己的问题:`git clone file:/// Users / me / Projects / myProject myClonedProject --shallow-since = 2016-09-02`就像一个吊饰! (3认同)
  • @jez,您可以通过运行`git filter-branch---all`将您的浅仓库转换为普通仓库。这将更改其中的所有哈希,但之后您将可以将其推送到新的回购 (3认同)
  • @Trogdor答案应该是`cd limitedRepo`,因为那是你需要删除对不存在原点的引用.我已经提交了一个编辑. (2认同)
  • 当我尝试将这个浅表克隆推到一个* new *仓库时(我想这样做是因为我想摆脱仓库的历史,并开始一个历史更短的新仓库),我从Gitlab收到了一个错误不允许更新。有一种方法可以将浅表克隆转换为正常回购,而无需再次恢复所有额外的历史记录。 (2认同)

Chr*_*aes 57

这种方法易于理解并且工作正常.script($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
Run Code Online (Sandbox Code Playgroud)

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

评论:我知道这与@yoyodin几乎相同,但这里有一些重要的额外命令和信息.我试着编辑答案,但由于@ yoyodin的答案是一个重大变化,我的编辑被拒绝了,所以这里是信息!

  • 合并各地的冲突......不是很有用 (3认同)
  • @Warpzit 我通过在 `rebase` 命令中添加 `-p` 来消除合并冲突,正如其他答案中所建议的 (3认同)
  • @ user5359531感谢您的评论,我为每个命令添加了一些评论.希望这可以帮助. (2认同)
  • 我完全遵循了这一点,我得到的只是与以前相同的历史记录,一个新分支从我想要修剪的提交开始,具有与以前相同的历史记录。没有删除任何历史记录。 (2认同)

yoy*_*dyn 49

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

#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp
Run Code Online (Sandbox Code Playgroud)

这里$1是SHA-1的承诺,你要保持和脚本将创建一个包含之间的所有提交新的分支$1,并master与所有的旧的历史将被丢弃.请注意,此简单脚本假定您没有调用现有分支temp.另请注意,此脚本不会清除旧历史记录的git数据.运行git gc --prune=all && git repack -a -f -F -d您确认后,你真正想失去所有的历史.您可能还需要rebase --preserve-merges警告该功能的git实现并不完美.如果使用,请手动检查结果.

  • 我试过这个,但在`rebase`步骤中遇到了合并冲突.奇怪 - 我没想到在这种情况下合并冲突是可能的. (20认同)
  • @CraigMcQueen 尝试使用`git rebase -p --onto temp $1 master`(使用`-p`)。这会保留合并提交并应避免合并冲突。否则 rebase 会尝试扁平化合并提交。 (3认同)
  • 如果您签出的提交不包含任何文件,请使用`git commit --allow-empty -m"截断历史记录". (2认同)
  • 如何将其推回远程主站?当我这样做时,我最终得到了新旧历史. (2认同)

Jef*_*ica 33

作为替代,以改写历史,可以考虑使用git replace本文来自临Git的.讨论的示例涉及替换父提交以模拟树的开头,同时仍将完整历史记录保存为单独的分支以便安全保存.

  • 我对不在现场的回答感到气馁;但它确实链接到 GitScm 站点,并且它链接到的教程写得很好,似乎直接指向了 OP 的问题。 (2认同)
  • 在 http://stackoverflow.com/q/6800692/873282 上讨论了“git replace”与“gitraft” (2认同)

kop*_*por 22

如果你想保持上游与仓库全部历史记录,但地方小签,做一浅克隆用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后,请参阅远程拒绝(不允许浅更新).如果你想这样做,你必须坚持嫁接.


小智 16

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

1.忽略比某个提交更旧的所有内容

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

git rev-parse HEAD~2000 > .git/info/grafts
Run Code Online (Sandbox Code Playgroud)

git rev-parse为我们提供了当前提交的第2000个父级的提交ID.如果存在,上面的命令将覆盖移植文件.检查它是否在那里.

2.重写Git历史(可选)

如果你想让这个嫁接的假父母成为真正的父母,那么运行:

git filter-branch -- --all
Run Code Online (Sandbox Code Playgroud)

它将更改所有提交ID.需要有力地更新此存储库的每个副本.

3.清理磁盘空间

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

git prune
git gc
Run Code Online (Sandbox Code Playgroud)

替代方案:浅拷贝

如果您有另一个存储库的浅表副本并且只想保存一些磁盘空间,则可以更新.git/shallow.但要小心,没有任何东西指向之前的提交.所以你可以运行这样的东西:

git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc
Run Code Online (Sandbox Code Playgroud)

浅入口就像贪污一样.但要注意不要同时使用移植物和浅层.至少,那里没有相同的条目,它将失败.

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


Shi*_*hah 6

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

首先创建一个虚拟提交。此提交将显示为截断存储库中的第一个提交。您需要这个,因为此提交将保存您所保留的历史记录的所有基本文件。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}
Run Code Online (Sandbox Code Playgroud)

上面的命令将打印 SHA,例如d10f7503bc1ec9d367da15b540887730db862023.

现在只需输入:

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


归档时间:

查看次数:

110218 次

最近记录:

6 年,9 月 前