Git 自行分支

Git branching on its own

我一直在我作为独立贡献者的存储库中使用 git。存储库托管在 Github 上。出于某种原因,git 偶尔似乎在 Github 上分支,而我没有做任何改变。我只在一台计算机上更改此 repo,并且不在 Github 上修改任何东西。然而,正如您在 git log --oneline --decorate --graph --all 的这个缩短的选择中看到的那样,我似乎不得不随机执行此操作。提交 271056c 在 Github 的 cfacddf 处重复。

这可能是什么原因造成的?这是一个错误吗?

我现在考虑的可能理论:这可能是 git commit --amend 的结果吗?

git log --oneline --decorate --graph --all 的输出:

*   d030d88 (HEAD -> master, origin/master, origin/HEAD) Merge branch 'master' of ***
|\
| * cfacddf Fixed a few things:
* | 798afef modified:   tasks.txt
* | 2c19ea0 Paginated:
* | 271056c Fixed a few things:
|/
* 63a429c Added lock icon to annotation headers

git log 的输出:

commit d030d8820456b0f129d2d3e5167ed88f87ebe028
Merge: 798afef cfacddf
Author: ***
Date:   Wed Oct 3 16:49:00 2018 -0400

    Merge branch 'master' of https://github.com/malan88/icc

commit 798afefabe24027a955853771072755c08318b47
Author: Michael Alan <michaelalantarpon@gmail.com>
Date:   Wed Oct 3 16:48:54 2018 -0400

    modified:   tasks.txt

commit 2c19ea0b735d148f0057852c196254c4258c2c6b
Author: ***
Date:   Wed Oct 3 16:42:20 2018 -0400

    Paginated:

    - Author index
    - Book index
    - User Annotations
    - Tag index

commit 271056c1c34444dbb3b8c2b6a67efae67f27b44b
Author: ***
Date:   Wed Oct 3 15:24:14 2018 -0400

    Fixed a few things:

    - hash_id bug
    - line_number_form on read.html bug
    - horribly inefficient permissions check on line edits on read.html
    - Added lock change mechanism for when admin doesn't want to edit
        annotation
    - cleaned up some bad whitespace
    - Bug that didn't include query parameters in next parameter (e.g.
    pagenumbers, etc)

commit cfacddfb5395e0fa3676fbc1e19457c45c7c3529
Author: ***
Date:   Wed Oct 3 15:24:14 2018 -0400

    Fixed a few things:

    - hash_id bug
    - line_number_form on read.html bug
    - horribly inefficient permissions check on line edits on read.html
    - Added lock change mechanism for when admin doesn't want to edit
        annotation
    - cleaned up some bad whitespace
    - Bug that didn't include query parameters in next parameter (e.g.
    pagenumbers, etc)

commit cfacddfb5395e0fa3676fbc1e19457c45c7c3529
Author: ***
Date:   Wed Oct 3 15:24:14 2018 -0400

    Fixed a few things:

    - hash_id bug
    - line_number_form on read.html bug
    - horribly inefficient permissions check on line edits on read.html
    - Added lock change mechanism for when admin doesn't want to edit
        annotation
    - cleaned up some bad whitespace

commit 63a429c828cec06fa30e4994f3912fab387c7a4c
Author: ***
Date:   Wed Oct 3 15:21:58 2018 -0400

    Added lock icon to annotation headers

让我们从这里开始:

Possible theory I now consider: could it be the result of git commit --amend?

是。

现在让我们回到这个,因为这里有一个隐藏的假设,这是一个关键因素:

I have been using git in a repository for which I am the solo contributor. The repository is hosted on GitHub.

这不太对。您在这里的意思是 a 存储库位于 GitHub 上。它不是 存储库:它是几个或多个存储库之一。在本例中,它是两者之一。另一个在你自己的机器上!

要获得正确的解释,请参阅我的许多较长答案中的任何一个。另请参阅网站 Think Like (a) Git,其中有很多重要的背景信息。但简而言之,请记住这里有两个存储库:您的存储库和 GitHub 上的 Git 存储库。

当您进行提交时,它由其哈希 ID 唯一标识 - 一大串难看的十六进制数字,例如 cfacddfb5395e0fa3676fbc1e19457c45c7c3529。这就是您的 Git 存储在您的 b运行ch 名称 master 中的内容。每个提交还存储其前一个或 parent、提交的哈希 ID。合并提交是至少有两个 parent 的提交。当 b运行ch 名称或提交存储提交的哈希 ID 时,我们说此名称或此提交 指向 目标提交。

我们可以绘制诸如(水平方向)图表之类的东西,最新的提交向右:

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

