【问题标题】:GIT: Is there an option to push and rewrite history safely?GIT:是否有安全推送和重写历史的选项?
【发布时间】:2020-09-10 04:44:25
【问题描述】:

我有本地历史记录,其中包含来自远程服务器的提交 (注意= 在提交哈希附近)

= 66346e97 (HEAD -> dev) Implement bank_statement view form
= add8964f Typo fix
< c632ef4e Store extra info while generating billed usage
< 73386b71 Send exception to e.konkov@tucha.ua
...
< a6657b24 (xtucha/dev) Code comment
< b5c7c485 Ignore invoices with same docn issued to different provider
| = 3a361741 (xxxxxx/dev) Implement bank_statement view form
| = 0ff85c07 Typo fix
|/  
o f0df4c90 Store info about where invoices are sended into databases and report

git 是否有一个选项可以让我安全地推送和重写历史记录?

或者我唯一的选择是将我的提交重新设置为 xxxxxx/dev 上游?

$git pull -v --rebase

并获取下一个历史记录:

< a65ed362 (HEAD -> dev) Store extra info while generating billed usage
< cdad7b17 Send exception to e.konkov@tucha.ua
...
< ae0ec2d9 Code comment
< 20503ea7 Ignore invoices with same docn issued to different provider
o 3a361741 (xxxxxx/dev) Implement bank_statement view form

【问题讨论】:

  • “安全地重写历史”是什么意思?

标签: git git-rebase git-push


【解决方案1】:

你采取的行动 (git pull --rebase) 是正确的,我认为最终结果包含你想要的一切。

您可以确认您的新头像 (a65ed362) 的最终内容与您之前的头像 (66346e97) 的内容相同:

git diff a65ed362 66346e97

在运行rebase 时,git 将检测目标分支上是否已经存在一些提交。

在您的图表中,本地分支上标有 = 的两个提交与 xxxxxx/dev 上的两个提交匹配——在您的情况下:它们也具有相同的消息。

当你的分支变基时,git 不会重播这些提交。

其他提交不会有相同的 hash(对于初学者来说:它们不会有相同的历史记录),但它们会带来与原始提交相同的 changes提交。

