git fetch 究竟是做什么的?
What does git fetch exactly do?
编辑:我在提问之前检查了这个What does FETCH_HEAD in Git mean?。
很抱歉原来的不准确问题。
我的问题是 fetch 是如何工作的? fetch 是否丢弃所有当前日志?
这是我的情况:我和我的队友使用同一个只有一个分支的存储库。所以我们必须在推送任何东西之前进行获取。
我们通常这样做:
git status
git add .
git commit -m message1
git fetch origin
git reset head
git status
git add .
git commit -m message
git push
但重置后,我之前的提交(message1
)似乎消失了。
这是正常的还是有什么问题?
我怎样才能访问我的本地历史记录?
它们已同步,但我的本地历史记录不见了。
老员工,算了:最近在学GitCLI
有人告诉我输入“git fetch head
”来跟踪远程分支。
但我想知道这是做什么的?此命令会覆盖我的本地日志吗?
“git fetch
”和“git fetch head
”有什么区别?
您不必进行两次单独的提交,并且git fetch
不会丢弃任何日志。
--o--o--o (origin/master)
\
x--x (master: my local commits)
你应该做的是在 git fetch
命令获取的任何新提交之上重新设置你的本地提交:
git fetch
--o--o--o--O--O (origin/master updated)
\
x--x (master)
git rebase origin/master
--o--o--o--O--O (origin/master updated)
\
x'--x' (master rebased)
git push
--o--o--o--O--O--x'--x' (origin/master, master)
更简单,,我会使用配置:
git config pull.rebase true
git config rebase.autoStash true
然后一个简单的 git pull
将在 origin/master
之上自动重播您的本地提交。那么你可以git push
.
git fetch
本身真的很简单。复杂的部分前后都有。
这里首先要知道的是Git存储提交。事实上,这就是 Git 的本质:它管理提交的集合。这个集合很少收缩: 在大多数情况下,你对这个提交集合所做的唯一事情就是添加新提交.
提交、索引和 work-tree
每个提交都有几条信息,例如作者的姓名和电子邮件地址以及 time-stamp。每次提交还会保存您指定的所有文件的完整快照:这些文件存储在您的 index(也称为 暂存区) 当时你 运行 git commit
。您从其他人那里获得的提交也是如此:他们保存其他用户当时在其他用户索引中的文件 运行 git commit
.
请注意,每个 Git 存储库只有一个索引,至少在最初是这样。该索引与 work-tree 链接。在较新的 Git 版本中,您可以使用 git worktree add
添加额外的 work-tree;每个新的 work-tree 都带有一个新的 index/staging-area。该索引的要点是充当中间 file-holder,位于 "the current commit"(又名 HEAD
)和 work-tree 之间。最初,HEAD
提交和索引通常匹配:它们包含所有提交文件的相同版本。 Git 将文件从 HEAD
复制到索引中,然后从索引复制到 work-tree.
很容易看到 work-tree:它有普通格式的文件,您可以使用计算机上的所有常规工具查看和编辑它们。如果您为 Web 服务器编写 Java 或 Python 代码,或 HTML,则编译器或解释器或 web-server 可以使用 work-tree 文件。存储在索引中的文件,以及存储在每个 Git 提交中的文件,做 not 具有这种形式并且 not 可由编译器使用、口译员、web-server等。
关于提交要记住的另一件事是,一旦文件处于提交状态,它就无法更改。任何提交的任何部分都不能更改。因此,提交是永久性的——或者至少是永久性的,除非它被删除(这可以做到,但很困难,而且通常是不可取的)。然而,索引和 work-tree 中的内容可以随时修改。这就是它们存在的原因:索引几乎是一个 "modifiable commit"(除非它在您 运行 git commit
之前不会保存),并且 work-tree 将文件保存为其余电脑都可以用。1
1没必要同时索引和work-tree. VCS 可以将 work-tree 视为 "modifiable commit"。这就是 Mercurial 所做的;这就是 Mercurial 不需要索引的原因。这可以说是一个更好的设计——但它不是 Git 的工作方式,所以当使用 Git 时,你有一个索引。索引的存在是使 Git 如此之快的很大一部分:没有它,Mercurial 必须是 extra-clever,并且仍然不如 Git.[=144= 快]
提交记住他们的 parent;新提交是 children
当您通过 运行ning git commit
进行 new 提交时,Git 获取索引内容并制作所有内容的永久快照就在那一点上。 (这就是为什么你必须 git add
文件:你从你的 work-tree 复制它们,你已经改变了它们,回到你的索引,这样它们就可以 "photographed" 用于新的快照。)Git 还会收集提交消息,当然还会使用您的姓名和电子邮件地址以及当前时间来进行新提交。
但是Git还在新提交中存储了当前提交的哈希ID。我们说新提交 "points back to" 当前提交。例如,考虑这个简单的 three-commit 存储库:
A <-B <-C <-- master (HEAD)
这里说的是b运行ch namemaster
"points to"第三次提交,我已经标注了C
,而不是使用 Git 中难以理解的哈希 ID 之一,例如 b06d364...
。 (名称 HEAD
指的是 b运行ch 名称,master
。这就是 Git 如何将字符串 HEAD
转换为正确的哈希 ID:Git 跟随 HEAD
到 master
,然后从 master
中读取散列 ID。)提交 C
本身 "points to" — 保留散列 ID of—提交 B
,虽然;并提交 B
指向提交 A
。 (因为 commit A
是有史以来的第一个提交,所以它没有指向更早的提交,所以它根本没有指向任何地方,这使得它有点特殊。这被称为 根提交.)
要进行 new 提交,Git 将索引打包到快照中,将其与您的姓名和电子邮件地址等一起保存,and 包含提交 C
的哈希 ID,以使用新的哈希 ID 进行新提交。我们将使用 D
而不是新的哈希 ID,因为我们不知道新的哈希 ID 是什么:
A <-B <-C <-D
注意 D
如何指向 C
。现在 D
存在,Git 更改 存储在名称 master
下的哈希 ID,改为存储 D
的哈希 ID C
个。存储在 HEAD
中的名称本身并没有改变:它仍然是 master
。所以现在我们有了这个:
A <-B <-C <-D <-- master (HEAD)
你可以从这个图表中看出 Git 是如何工作的:给定一个名字,比如 master
,Git 只需按照箭头找到 latest 提交。该提交有一个指向其早期或 parent 提交的向后箭头,它有另一个指向其自身 parent 的向后箭头,依此类推,贯穿其所有祖先回到根提交。
请注意,虽然 children 记住了他们的 parent,但 parent 提交不记得他们的 children。这是因为 任何提交的任何部分都不能更改: Git 字面上 不能 添加 children 到parent,它甚至都没有尝试。 Git 必须始终向后工作,从新到旧。 commit 的箭头都是自动向后的,所以一般我都不画:
A--B--C--D <-- master (HEAD)
分布式存储库:git fetch
的作用
当我们使用 git fetch
时,我们有 两个不同的 Gits,它们不同但 相关 - 存储库。假设我们在两台不同的计算机上有两个 Git 存储库,它们都以相同的三个提交开始:
A--B--C
因为它们从完全相同的提交开始,所以这三个提交也具有相同的哈希 ID。这部分非常聪明,这就是散列 ID 是这样的原因:散列 ID 是 contents 的校验和2提交,以便任何两个完全相同的提交总是具有 相同 哈希 ID。
现在,你,在你的 Git 和你的存储库中,添加了一个新的提交 D
。与此同时,他们——无论他们是谁——可能已经添加了他们自己的新提交。我们将使用不同的字母,因为它们的提交必然具有不同的哈希值。我们还将主要从您(Harry)的角度来看待这个问题;我们称他们为 "Sally"。我们将在 您的 存储库的图片中再添加一件事:它现在看起来像这样:
A--B--C <-- sally/master
\
D <-- master (HEAD)
现在假设 Sally 进行了两次提交。在 her 存储库中,she 现在有这个:
A--B--C--E--F <-- master (HEAD)
或者也许(如果她从你那里获取,但还没有 运行 git fetch
):
A--B--C <-- harry/master
\
E--F <-- master (HEAD)
当你运行git fetch
,你把你的Git连接到莎莉的Git,问她有没有自提交 C
以来添加到 her master
的任何新提交。她做到了——她有她的新提交 E
和 F
。因此,您的 Git 从她那里获得了这些提交,以及完成这些提交的快照所需的一切。您的 Git 然后将这些提交添加到 您的 存储库,因此您现在拥有:
E--F <-- sally/master
/
A--B--C
\
D <-- master (HEAD)
如您所见,git fetch
为您所做的是收集她所有的 new 提交并将它们添加到您的存储库.
为了记住她master
在哪里,既然你已经和她Git交谈过,你的Git副本她 的主人到你的 sally/master
。你自己的master
,还有你自己的HEAD
,一点也不要变。只有这些 "memory of another Git repository" 名称,Git 调用 remote-tracking b运行ch 名称,更改。
2这个散列是一个加密散列,部分原因是它很难被愚弄 Git,部分原因是加密散列自然表现良好 Git的目的。当前的散列使用 SHA-1, 是 安全的,但已经遇到 brute-force 攻击,现在被放弃用于加密。 Git 可能会转移到 SHA2-256 或 SHA3-256 或其他一些更大的哈希值。会有一个t运行sition period,会有一些不愉快。 :-)
你现在应该合并或变基——git reset
通常是错误的
请注意,从 Sally 获取后,您的 存储库,仅 您的存储库,其中包含来自你们俩。 Sally 仍然没有你的新提交 D
.
即使不是 "Sally",您的另一个 Git 被称为 origin
,这仍然是正确的。现在您同时拥有 master
和 origin/master
,您必须做一些事情来将您的新提交 D
与他们最新的提交 F
:
连接起来
A--B--C--D <-- master (HEAD)
\
E--F <-- origin/master
(出于 graph-drawing 的原因,我将 D
移到了顶部,但这与之前的图表相同,
您在这里的主要两个选择是使用 git merge
或 git rebase
。 (还有其他方法可以做到这一点,但要学习的是这两种方法。)
Merge 实际上更简单,因为 git rebase
做的事情涉及合并的动词形式,to merg。 git merge
所做的是 运行 合并的动词形式,然后将结果作为 new 提交提交,称为 merge 提交 或简称 "a merge",这是合并的名词形式。我们可以这样绘制新的合并提交 G
:
A--B--C--D---G <-- master (HEAD)
\ /
E--F <-- origin/master
与常规提交不同,合并提交有两个 parents.3 它连接回用于进行合并的两个早期提交。这使得将你的新提交 G
推送到 origin
成为可能:G
带走了你的 D
,但也连接回他们的 F
,所以他们的Git 这个新更新没问题。
此合并与合并两个 b运行ches 得到的合并相同。事实上,您 确实 合并了两个 b运行 分支:您将 master
与 Sally 的(或 origin
的)合并 master
.
使用git rebase
通常很容易,但它的作用比较复杂。而不是合并你的提交D
和他们的提交F
来创建一个新的合并提交G
, git rebase
所做的是 copy 您的每个提交,以便新的 copies,它们是新的和不同的提交,在您的 upstream.
上的最新提交
在这里,您的上游是 origin/master
,而您拥有而他们没有的提交只是您的一个提交 D
。所以 git rebase
制作了 D
的 copy,我称之为 D'
,将副本放在他们的提交 F
之后,这样D'
的 parent 是 F
。中间图如下所示:5
A--B--C--D <-- master
\
E--F <-- origin/master
\
D' <-- HEAD
复制过程使用与 git merge
相同的合并代码来执行动词形式,合并,提交 D
的更改。 4 然而,一旦复制完成,变基代码发现没有更多的提交要复制,所以它然后 更改 你的 master
b运行ch 指向最终复制的提交 D'
:
A--B--C--D [abandoned]
\
E--F <-- origin/master
\
D' <-- master (HEAD)
这放弃了原来的提交 D
.6 这意味着我们也可以停止绘制它,所以现在我们得到:
A--B--C--E--F <-- origin/master
\
D' <-- master (HEAD)
现在很容易 git push
您的新提交 D'
回到 origin
。
3在Git(但不是Mercurial)中,合并提交可以有两个以上的parent。重复合并做不了的事,主要是炫耀一下。 :-)
4从技术上讲,至少在这种情况下,合并基础提交是提交 C
并且两个提示提交是 D
和 F
,所以在这种情况下,它实际上是完全一样的。如果你 rebase 不止一次提交,它会变得有点复杂,但原则上它仍然很简单。
5这种中间状态,其中 HEAD
与 master
分离,通常是不可见的。只有在 verb-form-of-merge 期间出现问题时您才会看到它,因此 Git 停止并且必须从您那里获得帮助才能完成合并操作。但是,当 确实 发生时——当在变基期间存在合并冲突时——知道 Git 处于此 "detached HEAD" 状态很重要,但只要rebase 自己完成,你不必太关心这个。
6原始提交链通过Git的reflogs和名称[=120=暂时保留]. ORIG_HEAD
值被下一个生成 "big change" 的操作覆盖,并且 reflog 条目最终会过期,通常是在该条目的 30 天后。之后,git gc
将真正删除原始提交链。
git pull
命令只是 运行s git fetch
然后是第二个命令
请注意,在 git fetch
之后,您通常必须 运行 第二个 Git 命令,git merge
或 git rebase
。
如果您事先知道您肯定会立即使用这两个命令之一,您可以使用 git pull
,其中 运行s git fetch
然后 运行s 这两个命令之一。您可以通过设置 pull.rebase
或提供 --rebase
作为 command-line 选项来选择 second 命令到 运行。
但是,在您非常熟悉 git merge
和 git rebase
的工作原理之前,我建议 不要 使用 git pull
,因为有时 git merge
和 git rebase
无法自行完成。在这种情况下,您必须知道如何处理这种失败。您 必须 知道您实际 运行 哪个命令。如果您自己 运行 命令,您将知道您 运行 哪个命令,以及必要时到哪里寻求帮助。如果你运行git pull
,你可能连第二条命令都不知道运行!
除此之外,有时您可能希望在 运行 第二个命令之前 查找 。 git fetch
带来了多少提交?进行合并与变基需要多少工作量?现在 merge 比 rebase 好,还是 rebase 比 merge 好?回答任何这些问题,你必须将git fetch
步骤与第二个命令分开。如果你使用 git pull
,你 必须 提前决定要 运行 哪个命令,然后你甚至不知道要使用哪个命令。
简而言之,只有在熟悉了它的两个部分的方式后才使用 git pull
——git fetch
,并且你选择的第二个命令——真的有效。
编辑:我在提问之前检查了这个What does FETCH_HEAD in Git mean?。
很抱歉原来的不准确问题。
我的问题是 fetch 是如何工作的? fetch 是否丢弃所有当前日志?
这是我的情况:我和我的队友使用同一个只有一个分支的存储库。所以我们必须在推送任何东西之前进行获取。
我们通常这样做:
git status
git add .
git commit -m message1
git fetch origin
git reset head
git status
git add .
git commit -m message
git push
但重置后,我之前的提交(message1
)似乎消失了。
这是正常的还是有什么问题?
我怎样才能访问我的本地历史记录?
它们已同步,但我的本地历史记录不见了。
老员工,算了:最近在学GitCLI
有人告诉我输入“git fetch head
”来跟踪远程分支。
但我想知道这是做什么的?此命令会覆盖我的本地日志吗?
“git fetch
”和“git fetch head
”有什么区别?
您不必进行两次单独的提交,并且git fetch
不会丢弃任何日志。
--o--o--o (origin/master)
\
x--x (master: my local commits)
你应该做的是在 git fetch
命令获取的任何新提交之上重新设置你的本地提交:
git fetch
--o--o--o--O--O (origin/master updated)
\
x--x (master)
git rebase origin/master
--o--o--o--O--O (origin/master updated)
\
x'--x' (master rebased)
git push
--o--o--o--O--O--x'--x' (origin/master, master)
更简单,
git config pull.rebase true
git config rebase.autoStash true
然后一个简单的 git pull
将在 origin/master
之上自动重播您的本地提交。那么你可以git push
.
git fetch
本身真的很简单。复杂的部分前后都有。
这里首先要知道的是Git存储提交。事实上,这就是 Git 的本质:它管理提交的集合。这个集合很少收缩: 在大多数情况下,你对这个提交集合所做的唯一事情就是添加新提交.
提交、索引和 work-tree
每个提交都有几条信息,例如作者的姓名和电子邮件地址以及 time-stamp。每次提交还会保存您指定的所有文件的完整快照:这些文件存储在您的 index(也称为 暂存区) 当时你 运行 git commit
。您从其他人那里获得的提交也是如此:他们保存其他用户当时在其他用户索引中的文件 运行 git commit
.
请注意,每个 Git 存储库只有一个索引,至少在最初是这样。该索引与 work-tree 链接。在较新的 Git 版本中,您可以使用 git worktree add
添加额外的 work-tree;每个新的 work-tree 都带有一个新的 index/staging-area。该索引的要点是充当中间 file-holder,位于 "the current commit"(又名 HEAD
)和 work-tree 之间。最初,HEAD
提交和索引通常匹配:它们包含所有提交文件的相同版本。 Git 将文件从 HEAD
复制到索引中,然后从索引复制到 work-tree.
很容易看到 work-tree:它有普通格式的文件,您可以使用计算机上的所有常规工具查看和编辑它们。如果您为 Web 服务器编写 Java 或 Python 代码,或 HTML,则编译器或解释器或 web-server 可以使用 work-tree 文件。存储在索引中的文件,以及存储在每个 Git 提交中的文件,做 not 具有这种形式并且 not 可由编译器使用、口译员、web-server等。
关于提交要记住的另一件事是,一旦文件处于提交状态,它就无法更改。任何提交的任何部分都不能更改。因此,提交是永久性的——或者至少是永久性的,除非它被删除(这可以做到,但很困难,而且通常是不可取的)。然而,索引和 work-tree 中的内容可以随时修改。这就是它们存在的原因:索引几乎是一个 "modifiable commit"(除非它在您 运行 git commit
之前不会保存),并且 work-tree 将文件保存为其余电脑都可以用。1
1没必要同时索引和work-tree. VCS 可以将 work-tree 视为 "modifiable commit"。这就是 Mercurial 所做的;这就是 Mercurial 不需要索引的原因。这可以说是一个更好的设计——但它不是 Git 的工作方式,所以当使用 Git 时,你有一个索引。索引的存在是使 Git 如此之快的很大一部分:没有它,Mercurial 必须是 extra-clever,并且仍然不如 Git.[=144= 快]
提交记住他们的 parent;新提交是 children
当您通过 运行ning git commit
进行 new 提交时,Git 获取索引内容并制作所有内容的永久快照就在那一点上。 (这就是为什么你必须 git add
文件:你从你的 work-tree 复制它们,你已经改变了它们,回到你的索引,这样它们就可以 "photographed" 用于新的快照。)Git 还会收集提交消息,当然还会使用您的姓名和电子邮件地址以及当前时间来进行新提交。
但是Git还在新提交中存储了当前提交的哈希ID。我们说新提交 "points back to" 当前提交。例如,考虑这个简单的 three-commit 存储库:
A <-B <-C <-- master (HEAD)
这里说的是b运行ch namemaster
"points to"第三次提交,我已经标注了C
,而不是使用 Git 中难以理解的哈希 ID 之一,例如 b06d364...
。 (名称 HEAD
指的是 b运行ch 名称,master
。这就是 Git 如何将字符串 HEAD
转换为正确的哈希 ID:Git 跟随 HEAD
到 master
,然后从 master
中读取散列 ID。)提交 C
本身 "points to" — 保留散列 ID of—提交 B
,虽然;并提交 B
指向提交 A
。 (因为 commit A
是有史以来的第一个提交,所以它没有指向更早的提交,所以它根本没有指向任何地方,这使得它有点特殊。这被称为 根提交.)
要进行 new 提交,Git 将索引打包到快照中,将其与您的姓名和电子邮件地址等一起保存,and 包含提交 C
的哈希 ID,以使用新的哈希 ID 进行新提交。我们将使用 D
而不是新的哈希 ID,因为我们不知道新的哈希 ID 是什么:
A <-B <-C <-D
注意 D
如何指向 C
。现在 D
存在,Git 更改 存储在名称 master
下的哈希 ID,改为存储 D
的哈希 ID C
个。存储在 HEAD
中的名称本身并没有改变:它仍然是 master
。所以现在我们有了这个:
A <-B <-C <-D <-- master (HEAD)
你可以从这个图表中看出 Git 是如何工作的:给定一个名字,比如 master
,Git 只需按照箭头找到 latest 提交。该提交有一个指向其早期或 parent 提交的向后箭头,它有另一个指向其自身 parent 的向后箭头,依此类推,贯穿其所有祖先回到根提交。
请注意,虽然 children 记住了他们的 parent,但 parent 提交不记得他们的 children。这是因为 任何提交的任何部分都不能更改: Git 字面上 不能 添加 children 到parent,它甚至都没有尝试。 Git 必须始终向后工作,从新到旧。 commit 的箭头都是自动向后的,所以一般我都不画:
A--B--C--D <-- master (HEAD)
分布式存储库:git fetch
的作用
当我们使用 git fetch
时,我们有 两个不同的 Gits,它们不同但 相关 - 存储库。假设我们在两台不同的计算机上有两个 Git 存储库,它们都以相同的三个提交开始:
A--B--C
因为它们从完全相同的提交开始,所以这三个提交也具有相同的哈希 ID。这部分非常聪明,这就是散列 ID 是这样的原因:散列 ID 是 contents 的校验和2提交,以便任何两个完全相同的提交总是具有 相同 哈希 ID。
现在,你,在你的 Git 和你的存储库中,添加了一个新的提交 D
。与此同时,他们——无论他们是谁——可能已经添加了他们自己的新提交。我们将使用不同的字母,因为它们的提交必然具有不同的哈希值。我们还将主要从您(Harry)的角度来看待这个问题;我们称他们为 "Sally"。我们将在 您的 存储库的图片中再添加一件事:它现在看起来像这样:
A--B--C <-- sally/master
\
D <-- master (HEAD)
现在假设 Sally 进行了两次提交。在 her 存储库中,she 现在有这个:
A--B--C--E--F <-- master (HEAD)
或者也许(如果她从你那里获取,但还没有 运行 git fetch
):
A--B--C <-- harry/master
\
E--F <-- master (HEAD)
当你运行git fetch
,你把你的Git连接到莎莉的Git,问她有没有自提交 C
以来添加到 her master
的任何新提交。她做到了——她有她的新提交 E
和 F
。因此,您的 Git 从她那里获得了这些提交,以及完成这些提交的快照所需的一切。您的 Git 然后将这些提交添加到 您的 存储库,因此您现在拥有:
E--F <-- sally/master
/
A--B--C
\
D <-- master (HEAD)
如您所见,git fetch
为您所做的是收集她所有的 new 提交并将它们添加到您的存储库.
为了记住她master
在哪里,既然你已经和她Git交谈过,你的Git副本她 的主人到你的 sally/master
。你自己的master
,还有你自己的HEAD
,一点也不要变。只有这些 "memory of another Git repository" 名称,Git 调用 remote-tracking b运行ch 名称,更改。
2这个散列是一个加密散列,部分原因是它很难被愚弄 Git,部分原因是加密散列自然表现良好 Git的目的。当前的散列使用 SHA-1, 是 安全的,但已经遇到 brute-force 攻击,现在被放弃用于加密。 Git 可能会转移到 SHA2-256 或 SHA3-256 或其他一些更大的哈希值。会有一个t运行sition period,会有一些不愉快。 :-)
你现在应该合并或变基——git reset
通常是错误的
请注意,从 Sally 获取后,您的 存储库,仅 您的存储库,其中包含来自你们俩。 Sally 仍然没有你的新提交 D
.
即使不是 "Sally",您的另一个 Git 被称为 origin
,这仍然是正确的。现在您同时拥有 master
和 origin/master
,您必须做一些事情来将您的新提交 D
与他们最新的提交 F
:
A--B--C--D <-- master (HEAD)
\
E--F <-- origin/master
(出于 graph-drawing 的原因,我将 D
移到了顶部,但这与之前的图表相同,
您在这里的主要两个选择是使用 git merge
或 git rebase
。 (还有其他方法可以做到这一点,但要学习的是这两种方法。)
Merge 实际上更简单,因为 git rebase
做的事情涉及合并的动词形式,to merg。 git merge
所做的是 运行 合并的动词形式,然后将结果作为 new 提交提交,称为 merge 提交 或简称 "a merge",这是合并的名词形式。我们可以这样绘制新的合并提交 G
:
A--B--C--D---G <-- master (HEAD)
\ /
E--F <-- origin/master
与常规提交不同,合并提交有两个 parents.3 它连接回用于进行合并的两个早期提交。这使得将你的新提交 G
推送到 origin
成为可能:G
带走了你的 D
,但也连接回他们的 F
,所以他们的Git 这个新更新没问题。
此合并与合并两个 b运行ches 得到的合并相同。事实上,您 确实 合并了两个 b运行 分支:您将 master
与 Sally 的(或 origin
的)合并 master
.
使用git rebase
通常很容易,但它的作用比较复杂。而不是合并你的提交D
和他们的提交F
来创建一个新的合并提交G
, git rebase
所做的是 copy 您的每个提交,以便新的 copies,它们是新的和不同的提交,在您的 upstream.
在这里,您的上游是 origin/master
,而您拥有而他们没有的提交只是您的一个提交 D
。所以 git rebase
制作了 D
的 copy,我称之为 D'
,将副本放在他们的提交 F
之后,这样D'
的 parent 是 F
。中间图如下所示:5
A--B--C--D <-- master
\
E--F <-- origin/master
\
D' <-- HEAD
复制过程使用与 git merge
相同的合并代码来执行动词形式,合并,提交 D
的更改。 4 然而,一旦复制完成,变基代码发现没有更多的提交要复制,所以它然后 更改 你的 master
b运行ch 指向最终复制的提交 D'
:
A--B--C--D [abandoned]
\
E--F <-- origin/master
\
D' <-- master (HEAD)
这放弃了原来的提交 D
.6 这意味着我们也可以停止绘制它,所以现在我们得到:
A--B--C--E--F <-- origin/master
\
D' <-- master (HEAD)
现在很容易 git push
您的新提交 D'
回到 origin
。
3在Git(但不是Mercurial)中,合并提交可以有两个以上的parent。重复合并做不了的事,主要是炫耀一下。 :-)
4从技术上讲,至少在这种情况下,合并基础提交是提交 C
并且两个提示提交是 D
和 F
,所以在这种情况下,它实际上是完全一样的。如果你 rebase 不止一次提交,它会变得有点复杂,但原则上它仍然很简单。
5这种中间状态,其中 HEAD
与 master
分离,通常是不可见的。只有在 verb-form-of-merge 期间出现问题时您才会看到它,因此 Git 停止并且必须从您那里获得帮助才能完成合并操作。但是,当 确实 发生时——当在变基期间存在合并冲突时——知道 Git 处于此 "detached HEAD" 状态很重要,但只要rebase 自己完成,你不必太关心这个。
6原始提交链通过Git的reflogs和名称[=120=暂时保留]. ORIG_HEAD
值被下一个生成 "big change" 的操作覆盖,并且 reflog 条目最终会过期,通常是在该条目的 30 天后。之后,git gc
将真正删除原始提交链。
git pull
命令只是 运行s git fetch
然后是第二个命令
请注意,在 git fetch
之后,您通常必须 运行 第二个 Git 命令,git merge
或 git rebase
。
如果您事先知道您肯定会立即使用这两个命令之一,您可以使用 git pull
,其中 运行s git fetch
然后 运行s 这两个命令之一。您可以通过设置 pull.rebase
或提供 --rebase
作为 command-line 选项来选择 second 命令到 运行。
但是,在您非常熟悉 git merge
和 git rebase
的工作原理之前,我建议 不要 使用 git pull
,因为有时 git merge
和 git rebase
无法自行完成。在这种情况下,您必须知道如何处理这种失败。您 必须 知道您实际 运行 哪个命令。如果您自己 运行 命令,您将知道您 运行 哪个命令,以及必要时到哪里寻求帮助。如果你运行git pull
,你可能连第二条命令都不知道运行!
除此之外,有时您可能希望在 运行 第二个命令之前 查找 。 git fetch
带来了多少提交?进行合并与变基需要多少工作量?现在 merge 比 rebase 好,还是 rebase 比 merge 好?回答任何这些问题,你必须将git fetch
步骤与第二个命令分开。如果你使用 git pull
,你 必须 提前决定要 运行 哪个命令,然后你甚至不知道要使用哪个命令。
简而言之,只有在熟悉了它的两个部分的方式后才使用 git pull
——git fetch
,并且你选择的第二个命令——真的有效。