Git 将名称 HEAD 附加到一个 b运行ch 名称,这样如果您有多个 b运行ch 名称,它就知道哪个是您当前的 b 运行通道。提交中的箭头不能改变——任何提交中的任何东西都不能永远改变——所以我们真的不需要绘制内部箭头:

...--F--G--H   <-- master (HEAD)

当你 运行 git commit 时,正常的程序是将索引中的任何内容冻结到一个新的快照中,使快照的 parent 成为 当前提交,使用来自 HEAD 附加的 b运行ch 名称的指针:

...--F--G--H--I

然后 Git 将 new 提交的哈希 ID 放入当前的 b运行ch 名称中,以便它现在指向新的提交:

...--F--G--H--I   <-- master (HEAD)

git commit --amend 的工作原理

我们刚刚看到 git commit:

  1. 将当前索引冻结到快照中;
  2. 写出一个新的提交,当前提交作为它的 parent;
  3. 将新提交的哈希写入当前 b运行ch。

--amend所做的只是更改为更改步骤2:不是使用当前提交作为新提交的parent,--amend使用当前提交的parents 作为新提交的 parents。 (如果有多个 parents——如果当前提交是一个合并——新提交获取当前提交的所有 parents。如果是正常的 single-parent non-merge提交,新提交得到一个新的 parent。)所以我们得到:

而不是从 ...-F-G-H...-F-G-H-I
          H
         /
...--F--G--I   <-- master (HEAD)

也就是说,--amend 将当前提交推开

如果你没有推送,这很好用

如果您从未将提交 H 推送到任何地方,现在修改它可以正常工作。新提交 I 接管; commit H 消失了,再也见不到了。 (您可以在自己的存储库中找到它一段时间——默认情况下至少 30 天,直到记住它的 reflog 条目过期。但它不会出现在正常的 git log 输出中。)

但是如果你有

它会失败

但是如果你已经将提交H发送到另一个Git——比如你存储在GitHub上的存储库——那么任何时候你联系 他们,他们都会再次向你提交 H,因为这是对 他们 [=22] 的重要提交=]b运行ch。这就是本案中发生的事情。

您已将提交 H 推送到 GitHub。然后你让你的 Git 把你的 H 推开,如上所述,但是 GitHub 仍然有 H。您的 Git 记住了这一点,但如果您的 Git 忘记了或者您删除了它的记忆,您的 Git 会在您下次 Git 调用 Git 在 Git 中心。所以现在你有了这个:

          H   <-- origin/master
         /
...--F--G--I   <-- master (HEAD)

从现在开始,当您在您的存储库中工作时,提交 H 仍然可见。您提交了 J (271056c1c34444dbb3b8c2b6a67efae67f27b44b) 和 K (798afefabe24027a955853771072755c08318b47):

          H   <-- origin/master
         /
...--F--G--I--J--K   <-- master (HEAD)

此时,你 运行 git pull,你的 Git 在 GitHub 调用 Git 并从中获取任何新提交GitHub(有 none)并更新你的 origin/master 以匹配他们的 master(仍然指向提交 H)。你的 git pull 然后让你的 Git 合并 他们的提交 H 和你的提交 K,进行新的 two-parent 合并提交 L:

          H_  <-- origin/master
         /  `----__
...--F--G--I--J--K-=L   <-- master (HEAD)

这就是您现在看到的。 (好吧,现在 你还要求 GitHub Git 将他们的 master 设置为 L,他们这样做了,所以你的自己的 Git 存储库的 origin/master 也转到 L。)

要修复它,您必须让所有其他 Git 忘记 H

在你之后"amend" H,你必须找到所有其他 Git 有 H 的存储库,并说服他们改用 I。在这种情况下,只涉及另一个 Git:位于 GitHub 的那个。你可以 运行:

git push origin master

您的 Git 在 Git 中心调用 Git 并礼貌地请求他们更改 master 以指向您的替换提交 I.但是如果你这样做,你会发现他们说不,我不会那样做,因为那样会失去commit H. 这种抱怨的形式是:

! [rejected]        master -> master (non-fast-forward)

在这种情况下,您知道问题出在哪里,并且您希望他们丢失(忘记)提交H。所以你可以使用 git push --force 或更高级的变体 git push --force-with-lease.

这些将您的请求(请更新您的master)转换为命令:更新您的master GitHub 仍然可以拒绝(如果您没有被授权这样做,也会拒绝),但一般来说,他们会服从他们拒绝请求的命令。 --force-with-lease 选项用作安全检查:而不只是说 无论如何都要这样做 ,你的 Git 对他们说:我认为你有你的 master 设置为 。如果是这样,请更换它。如果没有,请告诉我。 这是您可以在共享存储库上使用的内容,以防其他人也推送到 GitHub master.