代码之家  ›  专栏  ›  技术社区  ›  slhck

如何还原更改了所有文件的特定提交,从而在还原过程中导致大量冲突?

git
  •  1
  • slhck  · 技术社区  · 8 年前

    在协作存储库中,有人进行了一次提交,以某种方式使其看起来像是重新添加了存储库中的所有文件。

    这是历史的一部分 --date-order :

    * 28cbf861 - (65 minutes ago) update logs and metadata (HEAD -> master, origin/master, origin/HEAD)
    * e589776e - (18 hours ago) P2LTR15 update
    ...
    * d4c61147 - (5 days ago) Delete P2STR03_SRC00150_Q0448_0000_0-7.log
    * fa837509 - (5 days ago) Delete P2STR03_SRC00150_Q0427_0000_0-7.log
    *   9a5e2300 - (5 days ago) git pull at 20180421 10am.
    |\
    * | 6567df4b - (5 days ago) add md5 3min
    * | 6f7c80f7 - (5 days ago) Added md5sum files for all 3min SRCs
     /
    * b97e834f - (6 days ago) Delete P2STR13_SRC00605_Q0325_0000_0-9.log
    * 5cd9989b - (6 days ago) Delete P2STR13_SRC00605_Q0129_0000_0-9.log
    * 769ae25d - (6 days ago) Delete P2STR13_SRC00605_Q0209_0000_0-9.log
    ...
    

    错误的提交是 6f7c80f7 ,我想删除它,但保留 6567df4b 这个 git pull 提交( 9a5e2300 )是一个简单的合并提交。

    当我打印历史记录时 --日期订单 ,突然间 6f7c80f7 显示在最底部,即好像这是有史以来的第一次提交。

    * 28cbf861 - (70 minutes ago) update logs and metadata
    * e589776e - (18 hours ago) P2LTR15 update
    ...
    * fa837509 - (5 days ago) Delete P2STR03_SRC00150_Q0427_0000_0-7.log
    *   9a5e2300 - (5 days ago) git pull at 20180421 10am.
    |\
    | * b97e834f - (6 days ago) Delete P2STR13_SRC00605_Q0325_0000_0-9.log
    ...
    | * 697a103c - (7 months ago) initial commit
    * 6567df4b - (5 days ago) add md5 3min
    * 6f7c80f7 - (5 days ago) Added md5sum files for all 3min SRCs
    

    我试着 git revert 6f7c80f7 ,但它显示了很多我不知道如何解决的冲突,也就是说,当我 git status :

    On branch master
    Your branch is up to date with 'origin/master'.
    
    You are currently reverting commit 6f7c80f7.
      (fix conflicts and run "git revert --continue")
      (use "git revert --abort" to cancel the revert operation)
    
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
        deleted:    P2LTR15/.gitkeep
        deleted:    P2LTR15/MOS_P2LTR15_MO.csv
        ... (this is basically a list of almost all the files in the repository)
    
    Unmerged paths:
      (use "git reset HEAD <file>..." to unstage)
      (use "git add/rm <file>..." as appropriate to mark resolution)
    
        deleted by them: P2LTR15/P2LTR15.yaml
        deleted by them: P2LTR15/audioFrameInformation/P2LTR15_SRC00712_HRC021.afi
        ... (this is another list of files, I don't know why it is there, possibly those were actually changed)
    

    有没有其他方法可以让我重写Git历史,这样就不会发生这种错误的提交?

    我已经看过了 this guide 但它也只告诉我做一个恢复。那么,我是否应该在那之前尝试回到犯罪现场 cherry-pick commits 然后强制重写存储库?我认为这也不是一个好的选择。

    1 回复  |  直到 8 年前
        1
  •  2
  •   Community Mohan Dere    5 年前

    你说你的 git log 仅输出报价 部分 历史,但你也说承诺 6f7c80f7 添加 全部的 在引用的文本中, 6f7c80f7 从不显示它所连接的任何父提交。

    这表明 6f7c80f7 是一个 根提交: 一种初始提交,实际上,它添加了它显示的每个文件,因为“以前的”提交不存在,所以根据定义,每个文件在该提交中都必须是新的。

    图表 可以 具有多个根。这在Git提交图中有点不寻常,因为我们通常先签出一些现有的提交,然后进行更改,然后运行 git commit 保存新的完整快照。但还有其他方法:

    • 我们可以使用 git fetch 要获取不相关的存储库 git merge 具有 --allow-unrelated-histories 。如果Git版本足够旧, 合并分支 总是 允许不相关的历史记录,并且没有 --允许不相关的历史记录 标志:它只是在没有警告的情况下合并这些。(你可能会看到很多 add/add conflict 此处出现错误,但必须进行大量手动解决!)

      这增加了 他们的 另一个存储库的根提交到我们自己的提交图,它已经有了根提交,所以现在我们有两个根提交。

    • 我们可以使用 git checkout --orphan 设置我们自己的存储库状态,以便我们进行的下一次提交将是根提交。

      (如果我们 如果进行根提交,我们将遇到与“获取无关存储库”情况相同的合并问题。)

    • 我们可以不经意地或故意地摆弄Git内部,使用管道命令,如 git commit-tree 或者只是戳里面的文件 .git ,以创建新的根提交。

    在任何情况下,一旦我们有了这种状态,即有多个根,并且有一个合并提交合并了额外的根,然后在合并上构建了更多的提交,我们就有点陷入了这种情况。合并提交——在您的案例中, 9a5e2300 具有哈希ID 取决于 存在 二者都 提交链:一个指向原始根提交,另一个指向新根提交。任何具有 9a5e2300 作为父母在你的情况下,这只是一个承诺 fa837509 取决于 9a5e2300 ,因为它实际上将该哈希ID合并到自己的ID中 fa837509 (这里的图表中也只有一个提交)然后取决于 fa837509 ;如果图的其余部分是线性的,则每个子级依次取决于 它的 父ID,一直到最后。

    因此,如果要以某种方式摆脱额外的根提交,则合并提交需要具有不同的哈希ID,这意味着每个后续提交都将 而且 需要有不同的哈希ID。此外,如果 摆脱你自己 6f7c80f7 ,必须对其当前子级进行复制 6567df4b 另一个根提交!您也必须摆脱这个问题,这意味着没有任何合并提交功能 9a5e2300 要合并,那么您可能也希望放弃它,除非您希望以某种方式保留其 源快照 。然后需要从 fa837509 直到提示,并复制 那些 对不依赖于存在的新提交的提交 9a5e2300 ,但请使用您所做的任何具有合并提交中更新的源快照的提交。

    因此,这将导致您必须使用一个过程来避免两个根提交:

    • 在合并中复制快照 9a5e2300 到新的非合并提交。这将获得一个新的哈希ID(因为它是一个新的唯一提交)。把那个ID保存在某个地方。

    • 在中复制快照 fa837509 一个新的提交,它同样会获得一个新的哈希ID。新提交的父项将是您刚才保存的ID。将此ID保存在某个位置。

    • 在的子级中复制快照 fa837509 使用上一个副本作为新的父级提交。

    • 每次提交都要重复,直到到达提示。

    一旦您有了这个线性提交链,它不依赖于您想要摆脱的提交,那么您就可以简单地 停止使用 最初的承诺链 取决于您要删除的提交。如果你 还有其他人 这样做,就可以删除Git使用的名称 发现 那条旧链条,似乎那条旧链条从未存在过。使用 名称 对于他们来说,最终旧的承诺确实会被删除。(当所有的安全检查计时器都过期时,就会发生这种情况。这些安全检查的存在是为了让你在犯错误时可以找回旧的提交,但很快就会发现错误,通常是30天,这样Git就可以生成尚未连接到提交图中的对象,但是 14天内。正常的Git命令最坏也只能在几毫秒内将它们连接起来,因此14天几乎就是数千年。)

    以上说明 是可能的 按照您的建议执行(尽管您可能希望构建一个与我描述的略有不同的替换历史)。是否是 好主意 完全是另一个问题。重写历史的主要缺点是,无论您如何使用 git rebase 具有 git filter-branch 具有 The BFG Repo Cleaner 或者您自己想到的东西是,重写的存储库实际上是 存储库。或者至少,新的链条是新的:重写点之前的旧东西是一样的。你必须 拥有旧存储库副本的所有人 古老的 东西即使是 单身人士 保留旧的提交,并使用Git将旧的提交与新的提交进行融合(Git很乐意这样做), 所有旧的承诺都会回来 .现在你有了新老都挤在一起的快乐(?)存储库和您的问题越来越严重!

    将(部分或全部)你的承诺改写成新的历史并没有什么根本错误,你只需要(a)知道怎么做,(b)意识到“每个人都必须改变”的后果。如果没有其他人拥有您要替换的提交,那么这种重写是完全安全的 其他人员 其他Git存储库 这将使旧的承诺重新发挥作用。如果每个人 让老承诺理解这是如何运作的,并计划合作,这种历史重写是 主要地 安全:只有当有人犯错时才会出错(然后,取决于你自己的谨慎程度和/或偏执,可能只有他们才会犯错!)。当你有天真的用户,他们只是让Git默认 Borg-like action 集成了它遇到的每一项技术,这种重写会造成混乱。