被 git diff 搞糊涂了
Confused by git diff
我想生成一个补丁文件,说明我的分支和主分支之间的差异。但是该分支存在时间很长,所以我只是从 master 进行了合并以使其更新。如果我开始在 Bitbucket 中创建拉取请求,我可以很好地看到差异。但是当我在我的分支上执行 git diff master..
时,我看到显示的差异并不存在。它们是由合并产生的吗?我怎样才能获得与 Bitbucket 相同的差异列表 - 现在只是我的分支和主分支之间的差异?
TL;DR
我不太清楚你的困惑是从哪里开始的,但值得注意的是,使用 git diff
与生成拉取请求有很大不同。最终,它将归结为 运行ning git diff
正确的特定提交。诀窍在于找到正确的提交。
长
存储库中有什么
首先,记住Git保留的是什么。在某种基础层面上,Git 关心的是 源快照 ,以 提交 的形式保存。提交包含某个源代码树的完整快照。提交还包含一些 元数据: 提交人的姓名和电子邮件地址,有时是两个人(作者 和 committer:他们可能是相同的,也可能是分开的)和time-stamps他们是什么时候做的; parent 提交 ID,以便 Git 可以将提交系列呈现为谁(作者)做了什么(见下文)以及何时(时间戳);和一条日志消息,以提供作者对为什么他们做了他们所做的事情的描述。
由于每次提交都是一个完整的快照,为了查看谁实际做了什么,我们必须使用像 git diff
这样的命令。假设我们在 b运行ch master
上连续完成了两次提交,如下所示:
(parent) (child)
df731ac <- 049a12b <-- master
像 master
这样的 b运行ch 名称让我们找到最近的提交 049a12b
。我们使用 child 存储的 parent ID df731ac
来查找 parent,然后我们可以 运行 git diff df731ac 049a12b
——或者更简单地说,git show master
——将 df731ac
与 049a12b
进行比较。
无论这里出现什么不同,049a12b
的作者一定已经更改了它。但是df731ac
(前身或parent提交)是一个完整的快照,而049a12b
(后继或child提交是b运行ch的尖端master
) 也是一个完整的快照。了解这一点有助于理解下一部分。
注意,如上图所示,b运行ch name like master
or develop
or feature/tall
只包含一个特定提交的 ID。我们称此提交为 b运行ch 的 tip commit。当您将新提交添加到 b运行ch 时,Git 所做的是创建新提交,为其提供 ID,然后 将新提交 ID 写入 b运行通道名称。随着时间的推移,b运行ch 命名为 "move":它们总是指向最新的 (child-most) 提交。每个新提交都有,作为它的 parent, 是 之前 b运行ch 的提示的 ID,这让 Git 向后跟随这些通过存储库的指针。
如果 Git 提交哈希 ID 只是一个字母,我们可以绘制一个简单的 three-commit 存储库:
A <-B <-C <-- master
并添加一个新的提交将简单地包括将 C
作为其 parent 编写提交 D
,并使 master
指向 D
:
A--B--C--D <-- master
特殊名称 HEAD
通常包含 b运行ch 的名称。所以如果HEAD
包含master
,Git可以用HEAD
到selectb运行chmaster
,master
找到 D
。换句话说,Git 通常 开始 通过使用 b运行ch 名称来获取提示提交 ID。然后它查看该提交以获取其 parent ID,然后查看 parent 提交以获得另一个 parent,依此类推。这就是 b运行ch 名称的用途,以及它们的作用:它们发现 tip 提交。
使用git diff
所有 git diff
所做的(无论如何大多数时间 1)是采取 任何两个单独的提交 像这样和比较他们。为此,它需要将其两个输入解析为哈希 ID。这些哈希 ID 是两个提交;然后比较两个快照。
当您 运行 git diff master..
、Git 的 diff
t运行 将 master..
设置为 master
和 HEAD
(默认在..
周围填一个for空位置是HEAD
),然后t运行slates master
变成一个b运行ch提示 ID。如果 b运行ch master
的 tip 提交如上图所示为 049a12b
,则比较左半部分的哈希 ID 将为 049a12b
。对于右半部分,git diff
必须读取 HEAD
才能获得其 b运行ch 名称,例如 develop
或 feature/tall
或其他名称。然后 b运行ch 名称映射到它自己的提示提交。假设它的缩写 ID 是 6bc9702
。然后这个 git diff
命令最终告诉 Git 提取 049a12b
的源快照,6bc9702
的源快照,并比较这两个。
但是,您可以为您拥有的任意两个提交提供任意两个哈希值:
git diff 0123456 fedcba9
例如。但是您必须找到那些提交,或者 Git 将变成这些提交的某个名称。
(你说git diff A B
或git diff A..B
都没有关系;这些意思完全一样。这与git log
和大多数其他 Git 命令:只有 git diff
对 two-dot ..
语法有这种特殊处理。但是,规则 t如果缺少其中一个名称,at 将填写 HEAD
,这对 git diff
和其他 Git 命令很常见。)
1Git的git diff
可以产生一种叫做组合差异的东西,但是这些相当复杂, 与此处无关。
简要说明:git show
和 git log -p
我上面提到了git show
。 git show
所做的是自动为您找到 parent 提交,然后首先向您显示 元数据 ——作者(姓名、电子邮件、时间戳)和日志消息——然后是从 parent 到 child.
的差异
当你 运行 git log -p
时,这类似于每次提交时 运行ning git show
,从 child-most 开始并向后工作(注意git log
默认从 HEAD
开始)。也就是说,首先 git log
向您显示当前 b运行ch 的提示提交,如同 git show HEAD
,然后它向您显示提交的 parent 如同 git show
, 然后它会向您显示 parent 的 parent 就像 git show
一样,依此类推。
有一个相当大的区别:git show
将在任何 合并提交 上调用特殊的组合差异机制,而 git log
将只显示日志默认情况下,跳过任何对合并进行差异化的尝试。 (您可以使用一些标志来更改此行为。)
拉取请求
拉取请求更复杂,因为为了 提出 拉取请求,您必须向可以 运行 [=90= 的其他人开放您的存储库]2——这个词就是从这里来的,也是pull request的原意——或者找到或者创建一个共享仓库,push 你的一些提交到这个共享位置,然后请求其他人从共享位置获取你的提交。我将忽略 "pull request" 的原始含义——本质上只是一封要求其他人发送 运行 git fetch
的电子邮件消息——而是跳转到这些网站处理它的方式。
有了像 GitHub 和 Bitbucket 这样的服务,现在至少有 两个 涉及其他存储库。他们甚至 运行 试合并(虽然这不是很重要,除了验证拉取请求是否有意义)。我对 GitHub 比 Bitbucket 更熟悉(我自己使用 GitHub),但两者在这里的工作方式相同,至少从足够高的层次来看是这样。
在考虑拉取请求之前,您必须 "fork" 一个存储库。一个 fork 是一个克隆,但有一些额外的记忆,它是从 从 .3 克隆的存储库,在幕后,以您通常的方式不必关心,4 供应商做了很多 storage-sharing 所以每个叉子在供应商的服务器上占用很少 space。
不过,这种分叉是 两个 涉及额外存储库的原因。这为我们提供了三个我们必须跟踪的存储库:
- 您自己的 Git 存储库,在您的计算机上。这是你的,随你便。这也是你 运行
git diff
的地方。
- 您对原始存储库的复刻。您的机器和您的 Git 会将其称为
origin
遥控器。
原始存储库。这不一定在您的存储库中有任何名称。您可以——也许应该——添加另一个 remote,在其他示例中称为 upstream
。并不总是需要您添加它,但我们假设您已经添加了。如果还没有,运行:
git remote add upstream <url>
您从中分叉 origin
存储库的存储库 URL 在哪里。
我们将在下面提及您的 存储库、您的origin
和您的upstream
。请记住,这些远程名称实际上只是您自己的存储库中的短名称 指的是 另一个 Git 在某些 URL。这就是 remote 的含义:URL 的简称,其中 URL 有一个 Git 存储库。我们将使用 provider 这个词来表示 GitHub 或 Bitbucket。
2git pull
命令的意思是 short-cut 执行 git fetch
后跟第二个 Git 命令,全部通过一个命令。事实证明,分开使用这两个命令通常很重要 — 并非总是如此,但经常如此,以至于像这样组合它们可能是一个错误。可能,现在命名为 git fetch
的命令应该命名为 git pull
,而现在命名为 git pull
的命令可能是您传递给 git fetch
的选项,或者一对方便的快捷命令: git fm
对应 fetch-and-merge,git fr
对应 fetch-and-rebase。我建议新的 Git 用户避免使用 git pull
以支持单独的命令,至少在他们非常熟悉单独的命令之前。尽管如此,这个轻微的历史错误在今天已完全融入 Git,不仅因为 git pull
是 git push
的明显(但不正确)的对立面,而且就其名称而言"pull request".
3这已经超出了——或者也许 "beside" 是一个更好的描述——克隆体通过远程名称记住它们的起源的方式 origin
.在任何情况下,forks 最初更像是镜像克隆,但不像镜像克隆那样从属于它们从中分叉的存储库。所以它们是一种混合体,具有额外的功能——具体来说,包括您可以创建拉取请求的服务版本。
4GitHub 偶尔会在您删除分叉与删除未分叉的存储库时提出这个问题,因为 (a) 他们必须撤消特殊的分叉共享, (b) 删除分叉更安全,因为原始的(你从中分叉的)存储库仍然存在。我想 Bitbucket 是相似的。
提供商拉取请求以 git push
开头
关于 git push
的主要了解是它推送 提交 ,而不是文件。它通过调用其他一些 Git 存储库来完成此操作。然后它找出你有哪些他们没有的提交,给他们你的提交,并要求他们设置一些名称,通常是 b运行ch 名称,以记住特定的提交。
现在,你的分叉在origin
属于你,所以你可以git push
但是随时随地。它是一个真实的、实际的 Git 存储库(或类似存储库的东西),存储在提供者的机器上而不是您自己的机器上,但它就像您自己的 Git 存储库一样,因为它有提交,并且b运行ch 名称,那些 b运行ch 名称指向 tip 提交,指向以前的提交。
当你运行git push
时,你要求设置一个b运行频道名称,比如master
或develop
或feature/tall
,带有提交哈希 ID。如果他们的 Git 没有那个提交,你的 Git 给他们的 Git 这个提交。如果他们的 Git 没有那个提交的 parent,你的 Git 也给他们的 Git parent .这一直持续到你达到他们的 Git does 的一些提交。这些是你们在开始 git push
.
之前分享的内容
您提供给他们的提交哈希 ID 通常是 yourb运行ch 顶端的那个。所以如果你有:
...--H--I--J <-- master
而你 git push origin master
,你正在让你的 Git 呼叫他们的 Git 并说 "I'd like you to set your master
to commit J
"。如果他们的 Git 有他们的 master
指向提交 H
,并且缺少 I
和 J
,你的 Git 给他们 I
和 J
。
可能他们的 Git 有他们的 b运行ch 名称指向一些你 没有 有,或者不在从你的b运行ch开始形成的链。也许他们的 Git 有:
...--H--K <-- master
如果是这样,您要求他们添加 I
和 J
并让他们的主人记住 J
,默认情况下将被拒绝,因为这会导致:
K [abandoned]
/
...--H--I--J <-- master
之后他们将 "lose" 提交 K
,可能是真实的和永远的。不过,由于 origin
处的 Git 属于您,您通常可以使用 force push (git push --force
) 将您的礼貌请求变成命令:是的,将你的 master
设置为 J
即使这样会丢失 K
! (通常这是个坏主意,你不应该这样做它。相反,您应该 git fetch origin
将 K
引入您自己的存储库,然后合并或变基以将 K
与您自己的 I--J
合并。这给了您一个您 可以 礼貌地推送的新的和不同的提交或一组提交,不会丢失 K
。相反,它们将是新提交的纯粹添加。)
请注意,这些更改——通常是纯粹添加新提交,然后移动 b运行ch 名称 "forward"——进入 你的 分支。它们会影响您的 origin
,但 不会影响您的 upstream
。那毕竟不是您的 存储库!您不能直接推送到您的上游。
最后,拉取请求
相反,既然你的新提交在你的 origin
中,它是你的 upstream
的一个分支,你可以做的是发出拉取请求,通常使用一些 web 界面点击按钮。提供者的服务器会知道——如果有必要,你会告诉它——你想在你的 origin
中使用哪个 b运行ch 名称,以及你想使用哪个 b运行ch 名称在你的 upstream
.
中使用
提供商随后将通知实际控制上游的任何人您已发出此拉取请求。因为提供者有你的叉子——你的 origin
——专门与他们的存储库共享,即你的 upstream
,他们将可以直接访问你推送到 b运行ch 的提交,即现在在你的 origin
的 b运行ch 小费。
看到差异
现在我们拥有找到正确差异所需的所有工具。我们想比较 他们的 b运行ch 提示提交,从你选择的 b运行ch 名称开始当你提出拉取请求时,在你 upstream
b运行ch 中提交你在 运行 git push
时设置的提示。如果你前面有那两个hash ID,你可以运行 git diff <their-upstream-tip-hash> <your-origin-tip-hash>
.
但是哈希 ID 非常难看。如果我们可以让 Git 登上 运行 名单,那就太好了——我们可以做到。我跳过了上面 git fetch
的工作原理,但让我们深入研究一下。
使用git fetch
如果您 运行 git fetch upstream
,这会告诉您的 Git 调用 Git,该 Git 在您存储在 [=94] 下的 URL 处应答=].那是你的提供商的上游存储库的 Git,你 forked-from。您的 Git 将调用那个 Git,获取他们拥有但您没有的任何新提交,并将它们放入您的存储库中。然后——这是关键技巧——你的 Git 将设置你的 remote-tracking b运行ch names for upstream
来记录哈希 ID对于他们的每个 b运行ch 提示,根据他们现在拥有的任何东西。
他们的 master
成为您的 upstream/master
。他们的 feature/tall
变成了你的 upstream/feature/tall
。您的 Git 会为您记住这些,并提取它们的任何新提交。
当您 运行 git fetch origin
时同样如此:您的 Git 在 origin
呼叫另一个 Git — 这是您在供应商处的分支— 并加载任何你没有的提交 origin
。然后你的 Git 设置你的 origin/master
记住 origin
的 master
,等等。请注意,当您 git push
到 origin
并向他们提供更新时,您的 Git 知道他们是否 接受 更新。如果他们确实接受了您的更新,您的 Git 会在 origin/master
、origin/develop
下记录新的哈希 ID,依此类推。
因此,只要您的 Git 与 upstream
和 origin
的两个 Git 同步,如果不同步,您可以运行 git fetch
到 upstream
和 origin
更新它——你现在在你自己的存储库中有正确的提交,通过 upstream/theirbranch
和 [=179= 命名].因此,如果您发送了拉取请求,要求您的上游将您的 feature/tall
合并到他们的 develop
,而不是 git diff <magic hash 1> <magic hash 2>
,您可以 git diff upstream/develop origin/feature/tall
.
总结
您需要比较的两个提交是两个 other 存储库中的提交。如果这两个仓库在你自己的仓库中设置为 remotes upstream
和 origin
,并且你的仓库相对于这两个仓库是 up-to-date ,您可以 git diff
或 git log
或 git show
有问题的提交,并使用您的 remote-tracking 名称 upstream/*
和 origin/*
来定位特定的 b运行频道提示。
您可以有不在这些存储库 中的任何一个 中的提交,并且您可以看到如果将这些新提交推送到您自己的 origin
中会发生什么.这使您可以看到如果您推送它们然后发出拉取请求会发生什么:只需比较您的 upstream/*
remote-tracking name tip 提交与您自己的 b运行ch tip 提交。
我想生成一个补丁文件,说明我的分支和主分支之间的差异。但是该分支存在时间很长,所以我只是从 master 进行了合并以使其更新。如果我开始在 Bitbucket 中创建拉取请求,我可以很好地看到差异。但是当我在我的分支上执行 git diff master..
时,我看到显示的差异并不存在。它们是由合并产生的吗?我怎样才能获得与 Bitbucket 相同的差异列表 - 现在只是我的分支和主分支之间的差异?
TL;DR
我不太清楚你的困惑是从哪里开始的,但值得注意的是,使用 git diff
与生成拉取请求有很大不同。最终,它将归结为 运行ning git diff
正确的特定提交。诀窍在于找到正确的提交。
长
存储库中有什么
首先,记住Git保留的是什么。在某种基础层面上,Git 关心的是 源快照 ,以 提交 的形式保存。提交包含某个源代码树的完整快照。提交还包含一些 元数据: 提交人的姓名和电子邮件地址,有时是两个人(作者 和 committer:他们可能是相同的,也可能是分开的)和time-stamps他们是什么时候做的; parent 提交 ID,以便 Git 可以将提交系列呈现为谁(作者)做了什么(见下文)以及何时(时间戳);和一条日志消息,以提供作者对为什么他们做了他们所做的事情的描述。
由于每次提交都是一个完整的快照,为了查看谁实际做了什么,我们必须使用像 git diff
这样的命令。假设我们在 b运行ch master
上连续完成了两次提交,如下所示:
(parent) (child)
df731ac <- 049a12b <-- master
像 master
这样的 b运行ch 名称让我们找到最近的提交 049a12b
。我们使用 child 存储的 parent ID df731ac
来查找 parent,然后我们可以 运行 git diff df731ac 049a12b
——或者更简单地说,git show master
——将 df731ac
与 049a12b
进行比较。
无论这里出现什么不同,049a12b
的作者一定已经更改了它。但是df731ac
(前身或parent提交)是一个完整的快照,而049a12b
(后继或child提交是b运行ch的尖端master
) 也是一个完整的快照。了解这一点有助于理解下一部分。
注意,如上图所示,b运行ch name like master
or develop
or feature/tall
只包含一个特定提交的 ID。我们称此提交为 b运行ch 的 tip commit。当您将新提交添加到 b运行ch 时,Git 所做的是创建新提交,为其提供 ID,然后 将新提交 ID 写入 b运行通道名称。随着时间的推移,b运行ch 命名为 "move":它们总是指向最新的 (child-most) 提交。每个新提交都有,作为它的 parent, 是 之前 b运行ch 的提示的 ID,这让 Git 向后跟随这些通过存储库的指针。
如果 Git 提交哈希 ID 只是一个字母,我们可以绘制一个简单的 three-commit 存储库:
A <-B <-C <-- master
并添加一个新的提交将简单地包括将 C
作为其 parent 编写提交 D
,并使 master
指向 D
:
A--B--C--D <-- master
特殊名称 HEAD
通常包含 b运行ch 的名称。所以如果HEAD
包含master
,Git可以用HEAD
到selectb运行chmaster
,master
找到 D
。换句话说,Git 通常 开始 通过使用 b运行ch 名称来获取提示提交 ID。然后它查看该提交以获取其 parent ID,然后查看 parent 提交以获得另一个 parent,依此类推。这就是 b运行ch 名称的用途,以及它们的作用:它们发现 tip 提交。
使用git diff
所有 git diff
所做的(无论如何大多数时间 1)是采取 任何两个单独的提交 像这样和比较他们。为此,它需要将其两个输入解析为哈希 ID。这些哈希 ID 是两个提交;然后比较两个快照。
当您 运行 git diff master..
、Git 的 diff
t运行 将 master..
设置为 master
和 HEAD
(默认在..
周围填一个for空位置是HEAD
),然后t运行slates master
变成一个b运行ch提示 ID。如果 b运行ch master
的 tip 提交如上图所示为 049a12b
,则比较左半部分的哈希 ID 将为 049a12b
。对于右半部分,git diff
必须读取 HEAD
才能获得其 b运行ch 名称,例如 develop
或 feature/tall
或其他名称。然后 b运行ch 名称映射到它自己的提示提交。假设它的缩写 ID 是 6bc9702
。然后这个 git diff
命令最终告诉 Git 提取 049a12b
的源快照,6bc9702
的源快照,并比较这两个。
但是,您可以为您拥有的任意两个提交提供任意两个哈希值:
git diff 0123456 fedcba9
例如。但是您必须找到那些提交,或者 Git 将变成这些提交的某个名称。
(你说git diff A B
或git diff A..B
都没有关系;这些意思完全一样。这与git log
和大多数其他 Git 命令:只有 git diff
对 two-dot ..
语法有这种特殊处理。但是,规则 t如果缺少其中一个名称,at 将填写 HEAD
,这对 git diff
和其他 Git 命令很常见。)
1Git的git diff
可以产生一种叫做组合差异的东西,但是这些相当复杂, 与此处无关。
简要说明:git show
和 git log -p
我上面提到了git show
。 git show
所做的是自动为您找到 parent 提交,然后首先向您显示 元数据 ——作者(姓名、电子邮件、时间戳)和日志消息——然后是从 parent 到 child.
当你 运行 git log -p
时,这类似于每次提交时 运行ning git show
,从 child-most 开始并向后工作(注意git log
默认从 HEAD
开始)。也就是说,首先 git log
向您显示当前 b运行ch 的提示提交,如同 git show HEAD
,然后它向您显示提交的 parent 如同 git show
, 然后它会向您显示 parent 的 parent 就像 git show
一样,依此类推。
有一个相当大的区别:git show
将在任何 合并提交 上调用特殊的组合差异机制,而 git log
将只显示日志默认情况下,跳过任何对合并进行差异化的尝试。 (您可以使用一些标志来更改此行为。)
拉取请求
拉取请求更复杂,因为为了 提出 拉取请求,您必须向可以 运行 [=90= 的其他人开放您的存储库]2——这个词就是从这里来的,也是pull request的原意——或者找到或者创建一个共享仓库,push 你的一些提交到这个共享位置,然后请求其他人从共享位置获取你的提交。我将忽略 "pull request" 的原始含义——本质上只是一封要求其他人发送 运行 git fetch
的电子邮件消息——而是跳转到这些网站处理它的方式。
有了像 GitHub 和 Bitbucket 这样的服务,现在至少有 两个 涉及其他存储库。他们甚至 运行 试合并(虽然这不是很重要,除了验证拉取请求是否有意义)。我对 GitHub 比 Bitbucket 更熟悉(我自己使用 GitHub),但两者在这里的工作方式相同,至少从足够高的层次来看是这样。
在考虑拉取请求之前,您必须 "fork" 一个存储库。一个 fork 是一个克隆,但有一些额外的记忆,它是从 从 .3 克隆的存储库,在幕后,以您通常的方式不必关心,4 供应商做了很多 storage-sharing 所以每个叉子在供应商的服务器上占用很少 space。
不过,这种分叉是 两个 涉及额外存储库的原因。这为我们提供了三个我们必须跟踪的存储库:
- 您自己的 Git 存储库,在您的计算机上。这是你的,随你便。这也是你 运行
git diff
的地方。 - 您对原始存储库的复刻。您的机器和您的 Git 会将其称为
origin
遥控器。 原始存储库。这不一定在您的存储库中有任何名称。您可以——也许应该——添加另一个 remote,在其他示例中称为
upstream
。并不总是需要您添加它,但我们假设您已经添加了。如果还没有,运行:git remote add upstream <url>
您从中分叉
origin
存储库的存储库 URL 在哪里。
我们将在下面提及您的 存储库、您的origin
和您的upstream
。请记住,这些远程名称实际上只是您自己的存储库中的短名称 指的是 另一个 Git 在某些 URL。这就是 remote 的含义:URL 的简称,其中 URL 有一个 Git 存储库。我们将使用 provider 这个词来表示 GitHub 或 Bitbucket。
2git pull
命令的意思是 short-cut 执行 git fetch
后跟第二个 Git 命令,全部通过一个命令。事实证明,分开使用这两个命令通常很重要 — 并非总是如此,但经常如此,以至于像这样组合它们可能是一个错误。可能,现在命名为 git fetch
的命令应该命名为 git pull
,而现在命名为 git pull
的命令可能是您传递给 git fetch
的选项,或者一对方便的快捷命令: git fm
对应 fetch-and-merge,git fr
对应 fetch-and-rebase。我建议新的 Git 用户避免使用 git pull
以支持单独的命令,至少在他们非常熟悉单独的命令之前。尽管如此,这个轻微的历史错误在今天已完全融入 Git,不仅因为 git pull
是 git push
的明显(但不正确)的对立面,而且就其名称而言"pull request".
3这已经超出了——或者也许 "beside" 是一个更好的描述——克隆体通过远程名称记住它们的起源的方式 origin
.在任何情况下,forks 最初更像是镜像克隆,但不像镜像克隆那样从属于它们从中分叉的存储库。所以它们是一种混合体,具有额外的功能——具体来说,包括您可以创建拉取请求的服务版本。
4GitHub 偶尔会在您删除分叉与删除未分叉的存储库时提出这个问题,因为 (a) 他们必须撤消特殊的分叉共享, (b) 删除分叉更安全,因为原始的(你从中分叉的)存储库仍然存在。我想 Bitbucket 是相似的。
提供商拉取请求以 git push
开头
关于 git push
的主要了解是它推送 提交 ,而不是文件。它通过调用其他一些 Git 存储库来完成此操作。然后它找出你有哪些他们没有的提交,给他们你的提交,并要求他们设置一些名称,通常是 b运行ch 名称,以记住特定的提交。
现在,你的分叉在origin
属于你,所以你可以git push
但是随时随地。它是一个真实的、实际的 Git 存储库(或类似存储库的东西),存储在提供者的机器上而不是您自己的机器上,但它就像您自己的 Git 存储库一样,因为它有提交,并且b运行ch 名称,那些 b运行ch 名称指向 tip 提交,指向以前的提交。
当你运行git push
时,你要求设置一个b运行频道名称,比如master
或develop
或feature/tall
,带有提交哈希 ID。如果他们的 Git 没有那个提交,你的 Git 给他们的 Git 这个提交。如果他们的 Git 没有那个提交的 parent,你的 Git 也给他们的 Git parent .这一直持续到你达到他们的 Git does 的一些提交。这些是你们在开始 git push
.
您提供给他们的提交哈希 ID 通常是 yourb运行ch 顶端的那个。所以如果你有:
...--H--I--J <-- master
而你 git push origin master
,你正在让你的 Git 呼叫他们的 Git 并说 "I'd like you to set your master
to commit J
"。如果他们的 Git 有他们的 master
指向提交 H
,并且缺少 I
和 J
,你的 Git 给他们 I
和 J
。
可能他们的 Git 有他们的 b运行ch 名称指向一些你 没有 有,或者不在从你的b运行ch开始形成的链。也许他们的 Git 有:
...--H--K <-- master
如果是这样,您要求他们添加 I
和 J
并让他们的主人记住 J
,默认情况下将被拒绝,因为这会导致:
K [abandoned]
/
...--H--I--J <-- master
之后他们将 "lose" 提交 K
,可能是真实的和永远的。不过,由于 origin
处的 Git 属于您,您通常可以使用 force push (git push --force
) 将您的礼貌请求变成命令:是的,将你的 master
设置为 J
即使这样会丢失 K
! (通常这是个坏主意,你不应该这样做它。相反,您应该 git fetch origin
将 K
引入您自己的存储库,然后合并或变基以将 K
与您自己的 I--J
合并。这给了您一个您 可以 礼貌地推送的新的和不同的提交或一组提交,不会丢失 K
。相反,它们将是新提交的纯粹添加。)
请注意,这些更改——通常是纯粹添加新提交,然后移动 b运行ch 名称 "forward"——进入 你的 分支。它们会影响您的 origin
,但 不会影响您的 upstream
。那毕竟不是您的 存储库!您不能直接推送到您的上游。
最后,拉取请求
相反,既然你的新提交在你的 origin
中,它是你的 upstream
的一个分支,你可以做的是发出拉取请求,通常使用一些 web 界面点击按钮。提供者的服务器会知道——如果有必要,你会告诉它——你想在你的 origin
中使用哪个 b运行ch 名称,以及你想使用哪个 b运行ch 名称在你的 upstream
.
提供商随后将通知实际控制上游的任何人您已发出此拉取请求。因为提供者有你的叉子——你的 origin
——专门与他们的存储库共享,即你的 upstream
,他们将可以直接访问你推送到 b运行ch 的提交,即现在在你的 origin
的 b运行ch 小费。
看到差异
现在我们拥有找到正确差异所需的所有工具。我们想比较 他们的 b运行ch 提示提交,从你选择的 b运行ch 名称开始当你提出拉取请求时,在你 upstream
b运行ch 中提交你在 运行 git push
时设置的提示。如果你前面有那两个hash ID,你可以运行 git diff <their-upstream-tip-hash> <your-origin-tip-hash>
.
但是哈希 ID 非常难看。如果我们可以让 Git 登上 运行 名单,那就太好了——我们可以做到。我跳过了上面 git fetch
的工作原理,但让我们深入研究一下。
使用git fetch
如果您 运行 git fetch upstream
,这会告诉您的 Git 调用 Git,该 Git 在您存储在 [=94] 下的 URL 处应答=].那是你的提供商的上游存储库的 Git,你 forked-from。您的 Git 将调用那个 Git,获取他们拥有但您没有的任何新提交,并将它们放入您的存储库中。然后——这是关键技巧——你的 Git 将设置你的 remote-tracking b运行ch names for upstream
来记录哈希 ID对于他们的每个 b运行ch 提示,根据他们现在拥有的任何东西。
他们的 master
成为您的 upstream/master
。他们的 feature/tall
变成了你的 upstream/feature/tall
。您的 Git 会为您记住这些,并提取它们的任何新提交。
当您 运行 git fetch origin
时同样如此:您的 Git 在 origin
呼叫另一个 Git — 这是您在供应商处的分支— 并加载任何你没有的提交 origin
。然后你的 Git 设置你的 origin/master
记住 origin
的 master
,等等。请注意,当您 git push
到 origin
并向他们提供更新时,您的 Git 知道他们是否 接受 更新。如果他们确实接受了您的更新,您的 Git 会在 origin/master
、origin/develop
下记录新的哈希 ID,依此类推。
因此,只要您的 Git 与 upstream
和 origin
的两个 Git 同步,如果不同步,您可以运行 git fetch
到 upstream
和 origin
更新它——你现在在你自己的存储库中有正确的提交,通过 upstream/theirbranch
和 [=179= 命名].因此,如果您发送了拉取请求,要求您的上游将您的 feature/tall
合并到他们的 develop
,而不是 git diff <magic hash 1> <magic hash 2>
,您可以 git diff upstream/develop origin/feature/tall
.
总结
您需要比较的两个提交是两个 other 存储库中的提交。如果这两个仓库在你自己的仓库中设置为 remotes upstream
和 origin
,并且你的仓库相对于这两个仓库是 up-to-date ,您可以 git diff
或 git log
或 git show
有问题的提交,并使用您的 remote-tracking 名称 upstream/*
和 origin/*
来定位特定的 b运行频道提示。
您可以有不在这些存储库 中的任何一个 中的提交,并且您可以看到如果将这些新提交推送到您自己的 origin
中会发生什么.这使您可以看到如果您推送它们然后发出拉取请求会发生什么:只需比较您的 upstream/*
remote-tracking name tip 提交与您自己的 b运行ch tip 提交。