在日常工作中我们经常遇到分支创建与合并的。下面让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。你将经历如下步骤:
开发某个项目。为实现某个新的需求,创建一个分支。在这个分支上开展工作。正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。你将按照如下方式来处理:
切换到你的线上分支(production branch)。为这个紧急任务新建一个分支,并在其中修复它。在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。切换回你最初工作的分支上,继续工作。首先,我们假设你正在你的项目上工作,并且已经有一些提交。 现在你需要从master分支上拉出一个分支进行你新需求的开发,你可以运行一个带有 -b 参数的 git checkout 命令:
$ git checkout -b iss53 Switched to a new branch "iss53"该命令为下面两条命令的缩写:
$ git branch iss53 $ git checkout iss53你在iss53分支中进行了一系列的开发,你需将你的变更提交至git,命令如下:
$ git commit -a -m 'added a new footer [issue 53]'现在你接到那个电话,有个紧急问题等待你来解决。这时你需要切换回master 分支。执行如下命令:
$ git checkout master Switched to branch 'master'注意:当进行分支切换时,需要留意工作目录和暂存区里那些还没有被提交的修改,你需要将这些修改提交或者暂时隐藏。命令:
##隐藏变更 $ git stash ##查看栈中储存的工作 $ git stash list ##使用apply命令来应用储藏,同时在栈中继续保存 $ git stash apply ##使用pop命令来应用储藏然后立即从栈上扔掉它 $ git stash pop ##提交变更 $ git commit -a -m "xxx"接下来,你要修复这个紧急问题。让我们建立一个针对该紧急问题的分支(hotfix branch),在该分支上工作 直到问题解决:
$ git checkout -b hotfix Switched to a new branch 'hotfix' $ vim index.html $ git commit -a -m 'fixed the broken email address' [hotfix 1fb7853] fixed the broken email address 1 file changed, 2 insertions(+)接下来你需要将你的提交合并到master 分支来部署到线上。
$ git checkout master $ git merge hotfix Updating f42c576..3a0874c Fast-forward index.html | 2 ++ 1 file changed, 2 insertions(+)问题修复完成后需要将hotfix分支删除。
$ git branch -d hotfix Deleted branch hotfix (3a0874c).现在你可以切换回你iss53 分支分支继续你的工作。
$ git checkout iss53 Switched to branch "iss53" $ vim index.html $ git commit -a -m 'finished the new footer [issue 53]' [iss53 ad82d7a] finished the new footer [issue 53] 1 file changed, 1 insertion(+)假设你已经修正了 #53 问题,并且将你的工作合并入 master 分支。
$ 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不太一样,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一 些额外的工作。出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工 作祖先(C2),做一个简单的三方合并。 合并完成后,同样需要删除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 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:
$ 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")打开冲突文件如下:
<<<<<<< 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在这个======= 的上半部分代表你的master 分支所有的代码,而 iss53 分支所指示的版本在 ======= 的下半部分。你需要选择你想要的那个版本代码,同时去除<<<<<<< , ======= , 和 >>>>>>>。 冲突解决完成后,你可以再次运行 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来合并分支的话得到的提交历史如下图所示: 这是可以使用rebase命令进行变基操作,将提交到某一分支上的所有修改都移至另一分支上。它的原理是首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master)的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。 $ git checkout experiment $ git rebase master First, rewinding head to replay your work on top of it... Applying: added staged command接下来切换回到 master 分支,进行一次快进合并。
$ git checkout master $ git merge experiment可以看出与merge方式相比,使用变基操作会使提交历史更加整洁。
需要注意的是,rebase变基只能对尚未push到远程的本地修改执行变基操作清理历史,不要对已提交至远程的代码执行变 基操作。不然你同事会想打死你!!!