【讨论】:

    【解决方案2】:

    git 是否有一个选项可以让我安全地推送和重写历史记录?

    不完全是,不。 Git 拥有的是git push --force-with-lease

    请记住,Git finds 最初通过分支名称(或其他名称)提交,但真正重要的是哈希 ID。一个分支名称只存储一个哈希 ID:

    66346e97 (HEAD -> dev) Implement bank_statement view form
    

    这里,名称 HEAD 包含名称 dev,名称 dev 包含哈希 ID 66346e97(嗯,完整的 40 个字符版本;Git 显示缩写,例如前 8字符,以帮助我们这些可怜的人处理哈希 ID 的丑陋)。

    提交66346e97 本身包含上一个 提交的哈希ID add8964f。因此,在使用名称 dev 找到 66346e97 之后,Git 然后使用 66346e97 找到 add8964f。提交add8964f 然后允许Git 找到c632ef4e

    这里的一般方案是每个提交都包含其直接前任的原始哈希 ID。这是提交的元数据的一部分(相对于数据,它包含所有文件的快照)。如果每个提交都指向它的父级,那么我们只需要找到 last 提交的哈希 ID,就可以找到每个提交,只要它们在漂亮整洁的链中:

    ... <-F <-G <-H
    

    这里哈希H的提交是last,所以它指向哈希G的提交,它又指向哈希F的提交,以此类推. name master 可能包含提交 H 的实际哈希 ID,我们可以这样绘制:

    ...--F--G--H   <-- master
    

    如果有额外的分支,它们的提交可能最终指向H 本身:

    ...--F--G--H   <-- master
                \
                 I--J   <-- dev
    

    或者他们可能有提交指向F,或者两者都有:

           K--L   <-- rc1
          /
    ...--F--G--H   <-- master
                \
                 I--J   <-- dev
    

    在每种情况下,name 指向 last 提交,我们从该提交向后工作。当我们选择master 时,最后一次提交是H。当我们选择dev 时,最后一次提交是J。当我们选择rc1(候选发布)时,最后一次提交是L。在每种情况下,最后一次提交都会向后指向某个较早的提交。

    当我们执行“历史重写”操作时,例如将提交 K-L 重新定位到提交 H 之后,使用:

    git checkout rc1; git rebase master
    

    我们得到具有新的不同哈希 ID 的新提交:

                 K'-L'  <-- rc1
                /
    ...--F--G--H   <-- master
                \
                 I--J   <-- dev
    

    原来的两次提交并没有消失!它们仍在存储库中;他们只是没有分支名称了:

             K--L   ???
            /
           /     K'-L'  <-- rc1
          /     /
    ...--F--G--H   <-- master
                \
                 I--J   <-- dev
    

    但是,如果我们之前执行了 git push origin rc1,则提交 KL(无论这些哈希 ID 实际是什么)现在都存在于 另一个 Git 存储库中,位于我们的任何 URL以我们的名义origin。我们自己的 Git 记住这一点,通过将 他们的 名称 rc1 复制到 我们的 名称 origin/rc1。所以最后一张图看起来像这样:

             K--L   <-- origin/rc1
            /
           /     K'-L'  <-- rc1
          /     /
    ...--F--G--H   <-- master
                \
                 I--J   <-- dev
    

    他们的Git 仍然有他们的 rc1 指向提交L。他们还没有提交K'-L':我们只是现在才使用git rebase

    当使用各种查看器(包括您正在使用的查看器)时,我们可以我们的Git 将提交K'L' 标记为“等于”提交KL由于他们的精挑细选,这是我们刚刚运行git rebase 的结果。这就是您在 git log 和其他查看者中看到的内容。

    如果我们现在想通过 origin 调用另一个 Git,我们可以做以下三件事中的任何一件:

    • 如果需要(将需要)发送它提交K'-L',然后礼貌地要求它设置分支名称rc1指向提交L'。但是如果他们的rc1 仍然指向提交L,他们会拒绝,并告诉我们如果他们这样做,他们将失去提交K-L,可能永远丢失。当然,这正是我们希望他们做的事情,但是如果在我们考虑这个问题时,其他人向该链添加了一个新的提交怎么办?好吧,在这种情况下,它仍然会拒绝。

    • 1234563他们可能会服从。如果他们的rc1 之前指向L,那就是我们想要的。但如果其他人添加了新的提交,我们只是丢失了新的提交:不是我们想要的。
    • 幸运的是,有 --force-with-lease 选项,它可以满足我们的需要。

    如果我们使用git push --force-with-lease,我们的 Git 将:

    1. 使用名称 origin 中的 URL 调用他们的 Git
    2. 向他们提供提交 L',最终会发送提交 K'L',因为他们还没有提交;
    3. 向他们发送迄今为止最复杂的命令:

      • 我们认为您的名称rc1 包含哈希ID _____(填写我们origin/rc1 的空白,提交的哈希ID 为L)。
      • 如果正确,我们命令您将您的名字rc1 设置为指向提交_____(用L' 的哈希ID 填写空白)。如果不是,则拒绝此命令。
      • 无论如何,请告诉我们发生了什么。

    当我们得到回复时,我们可以立即判断他们是否他们的 rc1 指向提交L'。如果他们这样做了,一切都很好。如果他们没有,我们会得到一个提示:为什么:他们的rc1 是否指向其他提交,或者他们只是生我们的气并且无论如何拒绝更新他们的rc1? (如果他们在 GitHub 上并且他们已将 rc1 设置为“受保护”,我们会得到后者。如果我们是管理员,我们当然可以覆盖受保护状态。)

    如果他们的rc1 不再指向L,我们应该运行git fetch,以便我们更新自己的origin/rc1。然后我们可以弄清楚该怎么做:我们再次变基吗?我们要如何进行?一旦我们弄清楚并完成了它,我们就可以再做一个git push --force-with-lease。如果他们的rc1 没有移动,这个 可以工作。如果他们的rc1 在解决问题时移动了,我们会再次获取并重新开始——循环重复,直到我们完成工作的速度比其他任何人都快他们的rc1 在我们身上.

    所以:

    或者我唯一的选择是将我的提交重新设置为 xxxxxx/dev 上游?

    您应该决定您希望他们拥有哪些提交,以及您希望他们丢弃哪些提交。

    请注意,如果您确实说服 Git 改变事物......好吧,让我们暂时假设 Git 在 GitHub 上,只是为了具体。如果您确实说服 GitHub 上的 Git 更改他们的分支名称,以便他们丢失一些提交,那么您可能刚刚给第二个用户带来了问题 第三个 Git 存储库。也许第二个用户依赖于提交K-L0ff85c073a361741)。 second 用户很容易意外地重新引入那些原始提交,例如,通过使用 git merge

    因此,无论何时您要使用 git push --force,无论是否使用 -with-lease,您都需要确保其他 Git 存储库的所有用户都同意其他 Git 存储库的分支名称像这样移动。事先得到所有其他用户的同意,他们现在知道发生这种情况时该怎么做。

    如果您是其他 Git 存储库的唯一用户,您只需同意自己的意见。如果你不同意自己的观点,你可能应该和自己坐下来好好谈谈。 ?

    【讨论】:

    • 谈论它...?
    【解决方案3】:

    你可以尝试git checkout [your commit (example:0ff85c07)]然后git checkout -b [new branch name]

    然后您可以继续重写并从该新分支推送。

    【讨论】:

    • 你不回答3a361741f0df4c90..c632ef4e 提交发生了什么?新分支不会包含它们
    • 你可以阅读这个答案。我希望它可以帮助你stackoverflow.com/a/34501346/9749198 另外一种方式,我可以给出我的额外答案来跟踪每次提交的问题。步骤: 1. 签出您的哈希提交(例如:3a361741) 2. 现在您当前的分支是 3a361741。从此分支创建新分支(例如:fix-branch) 3. 现在您的当前分支是 fix-branch。您可以从此分支继续您的任务(重写或修复)。而不是提交并将更改推送到远程存储库。
    • 问题不是关于如何创建新分支。
    猜你喜欢
    • 2015-07-07
    • 2017-03-08
    • 2019-01-16
    • 2021-12-09
    • 2014-09-25
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 2013-08-07
    相关资源
    最近更新 更多