Git Push:HEAD:refs / heads /< branch>之间的区别是什么?和<分支> ;? [英] Git Push: What is the difference between HEAD:refs/heads/<branch> and <branch>?
问题描述
命令1执行的命令2不是什么?
1。 git push< projectpath> HEAD:参考文献/头/<分支>
2. git push< projectpath> <分支>
HEAD:refs / heads /的含义是什么?
VonC的答案是正确的(和upvoted) ,但我认为看另一种方式可能会更有意义。
请注意,所有这些都假设您使用的是<$的四字形式c $ c> git push ,即 git push remote refspec
。这里的 origin
。我们会在一段时间内更好地定义 refspec
。
git push
做
什么 git push
需要做)是在另一台机器上调用另一个Git实例,然后为其他Git提供一组引用(通常是分支名称,有时标记名称)以进行更新。 引用只是一个名称,比如 master
或 v1.2
,理想情况下< ( refs / heads / master
或 refs / tags / v1.2
),这样我们就可以确定它引用的是什么类型的分支,标签或其他。
为了让其他Git要更新Git移交的引用,您的Git还必须交出一些大型丑陋的SHA-1哈希值:每个引用一个。换句话说,你的Git会要求他们的Git将 refs / heads / master
设置为压缩对象
,然后发送它们)
获取名称和哈希部分
$ b $注意,Git的请求有两个部分:(1)完全限定的引用;(2)大丑陋的散列。 (实际上,还有第三部分,
--force
标志,但这一部分很简单,我们可以忽略它。)但是 / em>获取这些? 如果你写:
git push origin somename
你已经给你
Git提供了两条信息:您的Git用来查找URL的名称 origin
,名称 somename
。你的Git使用它来确定全名。是 somename
标签?如果是这样,完整名称是 refs / tags / somename
。是 somename
一个分支?如果是这样, full 名称是 refs / heads / somename
。无论哪种方式工作。当然,您也可以自己写出全名 - 如果名称既是分支和,也可以 来完成此操作,而不是让Git为你选择一个。 2 那么,你的Git在哪里得到大丑陋的哈希?答案是:从同一个名字。名称 somename
,无论是分支还是标签,都只是命名一些特定的Git对象。如果你想自己查看哈希,你随时都可以这样做:
git rev-parse somename
会显示给你。事实上,我得到了 ed4f38babf3d81693a68d06cd0f5872093c009f6
:我去了Git的Git仓库,做了 git rev-parse v2.1.1
,并且它会打印出该散列,因为<2.1 c $ c> v2.1.1 是自2.1.1版本发布以来Git存储库的任何完整副本中的有效标记。
请注意,当您 do 使用此表单时,
name
两个目的:找出它的全名,并得到它的散列。无论你的 HEAD
是什么,只有那个全名指向哪里。 但是Git不会必须使用你的分支(或标签的)ID
第四个参数 git push
被称为 refspec ,它的语法实际上允许两个部分用冒号分隔:
git push origin src:dst
在这种情况下, dst
em> name ,但是 src
部分提供散列。 Git通过 git rev-parse
运行 src
部分,并生成散列。所以你可以:
git push origin mybranch:refs / tags / v42
在另一个Git仓库中创建标记 v42
,使用任何提交哈希分支 mybranch
标识符。
通常 HEAD
包含一个分支名称
在Git中, HEAD
总是命名当前提交。通常通过命名分支来实现,并让分支命名提交。因此,通常 HEAD
包含一个像 master
这样的分支名称,并且分支名称总是让您获得提示提交(这就是Git 如何定义tip commit;参见 Git词汇表)。但是总是, 3 HEAD
可以变成一个提交:
$ git rev-parse HEAD
2b9288cc90175557766ef33e350e0514470b6ad4
因为 HEAD
是一个分支名称(然后是提示提交),否则你有一个分离HEAD,在这种情况下,Git存储当前提交ID HEAD
。
HEAD分离时推送
请记住,为了推动,Git需要获得这两个信息:散列和一个(完整)名称。当
HEAD
不是 HEAD
有一个分支名称 - 全名形式,实际上 - 分支名称有散列。但是当你处于分离HEAD模式时, HEAD
只有一个散列。 Git 不能在 HEAD
中找到分支名称。可能不是 1:您可能已经通过ID检出了一个提交,或者您可以通过标记名称检出,如下所示: $在这种情况下,Git要求你提供源散列
src
- 你可以仍然使用名称 HEAD
来获取它和 HEAD
作为源代码,Git确实需要你来拼出完整的目的地,因为Git无法说明如果它应该是一个分支( refs / heads / dst
)或一个标记( refs / tags / dst
)。 4 其他形式的 git push
你可以使用较少的参数运行 git push
,例如:
git push origin
甚至只是:
git push
如果没有 refspec
,Git会先咨询你的 push.default
设置。通常这是简单
(自Git版本2.0以来的默认值)。在这种情况下,Git只是简单地使用 HEAD
来找出要推送的内容 - 当然,这仅适用于 HEAD
不分离。这就是我们上面所描述的。
(其他三个设置也使用 HEAD
。其中之一 - 一个是在Git版本2.0之前的默认值 - 不是,但是这个特定的设置证明太容易出错,这就是为什么默认值改变的原因了。你可能不应该使用它,至少除非你是Git master。) (并且,如果省略 remote
,Git会再次使用 HEAD
来确定推到哪里,如果需要,默认为 origin
。)
您也可以推送多个refspecs:
git push origin branch1 branch2 tag1 HEAD:refs / tags / tag2
在这种情况下,每个refspec都以通常的方式处理:如果需要,你的Git每次都可以给他们的Git一个完全限定的名字;如果你没有使用 src:dst
表单(或者如果你使用
src:dst
表单,请查看 src
的ID) 您可以在refspecs中使用通配符:
git push origin'refs / heads / *:refs / heads / *'
(有些贝壳会吃,mangle, fold,spindle,or mutilate *
s需要使用引号,如本例所示;其他炮弹不会 - 或者至少通常不会 - 但是引用并不会伤害)。这将推动你的所有分支,或至少尝试。这往往是过度热情,推动你所有的临时工作和实验分支,可能不是你想要的,但这是Git在2.0版之前默认做的。
而且,你可以使用一个空的 src
:
$ b
git push origin :refs / heads / deleteme
这是一种特殊的语法,意思是让我的Git询问他们的Git 删除该引用(删除标签,拼出标签)。与分离的HEAD一样,在 侧缺少完全限定的名称意味着您应该完全限定其 的名称。 (请参阅脚注4)。 如果添加
$ b 强制标志
- -force
到你的 git push
命令,你的Git将这个标志传递给它们的Git。而不是礼貌的请求 - 请,先生,你想把 refs / heads / master
设置为 ed4f38babf3d81693a68d06cd0f5872093c009f6
? - 你的Git会把它作为一个相当坚持的需求。他们的Git仍然可以拒绝任何一种方式,但是他们的Git默认会做到,即使它不合理。 +
。例如,假设您为 master
和 develop
分支提供了新的提交,并且还有一组新的 rebased 提交实验
,其他人都同意允许您强制推送。
你可以这样做:
git push origin develop master; git push -f origin实验
但是您可以将它们合并为一个大推动:
pre $
git push origin develop + experiment master
(这对于 1 如果其他回购在您的同一台机器上,并且您使用的是 2 更好但是,首先不要陷入这种局面。有一个名称既是分支名称又是标签名称,这是非常令人困惑的。 (由于Git的缩写习惯,避免出现类似的令人困惑的情况:例如,不要命名名称类似于远程名称的分支,Git会处理它们,但 可能不会。: - )) 3 实际上,这个规则有一个例外,大多数人永远都不会注意到:when +
使得 成为一个命令( 更新实验
!),同时将其他人作为礼貌请求(请先生,如果您愿意,请更新开发
和 master
)。
$ b push
,但实际上你每天都会经常使用 git fetch
,它使用带
file://
或基于本地路径的URL,但这并不完全正确,但原理相同并且操作也是一样。
HEAD
命名一个未出生的分支。大多数情况下,这发生在一个新的仓库中,它根本没有任何提交。很显然,如果没有提交,那么不存在 HEAD
可以命名的提交ID。当你使用 git checkout --orphan
来创建一个新的孤立分支时,它也会出现。
What does command 1 do that command 2 doesn't?
1. git push <projectpath> HEAD:refs/heads/<branch>
2. git push <projectpath> <branch>
What is the meaning of "HEAD:refs/heads/"?
VonC's answer is correct (and upvoted), but I think another way of looking at this might make more sense.
Note that all of this is assuming that you're using the four-word form of git push
, i.e., git push remote refspec
. The remote
part here is usually just the name origin
. We'll define refspec
better in a moment.
What git push
does
What git push
needs to do (and therefore does) is to call up another Git instance on another machine,1 then give that other Git a set of references (usually branch names, sometimes tag names) to update. A reference is simply a name, like master
or v1.2
, that ideally should be fully-qualified (refs/heads/master
or refs/tags/v1.2
) so that we can be sure what kind of reference it is—branch, tag, or whatever.
In order for the other Git to update the references your Git hands over, your Git must also hand over some of those big ugly SHA-1 hashes: one per reference. In other words, your Git is going to ask their Git to set their refs/heads/master
to, say, ed4f38babf3d81693a68d06cd0f5872093c009f6
. (At this point—actually, just a bit before this point, really—your Git and their Git have a conversation about which objects yours want to send them, and which objects they already have, all done by these big ugly hash IDs. Once the two Gits agree about what's going to be sent over, yours does the counting objects
and compressing objects
and then sends them the objects. The "now, please set some names" part happens nearly last.)
Getting the name and hash parts
Note that there are two parts to your Git's request: (1) a fully-qualified reference, and (2) the big-ugly-hash. (In fact, there's also a third part, the --force
flag, but that part is easy and we can just ignore it.) But where does your Git get these?
If you write:
git push origin somename
you've given your Git two pieces of information: the name origin
, which your Git uses to look up the URL, and the name somename
. Your Git uses this to figure out the full name. Is somename
a tag? If so, the full name is refs/tags/somename
. Is somename
a branch? If so, the full name is refs/heads/somename
. Either way works. Of course, you can also write out the full name yourself—and if the name is both a branch and a tag, you may want to do that, rather than letting Git pick one for you.2
So, where does your Git get the big ugly hash? The answer is: from that same name. The name somename
, whether it's a branch or a tag, just names some particular Git object. If you want to see the hash yourself, you can do that any time:
git rev-parse somename
will show it to you. This is, in fact, how I got ed4f38babf3d81693a68d06cd0f5872093c009f6
: I went to a Git repository for Git and did git rev-parse v2.1.1
and it printed out that hash, because v2.1.1
is a valid tag in any complete copy of the Git repository since version 2.1.1 came out.
Note that when you do use this form—this git push remote name
form—Git looks up the name
argument in your repository for both purposes: to find out its full name, and to get its hash. It doesn't matter where your HEAD
is, only what that full name points to.
But Git does not have to use your branch's (or tag's) ID
The fourth argument to git push
is called a refspec, and its syntax actually allows two parts separated by a colon:
git push origin src:dst
In this case, the dst
part supplies the name, but the src
part supplies the hash. Git runs the src
part through git rev-parse
and that produces the hash. So you can:
git push origin mybranch:refs/tags/v42
to create tag v42
in the other Git repository, using whatever commit hash your branch mybranch
identifies.
Normally HEAD
contains a branch name
In Git, HEAD
always names the current commit. Usually it does so by naming a branch, and letting the branch name the commit. So, usually HEAD
contains a branch name like master
, and a branch name always gets you the tip commit of that branch (that's how Git defines "tip commit"; see the definition of branch in the Git glossary). But always,3 HEAD
can be turned into a commit:
$ git rev-parse HEAD
2b9288cc90175557766ef33e350e0514470b6ad4
because HEAD
is either a branch name (which is then the tip commit), or else you have a "detached HEAD", in which case Git stores the current commit ID directly in HEAD
.
Pushing when HEAD is detached
Remember that in order to push, Git needs to get those two pieces of information: the hash, and a (full) name. When HEAD
isn't "detached", Git can get both from it: HEAD
has a branch name—in the full name form, in fact—and the branch name has the hash. But when you are in "detached HEAD" mode, HEAD
only has a hash. Git can't find a branch name in HEAD
. There might not be one: you might have checked out a commit by ID, or maybe you checked out by tag name, as in:
$ git checkout v2.1.1
which put you in this "detached HEAD" mode.
In this case, Git demands that you supply both the source hash src
—you can still use the name HEAD
to get it—and the dst
destination name. And, if you use HEAD
as the source, Git really needs you to spell out the full destination, because Git can't tell, at this point, if it should be a branch (refs/heads/dst
) or a tag (refs/tags/dst
).4
Other forms of git push
You can run git push
with fewer arguments, e.g.:
git push origin
or even just:
git push
What happens here is that without a refspec
, Git consults your push.default
setting first. Usually this is simple
(the default since Git version 2.0). In this case, Git simply uses HEAD
to figure out what to push—which, of course, works only when HEAD
is not detached. That's just what we described above.
(Three of the other settings also use HEAD
. One of them—the one that was the default before Git version 2.0—does not, but that particular setting proved too error-prone, which is why the default changed. You probably should not use it, at least not unless you are a Git master.)
(And, if you leave out the remote
, Git again uses HEAD
to figure out where to push to, defaulting, if needed, to origin
.)
You can also push multiple refspecs:
git push origin branch1 branch2 tag1 HEAD:refs/tags/tag2
In this case, each refspec is handled in the usual way: get its fully qualified name if needed, so that your Git can give their Git a fully qualified name each time; and look up its hash ID if you didn't use the src:dst
form (or if you did use the src:dst
form, look up src
's ID instead).
You can use wildcards in refspecs:
git push origin 'refs/heads/*:refs/heads/*'
(some shells will eat, mangle, fold, spindle, or mutilate the *
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). This will push all your branches, or at least try to. This tends to be overly enthusiastic, pushing all your temporary work and experimentation branches, and is probably not what you want, but it's what Git did by default prior to version 2.0.
And, you can use an empty src
:
git push origin :refs/heads/deleteme
which is a special-case syntax that means "have my Git ask their Git to delete that reference" (to delete a tag, spell out the tag). As with a detached HEAD, the lack of a fully-qualified name on your side means you should fully-qualify the name for their side. (See footnote 4 again.)
The force flag
If you add --force
to your git push
command, your Git passes this flag on to their Git. Instead of a polite request—"please, sir, would you like to set your refs/heads/master
to ed4f38babf3d81693a68d06cd0f5872093c009f6
?"—your Git will send it as a rather insistent demand. Their Git can still refuse either way, but their Git will, by default, do it even if it's not sensible.
Refspecs allow you to control this flag more tightly. The force flag in an individual refspec is a leading plus sign +
. For instance, suppose you have new commits for both master
and develop
branches, and also a new set of rebased commits for experiment
, which everyone else has agreed that you are allowed to force-push.
You could do this:
git push origin develop master; git push -f origin experiment
but you can combine it all into one big push:
git push origin develop +experiment master
The leading +
on experiment
makes that one a command ("update experiment
!") while leaving the others as polite requests ("please, sir, if you like, update develop
and master
").
(This is all a bit esoteric for push
, but is actually something you use regularly every day with git fetch
, which uses refspecs with +
flags to create and update your remote-tracking branches.)
1If the "other repo" is on your same machine and you're using a file://
or local path based URL, this isn't quite true, but the principle is the same and the operations go the same way.
2Better yet, don't get yourself in this situation in the first place. It's very confusing to have one name that is both a branch name and a tag name. (There are similar confusing situations to avoid due to Git's habit of abbreviating: don't name branches with names that resemble remote names, for instance. Git will handle them just fine, but you might not. :-) )
3Actually, there's one exception to this rule, which most people will never notice: when HEAD
names an "unborn branch". Mostly this occurs in a new repository, which has no commits at all. Obviously, if there are no commits, there is no commit ID that HEAD
could name. It also occurs when you use git checkout --orphan
to create a new orphan branch.
4If you use an unqualified name, their Git will look up the name to qualify it. This means you may not know what kind of name you are trying to update or delete. It's generally not a good idea, anyway.
这篇关于Git Push:HEAD:refs / heads /< branch>之间的区别是什么?和<分支> ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!