我是git
的新手,我尝试过使用一些功能。
是什么
git diff HEAD...origin master
vs
git diff origin master
他们似乎给了我完全不同的结果。
也许值得注意的是,我确实origin/master
与origin master
不同。
难道这一切都意味着同样的事情吗?
答案 0 :(得分:2)
比较分支
$ git diff topic master (1) $ git diff topic..master (2) $ git diff topic...master (3)
主题提示与主分支之间的更改。
与上述相同。
- 醇>
自主题分支启动以来主分支上发生的更改。
特别是在三期<commit>...<commit>
git-diff范围形式上:
此表单用于查看包含和最多为第二个的分支上的更改,从两者的共同祖先开始。 &#34; git diff A ... B&#34;相当于&#34; git diff $(git-merge-base A B)B&#34;。你可以省略任何一个,它与使用HEAD具有相同的效果。
答案 1 :(得分:2)
您将几种不同的Git概念混合在一起。不可否认,这些Git概念都有非常类似的名称:遥控器,分支机构和远程跟踪分支机构。 (Git的命名法变得更糟,因为跟踪的概念与远程跟踪分支不同,当如果有一个分支跟踪另一个分支,则跟踪的分支称为 upstream ,这与使用远程作为上游或使用名为upstream
的远程不同。如果您&#39 ;不要混淆,你做得比我或大多数人都好。:-))
所以,让我们回顾一下这个问题并定义所有这些。
首先,我们有分支。单词&#34; branch&#34;在Git中实际上是不明确的:它可以引用分支名称,如master
,或者它可以引用系列提交,从提示开始 - 大多数人在一个分支上工作,并且在时间上倒退。换句话说,如果你随便说'#34; blah mumble branch master
yada yada&#34;,那么你是否意味着分支 name {{1通过以master
命名并在历史记录中向后工作的提交开始形成的一系列提交。
它通常可以从上下文中清楚,如果没有,你可以使用&#34;分支名称&#34;和&#34;分支结构&#34;区分他们。分支名称只是像master
这样的单词,除了要成为当前有效的分支名称,它必须是Git知道的名称,如果运行master
,Git将显示。另请参阅What exactly do we mean by "branch"?
请注意,分支名称可以直接转换为分支结构的最尖端提交。 git branch
命令使用了这一点,我们稍后会看到。要查看分支名称如何变为提交ID,请使用git diff
。这个命令只是看事物,所以任何时候都可以安全使用。现在就试试吧:
git rev-parse
和
$ git rev-parse master
如果您有其他分支,请尝试将其姓名传递给$ git rev-parse HEAD
。 (然后尝试git rev-parse
git rev-parse`。)
远程只是一个名称,例如git branch -vv and compare the abbreviated commit IDs you see in its output, to what you got from
。在这方面,它很像一个分支名称。区别在于远程名称存储在与分支名称不同的位置,如果运行origin
,Git将显示您的远程名称。除此之外,一个远程名称为您提供了两件事:能够运行git remote
和git fetch
而无需写出一个很长的URL-Git将远程名称保留在远程名称和能力之下拥有远程跟踪分支。
远程跟踪分支(又一次!)只是一个名称,但它从远程名称开始,如git push
,然后有一个斜杠,然后有一个分支的名称& #34;如在电视上看到的遥控器&#34;。 1 因此,你会看到像origin
这样的名字,这是典型的远程跟踪分支名称。
您的(常规,本地)分支名称和远程跟踪分支之间存在一个主要区别:Git会在您使用分支时更新分支:您检查它们,使用origin/master
向它们添加提交,使用git commit
向它们添加合并提交,依此类推。你可以git merge
一个分支,然后git checkout
会说你是&#34;在&#34;分支,例如git status
。
您的Git 不会以这种方式更新远程跟踪分支。事实上,你不能在&#34;&#34;他们都没有。相反,当你运行on branch master
- 这里使用远程名称git fetch origin
- 你的Git从远程查找URL,使用该URL调用另一个Git,并且有一个与它的小谈话。我应该说,你的Git从他们的Git中获取了所有他们的分支 - 分支名称的列表。然后你的Git从他们的Git获得他们拥有的任何提交,你不会:他们的分支结构。
一旦你的Git有了他们的分支结构,它就会设置你的远程跟踪分支(一个名字)指向最尖端的提交,就像他们的分支名称在他们的Git中一样。你的Git为他们的每个分支都这样做。这样,在origin
之后,您的远程跟踪分支机构现在会跟踪其分支的位置,这是您的Git最后一次使用他们的Git。
您的Git通过将您的远程名称(git fetch origin
)放在其分支名称(origin
)前面来构建远程跟踪分支名称。这就是您的远程跟踪分支master
的原因:他们的分支名称是origin/master
。
master
Git中的名称HEAD
非常特别。 (事实上,它非常特别,如果你设法以某种方式删除文件HEAD
,Git将不再相信你的Git存储库 是一个Git存储库!)但是,通常.git/HEAD
实际上只包含分支的名称。例如,如果您在分支HEAD
上,则特殊master
文件只包含字符串:HEAD
。 (字符串ref: refs/heads/master
实际上是分支名称refs/heads/master
的全名,但通常您不必担心这一点:Git会隐藏master
前缀,就像当您使用远程跟踪分支refs/heads/
时,它会隐藏refs/remotes/
前缀。)
当origin/master
包含分支名称时 - 正如我们刚才所说,这是通常的情况 - 名称HEAD
主要是写入当前分支名称的简写。因此,如果你在HEAD
上,master
只是说HEAD
的另一种方式。它并不是那么简短,但它的优势在于它即使你在分支llanfairpwllgwyngyll
上也能运作。更重要的是,这意味着像master
这样的程序不需要知道您所依赖的分支,或者等同于git log
之类的程序可以找出你在哪个分支。事实上,这正是 git status
如何找到的。
git status
列出了您的分支名称,例如git branch
。master
列出您的远程名称,例如git remote
。origin
列出了您的远程跟踪分支,例如git branch -r
。origin/master
命令 git diff
命令本身很不寻常。大多数Git命令以the gitrevisions
documentation中描述的方式处理分支名称和修订列表参数。但是,在git diff
中,两点和三点符号git diff
和branch1..branch2
都有新的不同含义。
(除此之外,branch1...branch2
还有一大堆子模式,您可以使用git diff
,git diff-index
和git diff-files
来调用这些子模式。这里不用担心。)
你跑了:
git diff-tree
这里有两个额外的困难,我会在一段时间内完全忽略其中一个。另一个问题是,它使用三点表示法,git diff HEAD...origin master
对它的特殊解释,需要理解git diff
命令。
让我们暂时简化第二个问题,假装相反,你写道:
git merge-base
对两个 -dot语法的特殊git diff HEAD..origin master
解释要简单得多:git diff
假装您根本没有使用这两个点,而是刚刚将两个名称写成两个独立的参数。所以这个特殊的形式意味着完全相同的东西:
git diff
这里有一点问题,因为我们只是命名三个的东西:特殊的git diff HEAD origin master
名称,一个看起来像的名字(事实上)是)远程,而不是分支或远程跟踪分支,最后是分支名称。 HEAD
命令在这里需要两个:它需要两个分支名称,或者至少两个可以解析为特定提交的参数。 2
当然,git diff
效果很好:它命名当前分支,该分支命名分支上最尖端的提交。如果当前分支HEAD
且master
决定提交master
,则24377c8...
也会解析为HEAD
,Git将使用24377c8...
作为先在diff中提交。
但是24377c8...
呢?这就是the gitrevisions
documentation的用武之地。一开始很难看到,但事实上,origin
会被视为origin
和{{ 1}}通常 3 映射到origin/HEAD
,所以通常这意味着&#34;无论提交origin/HEAD
提出什么&#34;。它绝对意味着&#34;无论提交origin/master
提出什么。&#34;
为了具体,请说明您的git rev-parse origin/master
是git rev-parse origin
提交HEAD
,而master
是他们的24377c8...
这是提交origin
。然后你可以输入:
master
也就是说,两个提交b240a77...
将比较这两个哈希 - 我们在这里使用这些缩短的哈希值,因为完整的40个字符太多了 - 但是额外的{{1} }?
这让我们陷入了前面提到的其他额外困难:git diff 24377c8 b240a77 master
可以接受两次以上的提交,如果它获得三次或更多次提交,它通常会 4 产生一个&#34;组合差异&#34;。如果单词git diff
不是分支名称,那么master
会抱怨它,git diff
会将其视为路径名,这会限制master
输出到特定路径。但当然git rev-parse
是一个有效的分支名称,因此它可能会被解析为修订版,并可能导致难以描述的行为。 (在Git版本2.8.1中,我尝试了它,它的作用特别奇怪。)
如果您做想要使用三点形式,请坚持使用一个带有两个分支名称的三点参数。在这种情况下,Git将使用git diff
来查找两个修订版的合并基础。 (有关详细信息,请参阅Drew Beres' much shorter answer to this question。 5 )
如果没有特别棘手的表单,您只需在要提供给diff
的名称上运行master
即可查看将要使用的提交内容:
git merge-base
这将显示两个提交ID,这些是git rev-parse
或git diff
将要比较的两个提交。使用三点语法时,您可以运行$ git rev-parse HEAD origin
以查看Git将选择与三点版本的右侧进行比较的提交。如果只打印一个修订版,那么修订版git diff HEAD origin
将与右侧进行比较。
(并且,请记住,所有这些行为都特定于git diff HEAD..origin
:其他命令(如git merge-base --all
)以不同的方式处理双点和三点语法。)
1 您可以创建不以远程名称开头的远程跟踪分支名称。您还可以创建以远程名称开头的本地分支名称。做其中任何一个都是一个坏主意,因为它会混淆人类。 Git会将它们保持在内部,Git使用git diff
和git diff
前缀来了解它们是本地和远程跟踪分支 - 但它只是无法使用;不要这样做。
2 更确切地说,git log
想要解析两个树的两个参数。但是,提交ID始终有效,并且分支名称会解析为提交ID,因此,至少在最初阶段,只关心自己查找提交可能更有意义。
3 当你第一次refs/heads
一个URL时,Git会设置远程refs/remotes/
来保存URL,并且还会发现它是否可以 - 哪个分支{{ 1}}其他Git存储库中的名称。然后,它会将您的远程跟踪名称git diff
设置为映射到该分支的远程跟踪名称。由于该分支在其他Git存储库中通常是git clone
,因此origin
通常是对HEAD
的符号引用。
但是,如果他们的Git存储库签出了不同的分支,那么您的origin/HEAD
将指向其他master
名称。 (并且,在Git中可能存在的错误中,origin/HEAD
实际上从未真正更新过origin/master
,即使它们应该在更改当前分支时更新它。)< / p>
4 由于origin/HEAD
处理三点符号的方式,因此从这里变得非常糟糕。但是,这取决于您的特定Git版本:旧版本的Git通过对您传入的参数进行文字字符串测试来检测到这一点,而较新的版本通过查看修订解析代码留下的标记来检测它。如果不构建旧版本的Git,我只能说我确信它们的行为与我测试的版本不同。
5 我几个小时前开始这个回答,被中断几次,发现自己无法解释为什么一些origin/whatever
命令产生了三个或更多分支名称和两个和三个点语法产生组合差异和一些产生的普通差异,所以不得不去查看git fetch origin
来源。它基本上有些错误。在捅了几个小时后,我相信我有一个修复,虽然因为Git维护者完全忽略了my git stash
fix,所以我不希望这里有很多。