命令1和命令2之间有什么不同?
1. git push <projectpath> HEAD:refs/heads/<branch>
2. git push <projectpath> <branch>
"HEAD:refs/heads/" 的含义是什么?"命令1和命令2之间有什么不同?
1. git push <projectpath> HEAD:refs/heads/<branch>
2. git push <projectpath> <branch>
"HEAD:refs/heads/" 的含义是什么?"VonC's answer是正确的(并且被点赞),但我认为另一种看待这个问题的方式可能更有意义。
请注意,所有这些都是基于使用git push
的四个单词形式,即git push 远程 refspec
。 这里的远程
部分通常只是名称origin
。 我们将在稍后更好地定义refspec
。
git push
执行的操作git push
需要做的(并且因此做了)是在另一台机器上调用另一个Git实例,1然后给那个Git实例一组引用(通常是分支名称,有时是标签名称)以进行更新。 引用只是一个名称,例如master
或v1.2
,理想情况下应该是完全限定的(refs/heads/master
或refs/tags/v1.2
),以便我们可以确定它是什么类型的引用-分支、标签或其他内容。refs/heads/master
设置为,比如,ed4f38babf3d81693a68d06cd0f5872093c009f6
。(此时 - 实际上是在这之前一点点 - 你的 Git 和他们的 Git 会就你想要发送给他们哪些对象以及他们已经拥有哪些对象进行对话,所有这些都通过这些大而丑陋的哈希 ID 来完成。一旦两个 Git 就将要发送的内容达成一致,你的 Git 开始执行"计算对象"和"压缩对象",然后将它们发送给他们。"现在,请设置一些名称"部分几乎是最后发生的。)--force
标志,但该部分很容易忽略。)但是,你的 Git 从哪里获取这些内容呢?git push origin somename
origin
,它用于查找URL,以及名称somename
。您的Git使用此信息来确定完整名称。 somename
是标签吗?如果是,则完整名称为refs/tags/somename
。 somename
是分支吗?如果是,则完整名称为refs/heads/somename
。两种方式都可以。当然,您也可以自己编写完整的名称-如果名称既是分支又是标记,则可能要这样做,而不是让Git为您选择一种。2
那么,您的Git从哪里获取大而丑的哈希值?答案是:从同一个名称中获取。无论是分支还是标记,名称somename
只是命名某个特定的Git对象。如果您想随时查看哈希,请随时这样做:git rev-parse somename
我会向你展示它。事实上,这就是我得到ed4f38babf3d81693a68d06cd0f5872093c009f6
的方法:我去了Git存储库并执行了git rev-parse v2.1.1
,它打印出了该哈希值,因为自从版本2.1.1发布以来,在任何完整的Git存储库中,v2.1.1
都是一个有效的标签。
请注意,当您使用这个形式——这个git push remote name
形式时,Git会查找您的仓库中的name
参数以完成两个目的:找到它的全名,并获取它的哈希值。不管您的HEAD
在哪里,只要该全名指向的位置正确即可。
git push
的第四个参数称为refspec,其语法实际上允许两个由冒号分隔的部分:
git push origin src:dst
dst
部分提供了名称,但 src
部分提供了哈希值。Git 将 src
部分通过 git rev-parse
运行,并生成哈希值。因此,您可以:git push origin mybranch:refs/tags/v42
使用分支mybranch
所标识的提交哈希,在其他Git存储库中创建标签v42
。
HEAD
包含一个分支名称在Git中,HEAD
始终命名当前提交。通常它通过命名一个分支并让分支命名提交来实现。因此,通常HEAD
包含类似于master
的分支名称,并且分支名称始终可以让您获得该分支的tip commit(这是Git定义“tip commit”的方式,请参阅Git词汇表中的分支定义)。但HEAD
始终可以转换为提交:
$ git rev-parse HEAD
2b9288cc90175557766ef33e350e0514470b6ad4
因为HEAD
要么是一个分支名称(然后是最新的提交),要么你有一个“分离的 HEAD”,在这种情况下,Git 直接将当前提交 ID 存储在HEAD
中。
请记住,为了推送,Git 需要获取这两个信息:哈希值和(完整的)名称。当HEAD
不是“分离的”时,Git 可以从中获取两者: HEAD
有一个分支名称 - 实际上是完整的名称形式,而且分支名称有哈希值。但是当您处于“分离的 HEAD”模式时,HEAD
只有一个哈希值。Git 无法在HEAD
中找到分支名称。可能没有一个:您可能已经通过 ID 检出了提交,或者可能已经按标签名称检出,例如:
$ git checkout v2.1.1
这会让你进入“分离 HEAD”模式。
在这种情况下,Git 要求你提供源哈希 src
(你仍然可以使用名称 HEAD
来获取它)和目标名称 dst
。如果你使用 HEAD
作为源,Git 真的需要你拼出完整的目标,因为此时 Git 无法确定它应该是一个分支(refs/heads/dst
)还是一个标签(refs/tags/dst
)。4
git push
的其他形式你可以使用较少的参数运行 git push
,例如:
git push origin
甚至只是:
git push
refspec
时,Git首先查看您的push.default
设置。通常这是simple
(自Git 2.0版本以来的默认设置)。在这种情况下,Git仅使用HEAD
来确定要推送的内容 - 当然,这只适用于HEAD
未分离的情况。这就是我们上面描述的内容。HEAD
。其中一个 - 在Git 2.0版本之前的默认设置不使用,但是该特定设置证明容易出错,这就是为什么默认设置更改的原因。除非你是Git大师,否则你可能不应该使用它。)remote
,Git还将使用HEAD
来确定要推送到哪里,并在需要时默认为origin
。)git push origin branch1 branch2 tag1 HEAD:refs/tags/tag2
git push origin 'refs/heads/*:refs/heads/*'
*
s so you may need to use quotes, as in this example; other shells won't—or at least usually won't—but it doesn't hurt to quote). 这会推送所有你的分支,或者至少尝试推送。这往往过于热情,会推送所有临时工作和实验分支,这可能不是你想要的,但这是Git在2.0版本之前默认做的。src
:git push origin :refs/heads/deleteme
git push
命令中添加--force
,则您的 Git 将此标志传递给他们的 Git。而不是一个礼貌的请求——“请问先生,您是否想将您的refs/heads/master
设置为ed4f38babf3d81693a68d06cd0f5872093c009f6
?”——您的 Git 将其发送为相当坚定的要求。他们的 Git 仍然可以拒绝任何一种方式,但默认情况下,即使这并不明智,他们的 Git 也会这样做。+
。例如,假设您有master
和develop
分支的新提交,以及一个新的rebased提交集合experiment
,其他人都同意您可以force-push。git push origin develop master; git push -f origin experiment
git push origin develop +experiment master
file://
或本地路径的URL,则这并不完全正确,但原则相同,操作方式相同。
2更好的方法是,首先不要让自己陷入这种情况。拥有一个既是分支名称又是标签名称的名称非常令人困惑。(由于Git的缩写习惯,避免使用类似远程名称的名称来命名分支也会出现类似的令人困惑的情况。Git会很好地处理它们,但是您可能不会。:-))
3实际上,有一个例外,大多数人永远不会注意到:当HEAD
指向“未命名分支”时。这主要发生在一个没有任何提交的新存储库中。显然,如果没有提交,就没有HEAD
可以命名的提交ID。当您使用git checkout --orphan
创建一个新的孤立分支时,它也会发生。
HEAD^{tree}
解析为空树,那么这种情况实际上可以让许多脚本正常工作。 - torek区别:
git push <projectpath> HEAD:refs/heads/<branch>
: 本地分支提交可能与远程分支提交不同,因为“HEAD”可以分离(未链接到任何分支)git push <projectpath> <branch>
: 本地分支提交将始终与远程分支提交相同。详细信息:
一个合适的分支(意思是不是分离的HEAD)是一个存储在由HEAD
直接引用的refname中的提交。
这意味着HEAD
是一个符号引用到refname
(其中包含实际提交),或者直接到提交(分离的HEAD)。还请参见“HEAD: current commit”。
HEAD:refs/heads/<branch>
是一个refspec,有<src>:<dst>
,其中<src>
通常是要推送的分支的名称,但它可以是任何任意的“SHA-1表达式”,例如master~4
或HEAD
,而<dst>
告诉哪个远程引用更新了此推送。
如果您有以下状态:
git checkout abranch
--x--Y--W--Y--Z (HEAD abranch)
|
(origin/abranch)
git push origin aBranch
将推送一个分支的HEAD(这里是Z)或者origin,结果为:
--x--Y--W--Y--Z (FEAD, abranch, origin/abranch)
git checkout abranch~2
(HEAD)
|
--x--Y--W--Y--Z (abranch)
|
(origin/abranch)
然后使用第二个语法推送,您将仅更新远程跟踪分支到该 W
提交:
git push origin HEAD:refs/heads/abranch
(HEAD)
|
--x--Y--W--Y--Z (abranch)
|
(origin/abranch)
git push origin aBranch
仍会将整个分支推送,即使HEAD没有引用aBranch
。
push
命令,<src>
是你的(本地)端,而 <dst>
是他们的(远程)端。你目前列出的顺序适用于 fetch
命令。 - torek