在我们的.mjs
分支中,我使用develop
创建了一个新分支。进行我的更改,提交并使用git checkout -b jsm/logging
推送到原始位置。进行了PR,并合并并删除了 remote 分支。再次进行调整,并用git push -u origin HEAD
修改了我的上一次提交。然后,我检查了自己的状态并用git commit --amend --no-edit -a
强行按下。令我惊讶的是,(力量)推错了分支!查看我的控制台日志(请注意,我已将git push -f
别名为g
,git
是st
的别名,而status
是别名为co
)。
旁注:例如,我还注意到当我尝试推动checkout
时,Git经常抱怨develop
不同步(需要先拉动),但是为什么会这样呢?当我不在那个分支上时,它与master做任何事情?似乎相关,不确定是什么问题。
控制台日志(分支名称在“ $”之前):
master
git config
josh:~/Projects/my-project jsm/logging $ git commit --amend --no-edit -a
[jsm/logging 4cdb3dc] add logging
Date: Mon Aug 27 15:18:41 2018 -0400
1 file changed, 12 insertions(+), 6 deletions(-)
josh:~/Projects/my-project jsm/logging $ git st
## jsm/logging...origin/jsm/logging [ahead 1, behind 1]
josh:~/Projects/my-project jsm/logging $ git push -f
Counting objects: 1, done.
Writing objects: 100% (1/1), 685 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:my-org/my-project
+ 5a649bc...8d320d2 develop -> develop (forced update)
^^^^^^^ why is it pushing a different branch than I'm on?!
josh:~/Projects/my-project jsm/logging $ g co develop
Switched to branch 'develop'
Your branch is up-to-date with 'origin/develop'.
josh:~/Projects/my-project develop $ g co jsm/logging
Switched to branch 'jsm/logging'
Your branch and 'origin/jsm/logging' have diverged,
and have 1 and 1 different commit each, respectively.
(use "git pull" to merge the remote branch into yours)
josh:~/Projects/my-project jsm/logging $ git st
## jsm/logging...origin/jsm/logging [ahead 1, behind 1]
josh:~/Projects/my-project jsm/logging $ git push -fu origin jsm/logging
Counting objects: 11, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (11/11), 1.04 KiB | 0 bytes/s, done.
Total 11 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), completed with 5 local objects.
To git@github.com:my-org/my-project
* [new branch] jsm/logging -> jsm/logging
Branch jsm/logging set up to track remote branch jsm/logging from origin.
答案 0 :(得分:3)
knugie's comment包含正确的原因:您将push.default
设置为matching
。您可能一直期望使用current
,甚至使用upstream
。那就是TL; DR就在那儿。
现代Git中的默认push.default
是simple
,大多数人认为它更安全-matching
是2.0之前的默认设置,它引起了很多人的头痛。但是要了解为什么是这种情况,让我们从较高的角度看一下git push
的作用。 git fetch
也是这样做的,因此在某种程度上值得涵盖两者。不过,我将省略特殊的特定于访存规则。
首先,您的Git选择一个(单个)其他Git进行联系。 (如果您从多个远程站点获取或推送到多个远程站点,则Git一次执行一次。)可以在某个URL上找到另一个Git。通常,您使用origin
之类的名称来提供URL,但是如果愿意,可以直接将其拼写出来。 (这很少是一个好主意,主要是史前Git的保留。)或者您可以让Git弄清楚。如果只有一个名为origin
的遥控器,则Git每次都会使它正确。 :-)
然后,您的Git在该URL(remote.origin.url
)上调用另一个Git。该其他Git具有自己的分支,标签和其他引用。您的Git拥有其Git列表,列出了所有分支,标签和其他参考。您可以通过运行git ls-remote origin
来查看Git看到的内容,执行以下两个步骤,打印出结果,然后停止。
对于git push
,下一步取决于几件事:
您是否在命令行上列出了refspecs? (如果是这样,则Git将使用您列出的refspec。)refspec位于远程名称之后,例如:git push origin <refspec1> <refspec2> ...
。如果您运行的git push
没有多余的参数,则不会列出任何参考规范。
如果未列出refspecs,此遥控器是否有特殊的默认设置? (如果是这样,则为默认设置。请注意,这是一个 refspec ,而不是像push.default
这样的字符串文字!)
否则,push.default
是默认设置。它有五个设置,我们将在下面介绍。
对于git fetch
,有一个相似的模式,但是Git几乎总是使用remote.remote.fetch
设置结束,例如remote.origin.fetch
,因为总是有这样的设置,而您作为用户将倾向于只运行git fetch origin
,甚至只是运行git fetch
。
另一个明显的问题是:refspec到底是什么?
refspec的第二简单形式类似于master:master
或jsm/logging:jsm/logging
,或者对于git fetch
,类似于master:origin/master
。也就是说,有一个左侧名称,一个冒号:
字符和一个右侧名称。
左边的名字是 source ,右边的名字是 destination 。每个名称都是一个 reference 或 ref 名称,这意味着您可以拼写一个全名,例如refs/heads/master
。如果您没有拼写名字,Git通常会正确地猜出master
是分支名称,而v1.2
是标签名称(通过查看您拥有的分支和标签名称),但是如果您猜错了,或者您真的想确定,则可以拼出全名。
但是我说这是 second 的最简单形式。最简单的方法是完全忽略冒号和目的地:master
或v1.2
或jsm/logging
。在这里,提取和推送在处理方式上有所不同:它们仍然仍然是操作的源,但是对于git push
,目的地是一个副本的来源。对于git fetch
,目的地是不保存名称(丢弃)。由于我们正在研究git push
,因此我们可以跳过获取特殊性,而专注于git push
喜欢如何在两侧使用 same 名称。
值得注意的是:您可以在引用规范中添加前导+
。这将为该refspec设置强制标志 。让我们在下面看一个简单的例子。
获取和推送必须完成两件事。首先,也是最重要的是转移 commits 。没有提交,Git一无所有:提交是Git存在的原因。它们(间接地)保存文件。
因此,如果您运行git push
,则您的Git会向其Git提供您所需要的,不需要的所有提交。如果您运行git fetch
,那么您的Git将从他们的Git中获取他们不需要的任何提交。
您(或他们)将需要的一组提交由 reachability 确定,这是一个相当大的主题。有关此的非常好的介绍,请参见Think Like (a) Git。然而,一行总结的摘要是,当您推送分支时,它们将需要分支上的提交;当您获取其分支时,将需要它们分支上的提交。
已将正确的提交集转移到正确的Git,您的两个Git现在必须在最后一步进行合作:设置一些名称。如果您正在git push
,请让您的Git要求他们的Git设置其他们的名称:您要求他们更新其master
,或更新或创建其{{1} }, 例如。如果您jsm/logging
在使用,则您的Git会根据他们的git fetch
来设置origin/master
,以及将他们的master
重命名为{{ 1}}是通过master
中设置的 refspecs 发生的。
因此,如果您在命令行上 do 命名一组引用规范,则Git将根据您列出的源名称来获取或推送提交。接收方Git将根据列出的目的地名称,通过在您的Git中设置一些名称来记住(如果origin/master
,则在其Git中,如果设置remote.origin.fetch
,则根据其列出的名称)
请注意,fetch
可以根据礼貌的请求(*请将您的push
设置为git push
)或作为强制命令来发送其最终的名称设置操作:设置您的master
到a123456....
还是可以不睡觉而直接睡觉!他们的Git 可以仍然拒绝命令,但是通常的默认设置是检查礼貌的请求以查看是否只需添加新的提交,并遵守有力的命令即可。
master
会后退,也许一直到a123456...
如果仅运行git push
或push.default
(带有或不带有force标志),则Git使用一些默认设置。如果您没有针对遥控器的特定refspec,则您的Git使用git push origin
。这是它的五个设置所在的地方:
git push
:这使push.default
失败,从而迫使您列出一些refspec。 (我自己尝试了一段时间,但感觉太痛苦了。)
nothing
:这告诉您的Git使用您的当前分支。可能正是您所期望的。等同于进行git push
。
current
(又名git push remote refs/heads/branch:refs/heads/branch
):这告诉Git使用当前分支作为源,但使用其上游名称作为目标。也就是说,如果您当前的分支是upstream
,但是tracking
的上游是B
,则相当于B
。 1
origin/not-B
:类似于git push origin B:not-B
,但要求上游名称与当前分支名称匹配。也就是说,如果simple
的上游是upstream
,而您在master
上,则origin/master
会按您的期望推送到master
,但是如果{{ 1}}推送到git push
,而您在origin/master
上,B
只是失败。
origin/not-B
:您的Git会遍历其Git的分支名称列表(所有以B
开头的git push
名称)。对于他们拥有的每个分支名称,您的Git都会推送同名的您的分支。
请注意,如果您使用matching
标志,则该标志适用于所有推送的分支。如果您的模式是git ls-remote
,则它适用于匹配的分支。这就是为什么您的输出显示为:
refs/heads/
您的Git发现您和他们俩都有--force
。他们的是 matching
,您的是+ 5a649bc...8d320d2 develop -> develop (forced update)
,而refs/heads/develop
不是5a649bc
的祖先。礼貌的请求-*请将您的8d320d2
设置为5a649bc
-将被拒绝,但是在使用了强制标志的情况下,您的Git发送了一条命令,并且Git服从了。这丢失了他们的8d320d2
的一些提交,因此他们说“强制更新”,并且您的Git打印了该内容,并显示了三个点(正常的非强制develop
仅显示两个点)。
如果您自己的存储库中仍有提交8d320d2
,则可以轻松地从中恢复。如果没有,那就比较棘手。要恢复,请尝试运行:
develop
这使用了一个refspec,其中设置了push
(强制标志), source 是原始提交哈希5a649bc
,而 destination 是分支名称git push origin +5a649bc:refs/heads/develop
。请注意,在此处拼写+
是明智的(甚至可能是必要的),因为源“名称”是原始哈希ID,因此您的Git不知道这应该是分支。
1 这可能会也可能不会召唤莎士比亚的幽灵。 (或者是哈姆雷特国王?)