Git Push:HEAD:refs / heads /< branch>之间的区别是什么?和<分支&gt ;? [英] Git Push: What is the difference between HEAD:refs/heads/<branch> and <branch>?

查看:161
本文介绍了Git Push:HEAD:refs / heads /< branch>之间的区别是什么?和<分支&gt ;?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

命令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 设置为 ed4f38babf3d81693a68d06cd0f5872093c009f6 。 (在这一点上 - 实际上,就在这一点之前,真的 - 你的Git和他们的Git有一个关于你想要发送它们的对象以及他们已经拥有哪些对象的对话,所有这些都是由这些丑陋的哈希ID完成的。两个Gits同意将要发送的内容,你的计数对象和压缩对象,然后发送它们)



获取名称和哈希部分


$ b $注意,Git的请求有两个部分:(1)完全限定的引用;(2)大丑陋的散列。 (实际上,还有第三部分, --force 标志,但这一部分很简单,我们可以忽略它。)但是 获取这些?



如果你写:

  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 使用此表单时, remote 名称 form-Git在您的存储库中查找 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 不是时,Git可以从它获取: HEAD 有一个分支名称 - 全名形式,实际上 - 分支名称有散列。但是当你处于分离HEAD模式时, HEAD 只有一个散列。 Git 不能 HEAD 中找到分支名称。可能不是 1:您可能已经通过ID检出了一个提交,或者您可以通过标记名称检出,如下所示:

 
















$在这种情况下,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 设置。通常这是简单(自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 的I​​D)



您可以在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默认会做到,即使它不合理。

单个refspec中的强制标志是一个前导加号 + 。例如,假设您为 master develop 分支提供了新的提交,并且还有一组新的 rebased 提交实验,其他人都同意允许您强制推送。



你可以这样做:

  git push origin develop master; git push -f origin实验

但是您可以将它们合并为一个大推动:



pre $ git push origin develop + experiment master

实验中的前导 + 使得 成为一个命令( 更新实验!),同时将其他人作为礼貌请求(请先生,如果您愿意,请更新开发 master )。
$ b

(这对于 push ,但实际上你每天都会经常使用 git fetch ,它使用带 + 标志来创建和更新您的远程追踪分支。)






1 如果其他回购在您的同一台机器上,并且您使用的是 file:// 或基于本地路径的URL,但这并不完全正确,但原理相同并且操作也是一样。



2 更好但是,首先不要陷入这种局面。有一个名称既是分支名称​​又是标签名称,这是非常令人困惑的。 (由于Git的缩写习惯,避免出现类似的令人困惑的情况:例如,不要命名名称类似于远程名称的分支,Git会处理它们,但 可能不会。: - ))



3 实际上,这个规则有一个例外,大多数人永远都不会注意到:when HEAD 命名一个未出生的分支。大多数情况下,这发生在一个新的仓库中,它根本没有任何提交。很显然,如果没有提交,那么不存在 HEAD 可以命名的提交ID。当你使用 git checkout --orphan 来创建一个新的孤立分支时,它也会出现。

如果您使用非限定名称,他们的Git将查找名称来限定它。这意味着你可能不知道你试图更新或删除的是什么样的名字。无论如何,这通常不是一个好主意。


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 /&lt; branch&gt;之间的区别是什么?和&lt;分支&gt ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