-
1. 起步
-
2. Git 基础
-
3. Git 分支
-
4. 服务器上的 Git
- 4.1 协议
- 4.2 在服务器上部署 Git
- 4.3 生成 SSH 公钥
- 4.4 架设服务器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管服务
- 4.10 小结
-
5. 分布式 Git
-
A1. 附录 A: Git 在其他环境
- A1.1 图形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小结
-
A2. 附录 B: 在应用程序中嵌入 Git
-
A3. 附录 C: Git 命令
3.2 Git 分支 - 基本的分支与合并
基本的分支与合并
我们来过一遍在实际场景中会用到的分支与合并的简单例子。你会遵循以下步骤:
-
在一个网站上做一些工作。
-
为一个正在处理的新用户故事创建一个分支。
-
在该分支上做一些工作。
在这个阶段,你会接到一个电话,说另一个问题很关键,你需要一个热修复。你会做以下事情:
-
切换到你的生产分支。
-
创建一个分支来添加热修复。
-
测试通过后,合并热修复分支,并推送到生产环境。
-
切换回你原来的用户故事,继续工作。
基本的分支
首先,假设你正在你的项目上工作,并且已经在 `master` 分支上提交了几次。
你已经决定要处理公司使用的任何问题跟踪系统中的问题 #53。要同时创建一个新分支并切换到它,你可以运行带有 `-b` 选项的 `git checkout` 命令:
$ git checkout -b iss53
Switched to a new branch "iss53"
这相当于:
$ git branch iss53
$ git checkout iss53
你在你的网站上工作并做了一些提交。这样做会使 `iss53` 分支向前移动,因为你已将其检出(也就是说,你的 `HEAD` 指向它):
$ vim index.html
$ git commit -a -m 'Create new footer [issue 53]'
现在你接到电话说网站有问题,需要立即修复。使用 Git,你无需将你的修复与你所做的 `iss53` 更改一起部署,也无需在将你的修复应用到生产环境之前,费力地撤销这些更改。你只需切换回你的 `master` 分支。
但是,在你这样做之前,请注意,如果你的工作目录或暂存区有未提交的更改与你正在检出的分支冲突,Git 将不允许你切换分支。切换分支时最好有一个干净的工作状态。有一些方法可以解决这个问题(即,暂存和提交修订),我们将在稍后的 暂存和清理 中介绍。现在,我们假设你已提交了所有更改,因此你可以切换回你的 `master` 分支:
$ git checkout master
Switched to branch 'master'
此时,你的项目工作目录与你开始处理问题 #53 之前完全一样,你可以专注于你的热修复。这是一个重要的记忆点:当你切换分支时,Git 会重置你的工作目录,使其看起来像你上次在该分支上提交时的样子。它会自动添加、删除和修改文件,以确保你的工作副本是该分支在你上次提交时的样子。
接下来,你需要进行热修复。让我们创建一个 `hotfix` 分支,以便在该分支上工作,直到它完成:
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'Fix broken email address'
[hotfix 1fb7853] Fix broken email address
1 file changed, 2 insertions(+)
你可以运行你的测试,确保热修复是你想要的,最后将 `hotfix` 分支合并回你的 `master` 分支以部署到生产环境。你使用 `git merge` 命令来完成此操作:
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
你会在该合并中注意到“快进”这个短语。因为你合并的 `hotfix` 分支所指向的提交 `C4` 直接位于你所在提交 `C2` 的前面,Git 只是将指针向前移动。换句话说,当你尝试将一个提交与可以通过跟踪第一个提交的历史记录到达的提交合并时,Git 通过向前移动指针来简化事情,因为没有要合并的不同工作——这称为“快进”。
你的更改现在位于 `master` 分支指向的提交快照中,你可以部署修复了。
在你的超级重要修复部署之后,你已准备好切换回你中断之前正在做的工作。但是,首先你将删除 `hotfix` 分支,因为你不再需要它——`master` 分支指向同一个位置。你可以使用 `git branch` 的 `-d` 选项删除它:
$ git branch -d hotfix
Deleted branch hotfix (3a0874c).
现在你可以切换回你正在处理的问题 #53 的工作分支,并继续在该分支上工作。
$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'Finish the new footer [issue 53]'
[iss53 ad82d7a] Finish the new footer [issue 53]
1 file changed, 1 insertion(+)
值得注意的是,你在 `hotfix` 分支中做的工作不包含在 `iss53` 分支的文件中。如果你需要将其拉入,你可以通过运行 `git merge master` 将你的 `master` 分支合并到你的 `iss53` 分支中,或者你可以等到稍后决定将 `iss53` 分支拉回 `master` 时再集成这些更改。
基本合并
假设你已决定你的问题 #53 工作已完成并准备好合并到你的 `master` 分支中。为了做到这一点,你将你的 `iss53` 分支合并到 `master` 中,就像你之前合并你的 `hotfix` 分支一样。你所要做的就是检出你希望合并到的分支,然后运行 `git merge` 命令:
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)
这看起来与你之前做的 `hotfix` 合并有些不同。在这种情况下,你的开发历史已从某个较旧的点分叉。因为你所在分支上的提交不是你正在合并的分支的直接祖先,Git 必须做一些工作。在这种情况下,Git 会进行简单的三向合并,使用分支提示指向的两个快照和两者的共同祖先。
Git 不仅仅是向前移动分支指针,它还会创建一个新的快照,该快照是此三向合并的结果,并自动创建一个指向它的新提交。这被称为合并提交,它的特别之处在于它有多个父级。
现在你的工作已合并,你不再需要 `iss53` 分支。你可以在你的问题跟踪系统中关闭该问题,并删除该分支:
$ git branch -d iss53
基本合并冲突
有时,这个过程并不顺利。如果你在合并的两个分支中以不同的方式更改了同一个文件的相同部分,Git 将无法干净地合并它们。如果你对问题 #53 的修复修改了文件的相同部分,而 `hotfix` 分支也修改了该部分,你将得到一个合并冲突,它看起来像这样:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Git 没有自动创建一个新的合并提交。它已暂停该过程,直到你解决冲突。如果你想在合并冲突后随时查看哪些文件未合并,你可以运行 `git status`:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
任何有合并冲突且尚未解决的文件都列为未合并。Git 会将标准冲突解决标记添加到有冲突的文件中,因此你可以手动打开它们并解决这些冲突。你的文件包含一个看起来像这样的部分:
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
这意味着 `HEAD` 中的版本(你的 `master` 分支,因为那是在你运行合并命令时你已检出的)是该块的顶部部分(`=======` 之上的所有内容),而你的 `iss53` 分支中的版本看起来像底部部分的所有内容。为了解决冲突,你必须选择其中一边或另一边,或者自己合并内容。例如,你可能会通过用以下内容替换整个块来解决此冲突:
<div id="footer">
please contact us at email.support@github.com
</div>
这个解决方案包含每个部分的一些内容,并且 `<<<<<<<`、`=======` 和 `>>>>>>>` 行已被完全删除。在你解决了每个有冲突文件中的每个这些部分之后,对每个文件运行 `git add` 以将其标记为已解决。暂存文件会在 Git 中将其标记为已解决。
如果你想使用图形工具来解决这些问题,你可以运行 `git mergetool`,它会启动一个适当的可视化合并工具,并引导你解决冲突:
$ git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html
Normal merge conflict for 'index.html':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (opendiff):
如果你想使用除默认工具(Git 在这种情况下选择了 `opendiff`,因为该命令是在 macOS 上运行的)之外的合并工具,你可以在“one of the following tools”上方看到所有支持的工具列表。只需键入你更想使用的工具的名称。
|
注意
|
如果你需要更高级的工具来解决棘手的合并冲突,我们将在 高级合并 中介绍更多关于合并的内容。 |
退出合并工具后,Git 会询问你合并是否成功。如果你告诉脚本它成功了,它会暂存文件以将其标记为已为你解决。你可以再次运行 `git status` 以验证所有冲突都已解决:
$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.html
如果你对此感到满意,并且你验证了所有有冲突的文件都已暂存,你可以键入 `git commit` 来完成合并提交。提交消息默认看起来像这样:
Merge branch 'iss53'
Conflicts:
index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
# modified: index.html
#
如果你认为对将来查看此合并的其他人会有帮助,你可以修改此提交消息,详细说明你如何解决合并,并解释你所做更改的原因(如果这些不明显)。