在 master 分支上意外执行 "Git push origin master"
Accidentally executed "Git push origin master" while on branch off master
我创建了一个本地 Git 主分支分支(我们称之为 BranchB)。然后我在那个分支上提交了我的更改(到 BranchB)。但是到了推送到远程存储库的时候,我喝了太多咖啡,我写了 git push origin master
而不是 git push
来将新分支推送到存储库。
然后我进入 GitHub 查看 master 上的提交。我最近不小心推到master的改动这里没有出现?
那么当我在分支 B 上推送到 master 时到底发生了什么? 这是否只是推送自从我指定 master 以来在 master 上完成的提交。
您在此处作为推送参数提供的 <refspec>
是 master
,其中完整的规范形式应该是 <src>:<dst>
,<src>
是推送和 <dst>
目标参考。当您省略一个时,git 假定您的参数是 <src>
并且没有给出 <dst>
。在这种情况下,如果分支有一个 <repository>.push
配置集(在大多数情况下如此),that ref 将用作您的推送源。
所以是的,它将 master(没有新提交)推送到它的远程副本。无害的空操作。
(也许用 git config -l | grep origin
或类似的东西检查你自己的配置。)
您会 已将 BranchB 推送到 master 上,并带有显式 :
git push origin BranchB:master
这是我从文档中了解到的,即 git-push 手册页中的 the <refspec>
paragraph。您的结果似乎很有意义。
简短回答:你很好。 Git 最有可能说:
Everything up-to-date
什么也没做。如果不是,您只将在 master
上所做的提交发送到您的 GitHub Git 的 master
.
较长的答案仍然是你很好,但你可能在对 Git 分支的一些误解下工作。他们不会咬你的……还。
提交形成向后指向的链
每个 Git 提交都由其哈希 ID 唯一标识。给定哈希 ID,Git 可以找到提交。提交内部是另一个哈希 ID — 提交的 parent 提交,即在此提交之前的提交。当然,在提交中还有提交者的姓名和电子邮件地址,以及他们的日志消息,以及(虽然它有点间接)构成该提交的所有文件的完整快照。
这意味着从任何给定的提交,我们都可以倒退到上一个提交。因此,如果我们有一系列提交:
... <-F <-G <-H
我们只需要最后这样的提交的哈希ID,例如H
。 Git 将能够读取 H
并找出其父 G
的哈希 ID,读取 G
并找出 F
的 ID,等等。
分支 name 像 master
因此只标识一个提交
Git 查找任何分支的 last 提交的哈希 ID 的方式是通过分支名称。名称本身,master
或 BranchB
或其他名称,仅包含哈希 ID:
...--F--G--H <-- master, BranchB
此处两个名称存储相同哈希ID。因此 master
上的所有提交也在 BranchB
.
上
要进行 new 提交,Git 将保存快照,保存您的姓名和电子邮件等,并保存 [=146] 的哈希 ID =] 当前 提交 H
。这成为一个新的提交 I
,它被分配了新的唯一哈希 ID:
...--F--G--H <-- master, BranchB
\
I
现在必须更改其中一个名称!
您的 HEAD
会记住您所在的分支
为了知道要更新哪个名称,Git 将特殊名称HEAD
附加到一个分支。如果 HEAD
附加到 BranchB
,那是 Git 将更新的名称:
...--F--G--H <-- master
\
I <-- BranchB (HEAD)
就是这么简单
快进
嗯,"that simple" 没那么简单:分支名称 do 像这样移动,然后 Git 找到 通过从最晚开始并向后工作来提交。您可以随时要求 Git 将任何分支名称移动到任何现有提交。假设我们强制 Git 将 BranchB
移回提交 H
。我们如何找到提交 I
?
如果我们这样做,就很难找到I
。 (有很多机制,但我们先不用担心它们。)简单的部分是,只要分支名称移动 forward——随着我们所做的新提交,一个在一次,或者我们选择的一堆提交也向前推进,我们很好,因为从过去 I
的某个地方回到 I
,我们总是可以回到 I
.
这就是Git的特殊术语快进的意思。如果从 new "last" 提交,我们仍然可以回到旧的 "last" 提交,则分支名称更新是一个快进操作。
推(终于!)
这就是 git push
发挥作用的地方。当您 运行 git push
时,您的 Git 会调用其他 Git,在某些 URL。您的 Git 和他们的 Git 进行了对话。您的 Git 将根据需要将新提交移交给他们的 Git,然后您的 Git 要求他们的 Git 设置 他们的 [=176] 之一=] 分支名称。
请记住,他们的 Git 是一个 Git 存储库,就像您的一样。所以他们的 Git 有 他们自己的 分支名称。他们已经有 master
:
...--F--G--H <-- master
您的 Git 将调用他们的 Git 并询问他们:嘿,为什么不将您的 master
设置为指向提交 H
? 他们会说:它已经做到了。 你的会说:哦,好的,再见! 还有你的Git 将打印:
Everything up-to-date
或者,您可以发送一些新的提交哈希 ID。您的 Git 将向他们的 Git 提供您的新提交,只要 link 这个新提交返回 H
所需的数量,或者您的 Git 和他们的 Git 共享一些较早的提交。然后你的 Git 将作为他们的 Git:嘿,将你的 master
设置为另一个哈希 ID 怎么样? 他们可能会说 好的,否则他们可能会说:不!那不是快进! 现在你知道快进是什么意思,你知道如果他们拒绝你的请求,那是因为无论提交什么 他们 master
名称,这不在您查看 您的 master
并向后工作时得到的反向链中。
也就是说,假设您先发制人——其他人在其他地方向他们发送了新的提交:
...--F--G--H--L <-- master
您的 Git 做出了一些新的提交(并且您在 master
上):
J--K <-- master (HEAD)
/
...--F--G--H <-- origin/master
\
I <-- BranchB
所以您的 Git 向他们发送 J-K
链并要求他们将 master
设置为 K
,但这会丢失他们的 L
—你甚至没有。你必须得到他们的 L
并弄清楚如何保持他们的 L
.
只要他们没有任何你的 git push
请求会丢弃的提交,他们会接受你的请求。所以如果他们没有有L
,而你运行git push origin master
,你会发送你的J-K
并要求他们将他们的 master 设置为 K
他们会的。
即使您已将 HEAD
重新附加到 BranchB
也是如此:
J--K <-- master
/
...--F--G--H <-- origin/master
\
I <-- BranchB (HEAD)
A git push origin master
让你的 Git 调用他们的 Git,向他们提供你的提交 K
,并要求他们将他们的 master
移到那里,你的 master
在哪里。
A git push origin BranchB
让你的 Git 调用他们的 Git,向他们提供你的提交 I
,并要求他们移动(或创建)他们的 BranchB
那里。
请注意,您还可以 git push origin master BranchB
向他们提供两个提示提交(上图中的 K
和 I
),并提出两个请求。或者,如 ,您可以为单词 origin
之后的每个单词设置完整的 refspec 参数,例如 git push BranchB:master
或 git push master:BranchB
.这些让你的 Git 像以前一样向他们发送你的提交,然后请求他们设置他们的目标分支名称(冒号后面的名称)以匹配你的源分支的提示提交(从冒号之前的名称)。通常,混合这样的名称不是一个好主意——如果没有别的,它会过于混乱。但在某些特殊情况下,它是一个有用的快捷方式。
我创建了一个本地 Git 主分支分支(我们称之为 BranchB)。然后我在那个分支上提交了我的更改(到 BranchB)。但是到了推送到远程存储库的时候,我喝了太多咖啡,我写了 git push origin master
而不是 git push
来将新分支推送到存储库。
然后我进入 GitHub 查看 master 上的提交。我最近不小心推到master的改动这里没有出现?
那么当我在分支 B 上推送到 master 时到底发生了什么? 这是否只是推送自从我指定 master 以来在 master 上完成的提交。
您在此处作为推送参数提供的 <refspec>
是 master
,其中完整的规范形式应该是 <src>:<dst>
,<src>
是推送和 <dst>
目标参考。当您省略一个时,git 假定您的参数是 <src>
并且没有给出 <dst>
。在这种情况下,如果分支有一个 <repository>.push
配置集(在大多数情况下如此),that ref 将用作您的推送源。
所以是的,它将 master(没有新提交)推送到它的远程副本。无害的空操作。
(也许用 git config -l | grep origin
或类似的东西检查你自己的配置。)
您会 已将 BranchB 推送到 master 上,并带有显式 :
git push origin BranchB:master
这是我从文档中了解到的,即 git-push 手册页中的 the <refspec>
paragraph。您的结果似乎很有意义。
简短回答:你很好。 Git 最有可能说:
Everything up-to-date
什么也没做。如果不是,您只将在 master
上所做的提交发送到您的 GitHub Git 的 master
.
较长的答案仍然是你很好,但你可能在对 Git 分支的一些误解下工作。他们不会咬你的……还。
提交形成向后指向的链
每个 Git 提交都由其哈希 ID 唯一标识。给定哈希 ID,Git 可以找到提交。提交内部是另一个哈希 ID — 提交的 parent 提交,即在此提交之前的提交。当然,在提交中还有提交者的姓名和电子邮件地址,以及他们的日志消息,以及(虽然它有点间接)构成该提交的所有文件的完整快照。
这意味着从任何给定的提交,我们都可以倒退到上一个提交。因此,如果我们有一系列提交:
... <-F <-G <-H
我们只需要最后这样的提交的哈希ID,例如H
。 Git 将能够读取 H
并找出其父 G
的哈希 ID,读取 G
并找出 F
的 ID,等等。
分支 name 像 master
因此只标识一个提交
Git 查找任何分支的 last 提交的哈希 ID 的方式是通过分支名称。名称本身,master
或 BranchB
或其他名称,仅包含哈希 ID:
...--F--G--H <-- master, BranchB
此处两个名称存储相同哈希ID。因此 master
上的所有提交也在 BranchB
.
要进行 new 提交,Git 将保存快照,保存您的姓名和电子邮件等,并保存 [=146] 的哈希 ID =] 当前 提交 H
。这成为一个新的提交 I
,它被分配了新的唯一哈希 ID:
...--F--G--H <-- master, BranchB
\
I
现在必须更改其中一个名称!
您的 HEAD
会记住您所在的分支
为了知道要更新哪个名称,Git 将特殊名称HEAD
附加到一个分支。如果 HEAD
附加到 BranchB
,那是 Git 将更新的名称:
...--F--G--H <-- master
\
I <-- BranchB (HEAD)
就是这么简单
快进
嗯,"that simple" 没那么简单:分支名称 do 像这样移动,然后 Git 找到 通过从最晚开始并向后工作来提交。您可以随时要求 Git 将任何分支名称移动到任何现有提交。假设我们强制 Git 将 BranchB
移回提交 H
。我们如何找到提交 I
?
如果我们这样做,就很难找到I
。 (有很多机制,但我们先不用担心它们。)简单的部分是,只要分支名称移动 forward——随着我们所做的新提交,一个在一次,或者我们选择的一堆提交也向前推进,我们很好,因为从过去 I
的某个地方回到 I
,我们总是可以回到 I
.
这就是Git的特殊术语快进的意思。如果从 new "last" 提交,我们仍然可以回到旧的 "last" 提交,则分支名称更新是一个快进操作。
推(终于!)
这就是 git push
发挥作用的地方。当您 运行 git push
时,您的 Git 会调用其他 Git,在某些 URL。您的 Git 和他们的 Git 进行了对话。您的 Git 将根据需要将新提交移交给他们的 Git,然后您的 Git 要求他们的 Git 设置 他们的 [=176] 之一=] 分支名称。
请记住,他们的 Git 是一个 Git 存储库,就像您的一样。所以他们的 Git 有 他们自己的 分支名称。他们已经有 master
:
...--F--G--H <-- master
您的 Git 将调用他们的 Git 并询问他们:嘿,为什么不将您的 master
设置为指向提交 H
? 他们会说:它已经做到了。 你的会说:哦,好的,再见! 还有你的Git 将打印:
Everything up-to-date
或者,您可以发送一些新的提交哈希 ID。您的 Git 将向他们的 Git 提供您的新提交,只要 link 这个新提交返回 H
所需的数量,或者您的 Git 和他们的 Git 共享一些较早的提交。然后你的 Git 将作为他们的 Git:嘿,将你的 master
设置为另一个哈希 ID 怎么样? 他们可能会说 好的,否则他们可能会说:不!那不是快进! 现在你知道快进是什么意思,你知道如果他们拒绝你的请求,那是因为无论提交什么 他们 master
名称,这不在您查看 您的 master
并向后工作时得到的反向链中。
也就是说,假设您先发制人——其他人在其他地方向他们发送了新的提交:
...--F--G--H--L <-- master
您的 Git 做出了一些新的提交(并且您在 master
上):
J--K <-- master (HEAD)
/
...--F--G--H <-- origin/master
\
I <-- BranchB
所以您的 Git 向他们发送 J-K
链并要求他们将 master
设置为 K
,但这会丢失他们的 L
—你甚至没有。你必须得到他们的 L
并弄清楚如何保持他们的 L
.
只要他们没有任何你的 git push
请求会丢弃的提交,他们会接受你的请求。所以如果他们没有有L
,而你运行git push origin master
,你会发送你的J-K
并要求他们将他们的 master 设置为 K
他们会的。
即使您已将 HEAD
重新附加到 BranchB
也是如此:
J--K <-- master
/
...--F--G--H <-- origin/master
\
I <-- BranchB (HEAD)
A git push origin master
让你的 Git 调用他们的 Git,向他们提供你的提交 K
,并要求他们将他们的 master
移到那里,你的 master
在哪里。
A git push origin BranchB
让你的 Git 调用他们的 Git,向他们提供你的提交 I
,并要求他们移动(或创建)他们的 BranchB
那里。
请注意,您还可以 git push origin master BranchB
向他们提供两个提示提交(上图中的 K
和 I
),并提出两个请求。或者,如 origin
之后的每个单词设置完整的 refspec 参数,例如 git push BranchB:master
或 git push master:BranchB
.这些让你的 Git 像以前一样向他们发送你的提交,然后请求他们设置他们的目标分支名称(冒号后面的名称)以匹配你的源分支的提示提交(从冒号之前的名称)。通常,混合这样的名称不是一个好主意——如果没有别的,它会过于混乱。但在某些特殊情况下,它是一个有用的快捷方式。