合并
合并的流程如下:假定将other分支合并到master分支上。
第一步,执行git merge other, Git会自动根据获取当前分支,并判断是否符合合并的条件。
第二步,基于基点(base commit), 计算other分支与master分支上每个文件的差异,并尝试合并,若合并成功,无冲突,此时无需人工介入,Git自动创建一个合并提交(Merge commit),合并完成。
第三步,若有冲突,把文件标志为”unmerged”状态,人工介入,解决冲突,手动创建一个提交,合并结束。
第四步,第三步的另一种方案,当发现冲突之后,撤销合并操作,合并撤销。
1.1 前提条件
A merge must occur within a single repository
合并的分支处于同一个版本库。
before you begin a merge, it’s best to tidy up your working directory. During a normal merge, Git creates new versions of files and place them in your working directory when it is finished. furthermore, Git also uses the index to store temporary and intermediate versions of files during the operation
在合并之前,工作目录必须是干净的。
1.2 有冲突
若发生冲突,合并中断,需要人工介入,执行git status,可以查看冲突的文件列表。
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: Hello.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
result.txt
no changes added to commit (use "git add" and/or "git commit -a")
显示Hello.txt文件存在冲突,点击查看冲突的内容。
<<<<<<< HEAD
Line under master
=======
Line under other
>>>>>>> other
横线之上,master分支上的Hello.txt与基准点(base commit)的比较。横线之下,other分支上的Hello.txt与基准点(base commit)的比较。
1.2.1 解决冲突
逐个解决冲突的文件,只有三种策略。
第一种,完全删除掉当前分支的内容。accept theirs。
第二种,完全删除合并分支的内容。accept ours。
第三种,前两种的mix。
在实践中,需要遵循不覆盖别人代码的原则,所以accept ours很少使用。
1.2.2 回滚
回滚,本质就是清空当前工作目录中的修改。若发生冲突,合并操作中断,不会自动创建合并提交(merge commit)。
当执行合并时,
当前分支的HEAD临时保存在ORGI_HEAD。
被合并的分支HEAD临时保存在MERGE_HEAD。
执行git merge –-quit,退出合并操作, abort的合并可以重新启动,退出之后只能重新开始。
执行git reset –hard ORGI_HEAD, 回滚到最近的一次commit。
1.3 无冲突
无冲突情况下,Git自动创建merge commit,日志信息为merge branch xx into XX,例如将other合并到master,显示为merge branch other into master。
命令窗口会打印合并的策略,
1.3.1 already up-to-date
when all the commits from the other branch (its HEAD) are already present in your target branch, even if it has advanced on its own, the target branch is said to be already up-to-date
当master分支与other分支的差集为空时,或者表述为other分支中所有的提交都在master集合中。此时other分支合并到master分支,采用already up to date。
它没有任何操作,因为它们已经合并过了。
1.3.2 fast-forward
A fast-forward merge happens when your branch HEAD is already fully present and represented in the other branch. This is the inverse of the Already up-to-date case
fast-forward是快进到最近一次提交, 当master分支合并到other分支时,other分支的HEAD会指向master分支的最近一次提交。
例如将master合并到other分支上时,只是将other分支的HEAD指向master的最近一次提交。
1.3.3 resolve
the resolve strategy operates on only two branches, locating the common ancestor as the merge basis and performing a direct three-way merge by applying the change from the merge base to the tip of the other branch HEAD onto the current branch
默认的方式,比较两个分支的不同文件,尝试合并,若无冲突,会自动创建合并提交(merge commit)
1.3.4 recursive
两个以上的分支进行合并时,采用的策略。假设A, B, C三个分支进行合并,根据merge命令中参数的顺序,先合并A, B,然后将二者的合并结果与C进行合并,类似于数据库中的join操作。
1.3.5 octopus
The octopus strategy is specifically designed to merge together more than two branches simultaneously. Conceptually, it is fairly simple, internally, it calls the recursive merge strategy multiple times, once for each branch you are merging, However, this strategy cannot handle a merge that requires any form of conflict resolution that would necessitate user interaction. In such a case, you are forced to do a series of normal merges, resolving the conflicts one step at a time.
多线程,异步执行,例如A, B,C,D,异步执行A与B合并,C与D合并,然后再将二者的结果进行合并。
1.4 指令
格式:
git merge指令有两种格式。
第一种格式,git merge (--continue, --abort, --quit)控制合并的流程,abort与continue是一对,quit退出后,只能重新进行合并。
第二种格式
git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[--no-verify]
[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
[--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>…]
选项:
n,stat:显示合并的统计信息。
no-commit:合并时不自动创建提交对象。
squash:压缩合并提交和当前提交为一个提交。不会这么干,会混乱提交历史。
m,F,edit:提交的日志信息,m从控制台输入,F从文件中获取,edit开启编辑器。
s :指定合并的策略。默认的策略即可。
no-verify:不校验合并提交的对象。
S :指定提交的签名。
allow-unrelated-histories:多个分支合并时,必须至少存在一个基准点。它的作用就是比较彼此的不同之处。这个选项慎用,因为除补丁之外,添加这个选项说明大概率是错误的合并
示例:
git merge other(当前分支为master)
变基
变基(rebase),改变分支的基准点,流程如下:
第一步,计算变基分支与当前分支的提交差集。
第二步,重新创建差集中的每一个提交对象。
第三步,变更变基分支与当前分支的基准点为HEAD(默认值,可以提供commit标识指定基准点)。
个人理解,rebase会修改有向无环图,实际中不使用。但是它的优点也非常明显,可以使有向无环图变的非常清晰。
例如dev, master分支,互相merge多次之后,有向无环图会存在很多交叉点,非常乱。
2.1 场景
存在三种情形。
第一种情形,假设master,dev分支存在直接的基准点
此时变基的过程如下:
创建新的提交对象D’, E’
将dev分支与master分支的基准点由A变为C。
第二种情形,假设master,dev分支存在间接的基准点。
此时的变基过程如下:
计算dev分支与master分支的差集,上图中为D,E,F,G
创建新的提交对象D’,E’,F’,G’。
master, dev分支的基准点变为C, some,dev分支的基准点消失。
具体的影响有:F,G提交消失,取而代之的是F’,G’。 D, E提交没有变,但是新创建了D’,E’提交,它们的变更集与D,E 是一样的。提交的历史结构图也发生了改变。
第三种情形,假设master,dev分支存在直接基准点,但是dev分支上存在子分支
此时的变基过程如下:
创建新的提交对象D’,E’
dev,master分支的基准点由A变为C。
dev,some分支的基准点消失,some,master分支基准点为A。
可以看到整个过程也是多出了两个提交,D’, E’,而且是重复的。
2.2 指令
格式:
git rebase有三种指令格式:
第一种, git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch),控制rebase流程的,略。
第二种格式:
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
第三种格式与第二种格式是类似的。
选项:
-i,interactive为交互模式,它会打开编辑器,允许修改整个DRG。压缩,排序,修改单个提交对象等等。
--exec :执行1到N组shell脚本指令。略。
--onto :指定基准点。若指定的是分支,则为分支的最近一次提交。
upstream, branch:远程分支名称或本地分支名称。