【问题标题】:How do I remove the old history from a git repository?如何从 git 存储库中删除旧历史记录?
【发布时间】:2011-05-29 18:31:06
【问题描述】:

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

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

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

的一些变体
git filter-branch

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

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

【问题讨论】:

    标签: git git-filter-branch git-reflog git-gc


    【解决方案1】:

    也许现在回复太晚了,但由于此页面是 Google 的第一个结果,它可能仍然有用。

    如果你想在你的 git repo 中释放一些空间,但不想重建你所有的提交(rebase 或 graft),并且仍然能够从拥有完整 repo 的人那里推送/拉取/合并,你可以使用git clone shallow 克隆(--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
    

    How to remove all git local tags?

    Ps:旧版本的 git 不支持从浅仓库克隆/推送/拉取。

    【讨论】:

    • +1 这是较新版本 Git 的正确答案。 (哦,请回到PPCG!)
    • @Trogdor 答案应该是cd limitedRepo,因为这是您需要删除对不存在来源的引用的地方。我已提交修改。
    • @Jez 那将是另一个投票最多的答案。如果你想永久摆脱历史,这个答案不适合你。它用于处理 庞大的历史。
    • 回答我自己的问题:git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02 就像一个魅力!
    • @Jez 您可以通过运行git filter-branch -- --all 将您的浅层回购转换为普通回购。这将更改其中的所有哈希值,但之后您将能够将其推送到新的仓库
    【解决方案2】:

    您可以创建新根提交的父级的graft 到无父级(或空提交,例如存储库的真正根提交)。例如。 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 都将更改,因此任何使用旧 repo 的人都不得与任何使用新 repo 的人合并。

    【讨论】:

    • 我必须通过git filter-branch --tag-name-filter cat -- --all 来更新标签。但我也有旧标签指向我想删除的旧历史。我怎样才能摆脱所有这些旧标签?如果我不删除它们,那么旧的历史记录不会消失,我仍然可以通过 gitk --all 看到它。
    • “只需将您的新根提交的父级移植到无父级”需要一些详细说明。我试过了,但没能弄清楚“没有父母”的语法。手册页声称需要父提交 ID;使用全零只会给我一个错误。
    • 如果其他人想知道它是如何工作的,这很容易:echo "&lt;NEW-ROOT-HASH&gt;" &gt; .git/info/grafts
    • 我同意,解释什么是移植物不仅有用
    • 引自链接的 wiki 页面上的移植物。 “从 Git 1.6.5 开始,添加了更灵活的 git replace ,它允许您用任何其他对象替换任何对象,并通过可以在 repos 之间推送和拉取的 ref 跟踪关联。”所以这个答案可能对于当前版本的 git 来说已经过时了。
    【解决方案3】:

    这个method 很容易理解并且运行良好。脚本的参数 ($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 答案的重大更改,我的编辑被拒绝了,所以这里是信息!

    【讨论】:

    • 我很欣赏对git prune 和git gc 命令的解释。脚本中的其余命令是否有解释?就目前而言,尚不清楚传递给它的参数是什么以及每个命令在做什么。谢谢。
    • @user5359531 感谢您的评论,我为每个命令添加了更多的 cmets。希望这会有所帮助。
    • 到处合并冲突...不是很有用
    • 我完全遵循了这一点,我得到的只是与以前相同的历史记录,从我想要修剪的提交开始的新分支具有与以前相同的历史记录。未删除任何历史记录。
    【解决方案4】:

    试试这个方法How to truncate git history:

    #!/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,脚本将创建包含$1 和master 之间的所有提交的新分支,并且所有较旧的历史记录都将被删除。请注意,这个简单的脚本假定您没有名为temp 的现有分支。另请注意,此脚本不会清除旧历史记录的 git 数据。在您确认您确实想要丢失所有历史记录后运行git gc --prune=all &amp;&amp; git repack -a -f -F -d。您可能还需要rebase --preserve-merges,但请注意该功能的 git 实现并不完美。如果您使用它,请手动检查结果。

    【讨论】:

    • 我试过了,但在rebase 步骤中遇到了合并冲突。奇怪——我没想到在这种情况下会发生合并冲突。
    • 如果您签出的提交不包含任何文件,请使用git commit --allow-empty -m "Truncate history"。
    • 如何将其推送回远程主机?当我这样做时,我最终会得到新旧历史。
    • “温度”应该是什么?您应该为此传递什么论据?当您实际运行这些命令时,是否有一个示例说明它们应该是什么样子?谢谢。
    • @CraigMcQueen 尝试使用git rebase -p --onto temp $1 master(使用-p)。这保留了合并提交,并且应该避免合并冲突。否则 rebase 会尝试扁平化合并提交。
    【解决方案5】:

    作为重写历史记录的替代方法,请考虑使用git replace,如this article from the Pro Git book。讨论的示例涉及替换父提交以模拟树的开始,同时仍将完整历史记录作为单独的分支保存。

    【讨论】:

    • 是的,我想你可以用它来做我们想做的事,如果你也对单独的完整历史分支进行核对的话。 (我们试图缩小存储库。)
    • 我对不在现场的答案感到沮丧;但它确实链接到 GitScm 站点,并且它链接到的教程写得很好,似乎直接指向了 OP 的问题。
    • @ThorSummoner 对不起!我将在现场更全面地开发答案
    • 不幸的是,这不是重写历史的替代方法。文章开头有一个令人困惑的句子,可能给人这样的印象。可以从这个答案中删除吗?您将在文章中看到作者确实重写了截断分支的历史,但提出了一种使用git replace 重新附加遗留“历史”分支的方法。我相信这已在您发布此答案的另一个问题上得到纠正。
    • git replace 与 git graft 的讨论在 stackoverflow.com/q/6800692/873282 进行
    【解决方案6】:

    如果您想保留具有完整历史记录的上游存储库,但本地较小的签出,请使用git clone --depth=1 [repo] 进行浅层克隆。

    push 提交后,你可以这样做

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

    另见How to remove local git history after a commit?。

    请注意,您不能将此“浅层”存储库推送到其他地方:“不允许浅层更新”。见Remote rejected (shallow update not allowed) after changing Git remote URL。如果你想这样做,你必须坚持嫁接。

    【讨论】:

    • 第 1 点。对我来说很重要。干杯
    【解决方案7】:

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

    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
    

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

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

    【讨论】:

    【解决方案8】:

    当 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
    

    或

    git rebase --onto temp $1 master
    

    【讨论】:

      【解决方案9】:

      这里有太多不是最新的答案,有些没有完全解释后果。以下是我使用最新的 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。然后它将回放所有提交之后 8365366 超过d10f750 的顶部。最后master 分支指针将更新为上次播放的提交。

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

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

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

      【讨论】:

        【解决方案10】:

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

        git clone --depth=1 ...
        

        做事

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

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

        【讨论】:

          【解决方案11】:

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

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

          【讨论】:

            【解决方案12】:
            1. 删除 git 数据,rm .git
            2. git 初始化
            3. 添加 git 远程
            4. 强制推送

            【讨论】:

            • 这将有助于删除所有历史记录,但不能满足他的要求:保留自 2010 年 1 月以来的历史记录
            • 只是想说谢谢,因为它在我的场景中帮助了我,即使这可能不是问题的正确答案
            猜你喜欢
            • 2019-03-17
            • 2017-06-16
            • 2015-06-04
            • 1970-01-01
            • 2021-01-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多