git merge
merging
你在一个分支上完成了一个新的feature,想把这个feature纳入到主分支里面,这样所有人都可以用了,你可以使用命令 git merge 或者 git pull.
这两条命令的语法格式如下:
git merge [head]
git pull . [head]
他们的结果是一样的(虽然merge命令看上去要简洁一点,在多个开发人员讨论时,pull 形式的原因将变得显而易见。)
这些命令执行了下面这些操作,我们把当前所在分支称为 current, 把要合并的分支称之为merge.
- 识别当前分支和要合并的分支所拥有的相同部分的历史提交,我们称之为 ancestor-commit.
- 最简单的情况,如果 ancestor-commit 等于 要合并的分支 merge. 那就什么都不做。 如果 ancestor-commit 等于当前所在的分支,那就执行快速移动合并。(fastforward merge).
- 否则,识别 ancestor-commit 和要合并的merge 分支之间的差异部分。
- 尝试着将差异部分中的变更纳入到 当前 current 分支的文件中。
- 如果没有冲突,就创建一个新的提交 commit. 这个提交有两个 parent. 也就是 current 和 merge. 将当前分支 current (还有当前分支的头指针 head) 指向这个新的提交 commit. 同时修改相应的工程文件。
- 如果有冲突,插入相应的冲突标记并通知用户。不创建提交。
重要提示:当您要求文件执行合并时,如果文件中存在未提交的更改,Git 可能会非常困惑。因此,请确保在合并之前提交到目前为止所做的任何更改。
因此,要完成上述示例,请再次签出主头并完成为论文编写新数据。现在,您希望引入对标头所做的这些更改。
存储库如下所示:
+---------- (D)
/ |
(A) -- (B) -- (C) -------------- (E)
| |
fix-headers master
|
HEAD
其中 (E) 是反映新数据已完成版本的提交。
您将运行:
git merge fix-headers
如果没有冲突,则生成的存储库如下所示:
+---------- (D) ---------------+
/ | \
(A) -- (B) -- (C) -------------- (E) -- (F)
| |
fix-headers master
|
HEAD
合并提交是 (F),具有父项 (D) 和 (E)。因为 (B) 是 (D) 和 (E) 之间的共同祖先,因此 (F) 中的文件应包含 (B) 和 (D) 之间的更改,即包含在 (E) 文件中的标题修复。
术语说明:当我说"将header A 合并到hear B"时,我的意思是header B 是当前头(current header),您正在从 header A 到它中绘制更改。header B 更新; header A 不执行任何操作.(如果将"合并"一词替换为"拉",则可能更有意义。
Resolving Conflicts
如果要合并的 commit 在一个位置发生更改,并且当前 commit 在同一位置发生更改,则会产生冲突。Git 无法告诉哪些更改应优先。
要解决提交,请编辑文件以修复冲突更改。然后运行 git add 以添加已解决的文件,并运行 git commit 以提交修复的合并。Git 记得您正处于合并中,因此它正确设置提交父项。
Fast Forward Merges
快速合并提交是 merge 的一种优化。 假设你的 repository 看上去像这样子:
+-- (D) -- (E)
/ |
(A) -- (B) -- (C) |
| |
current to-merge
|
HEAD
接着你运行 git merge to-merge. 在这个情况下,Git 只需要将 current HEAD 从 C 移除,并指向 E 就可以了。因为 C 所在的分支的所有 commit 都已经包含在 E 的 历史提交当中了。没有差异, 也就不需要 merge 了。因此,合并后的 repository 看上去像这样子:
+-- (D) -- (E)
/ |
(A) -- (B) -- (C) |
|
to-merge, current
|
HEAD
也就是说, to-merge 和 current 分支的 HEAD 都同时指向了 commit E. 并且 current 的 HEAD 仍然指向 current.
注意一个重要的区别:这个合并没有创建任何 commit. Git 只移动头部指针。
Common Merge Use patterns
合并两个分支有两个常见原因。第一种,如上所述,是从新功能分支到主分支的更改。
第二种使用模式是将主分支绘制到正在开发的特征分支中。这样,功能分支将最新的 Bug 修复和新功能添加到主分支中,使功能分支保持最新状态。定期执行此操作可降低将要素合并到主分支时产生冲突的风险。
执行上述功能的一个缺点是,您的功能分支最终将出现大量合并提交。解决这个问题的一个替代方案是 git rebase ,尽管这本身也伴随着问题。
Deleting a branch
将开发分支合并到主分支后,您可能不再需要开发分支。因此,您可能需要删除它,以便它不会使 git 分支列表混乱。
要删除分支,请使用 git branch -d [branch_name]。这只需从存储库的头部列表中删除指定的头。
例如,在上面的这个存储库中:
例如,在上面的这个存储库中:
我们可能不再需要修复头。因此,我们可以使用:
git branch -d fix-headers
生成的存储库如下所示:
+---------- (D) ---------------+
/ \
(A) -- (B) -- (C) -------------- (E) -- (F)
|
master
|
HEAD
重要提示:如果无法从其他头访问要删除的分支,git branch -d 将导致错误。为什么?请考虑以下存储库:
+----------- (E)
/ |
(A) -- (B) -- (C) |
| |
head1 head2
假设您删除了 head2。现在如何使用 commit(E)?你不能检查出来,因为它不是一个head。并且它不会出现在任何日志或其他地方,因为它不是 head1 的祖先。因此,commit (E) 实际上是无用的。在 Git 术语中,它是一个"悬空提交"(“dangling commit”),其信息将丢失。
Git 确实允许您使用 -D 选项强制删除将创建悬空提交的分支。但是,您应该这样做的情况很少见。
Think very carefully before using git branch -D.