完全掌握git(三)

1、前情回顾

完全掌握git(一)
完全掌握git(二)git

2、分支基础操做

分支其实就是一个指针指向某个commit提交,每进行一次提交,指针都会移到最新提交的位置。相似于串珠子,每次提交就像是一个一个的珠子,经过分支串联起来。以下图所示
分支.jpegsegmentfault

执行git init命令后,git会给项目自动建立一个空白的主分支,即master分支
① 查看分支
能够经过git branch命令查看当前项目中存在哪些分支,可是该命令只能查看本地分支,没法查看远程分支,若是要查看远程分支,那么咱们须要带上-a参数,即git branch -athis

> git branch -a
* master
 remotes/origin/HEAD -> origin/master
 remotes/origin/branch-a
 remotes/origin/maste

带星号的那个分支表示是当前活跃的分支,远程分支以remotes/origin/开头,以后的为远程版本库的分支名spa

② 建立分支
分支必需要至少一次提交,没有提交的分支是不能经过git branch -a命令查看到的。咱们能够经过git branch <分支名>命令来建立分支,建立的分支会以当前分支为基础,也就是说,新建立的分支会带上当前分支的全部提交指针

// 当前在master分支,执行命令建立branch-1
> git branch branch-1
// 建立的branch-1分支拥有和master分支同样的提交信息

固然咱们能够指定新建立分支的指针位置,即只要部分提交信息,能够经过git branch <分支名> <commit-id>,那么建立的分支的提交信息,将仅仅包含当前分支的第一个提交到<commit-id>的这部分提交信息code

> git branch branch-2 c12ac1

默认状况下是以当前分支为基础建立新的分支,咱们也能够指定以某条已经存在的分支为基础建立新分支,git branch <新分支名> <旧分支名>,如:对象

// 建立的branch-3将以branch-2分支为基础
> git branch branch-3 branch-2

③ 切换分支
git branch命令建立好分支后,并不会自动从当前分支切换到新建立的分支上,若是咱们须要切换到其余分支,那么能够经过git checkout <分支名>命令切换到指定分支上,如:blog

// 切换到branch-a分支上
> git checkout branch-a

固然checkout还能够作到建立并切换到新分支上,那就是加上-b参数,即git checkout -b <分支名>,如:开发

// 建立并切换到branch-4分支上
> git checkout -b branch-4

checkout命令还能够经过--orphan参数,建立一个孤儿分支,这是一个没有任何提交的分支,因此其没法被查看到,也叫空白分支,如:rem

// 建立一个没有任何提交信息的branch-5分支
> git checkout --orphan branch-5
Switched to a new branch 'branch-5'
> git branch
 branch-1
 branch-2
 branch-3
 branch-4
 master

能够看到新建立的branch-5孤儿分支并无被查看到,只有进行提交以后,才能被查看到。
须要注意的是,咱们切换分支的时候,当前分支中工做区或暂存区中有新的修改未提交,那么是没法切换到其余分支的,不过咱们能够经过git checkout --force <分支>的方式强制切换,可是这样会致使以前的修改被丢弃

④ 删除分支
删除分支,咱们能够经过git branch -d <分支名>命令,须要注意的是,删除分支的时候是没法本身删除本身的,也就是说,没法删除当前活跃分支。当咱们建立好了一个新的分支后,若是尚未在这个分支上进行过任何提交,那么咱们能够直接删除,由于新建立的分支上没有提交过新的东西,版本库没有发生变化。可是若是咱们在新建立的分支上提交过新的修改,那么新的分支上有了新的东西,那么这个时候删除分支就会提示,要删除的分支上有尚未被merge的东西,而没法删除,固然能够经过git branch -D <分支名>的方式进行强制删除。

// 当前在master分支上
// branch-1提交过新的修改
> git branch -d branch-1
error: The branch 'branch-1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D branch-1'.
> git branch -D branch-1
Deleted branch branch-1 (was 091034a).

⑤ 恢复分支
因为删除分支的时候,只是删除了指向相关提交的指针,可是该提交对象依然会留在版本库中,因此咱们只要找到删除分支时的散列值,那么就能够经过git branch <分支名> <删除分支的散列值>恢复删除的分支,例如,上面删除分支的时候的时候散列值为091034a,如:

// 恢复branch-1分支
> git branch branch-1 091034a

固然咱们能够同git reflog找到删除分支的时候的散列值

3、合并分支

分支合并是很是常见的操做,好比,咱们在主线分支上开发新功能,同时在另外一个分支上进行bug的修复,咱们要将bug的修复合并到主线上;除了两条分支上须要合并以外,同一条分支上也存在着合并的状况,好比,有两个开发者都在主线分支上进行开发,一个开发者A提交代码后,另外一个开发者B须要把开发者A提交的代码同步过来,也须要将A的提交进行合并。咱们能够经过merge命令而且指定一个分支名,能够将指定的分支合并到当前分支上

// 切换到master分支上
> git checkout master
// 将branch-1分支合并到master分支上
> git merge branch-1

git merge branch-1等价于git merge branch-1 master ,merge后面的第一个参数是源分支第二个参数为目标分支

屏幕快照 2020-01-02 下午5.53.22.png

从图上能够看到,branch-1由master分支的第二次提交处分叉而来,同时master分支上进行了第三次提交,而branch-1分支上则进行了第四和第五次提交,而后将branch-1上的提交合并到master分支上,在合并的过程当中,额外产生了一次合并提交,即第六次提交。经过git log咱们能够看到这六次提交都在,咱们能够经过git log --graph查看到分支的图形化结构。

① 分支合并的过程当中发生的事
进行合并的时候,git可以自动进行合并。若是两个修改的是同一个文件可是修改的地方不同,也就是说,修改的不是同一行,同一个文件在两个不一样的分支上显示不一样,好比a分支修改的是第一行,b分支修改的是第二行,对于a分支来讲,该文件的第二行内容与b文件的第二行内容不一样,对于b分支来讲,该文件第一行内容和a分支的第一行内容不一样,那么这个两个分支合并的时候,该文件第一行和第二行都出现了差别,可是合并以后该文件只有一个版本了,那么git是如何肯定最终合成应该怎么显示呢?git就是找到两个分支的分叉点以分叉点为参照,因此第一行显示a分支的修改,第二行显示b分支的修改。如图所示。
分支合并2.png

从图中能够看出,若是不以分叉点为参照,那么合并的时候,第一行就不知道要显示分支a修改了第一行仍是原始内容,一样第二行也不知道要显示分支b修改了第二行仍是原始内容

② 冲突
在实际的合并中,可能会出现两个开发者同时对同一个文件的同一行代码进行修改的状况,那么这个时候git就会出现合并冲突,由于这两个修改相对于分叉点而言都是最新的修改,而且都是同一行,因此git不知道此时应该用哪一个修改才正确,这个时候咱们就须要手动解决冲突。

// 将branch-1分支上的提交合并到master分支上
> git merge branch-1 master
Auto-merging foo.txt
CONFLICT (content): Merge conflict in foo.txt
Automatic merge failed; fix conflicts and then commit the result.
> git diff foo.txt
**diff --cc foo.txt**
**index 5f5fbe7,da8092b..0000000**
**\--- a/foo.txt**
**+++ b/foo.txt**
@@@ -1,3 -1,3 +1,7 @@@
 1
 2
-3
-4
++<<<<<<< HEAD
++3
++=======
++4
++>>>>>>> branch-1
> git add .
> git commit -m 'merge branch-1 to master'

合并过程当中出现了冲突,咱们找到出现冲突的那个文件进行手动修改,须要去除冲突标志,而后将其添加到暂存区并提交便可完成合并
固然修改好冲突文件并添加到暂存区后,咱们还能够经过git merge --continue命令继续merge操做,会自动弹出提交交互窗口,输入提交信息退出编辑便可完成合并。

③ 快速合并
正常状况下进行合并操做,无论能不能自动合并成功,都会产生一次用于记录合并操做的提交信息,以便咱们对版本库的发展过程进行追踪,可是当咱们从某个点分叉出一个分支后,一直都没有在这个分支上提交过,那么咱们在这个分支上进行合并的时候就变得很是简单,只须要把该分支的指针移到一下便可,而不须要产生一次合并提交了,咱们称这种提交为快速合并提交。快速合并提交不利于版本库的追踪,因此咱们能够经过--on-ff来强制快速合并产生一次合并提交,如:

> git merge --on-ff master branch-1

4、rebase变基净化提交历史

因为merge命令合并后会额外产生一次关于合并的提交,这样会致使咱们的分支不断地的分叉又不断的合并,分支结构将会变得很是复杂,而rebase命令则可用让咱们将某一次提交从一个节点转移到另外一个节点,从而帮咱们理顺提交历史。
变基的原理很简单,git会让咱们想要移动的提交序列在目标分支上按照相同的顺序重现一遍,至关于咱们为各个原提交作了一个副本,它们拥有相同的修改集、同一做者、日期、以及注释信息。
① 平滑合并分支
git rebase <源分支> <目标分支>相似于merge命令,做用就是将源分支的内容合并到目标分支上,只不过不会额外产生一次合并提交

// 将bug分支合并到master分支上
> git rebase bug master

② 删除分支上的某个或某段提交
若是在提交的过程当中发现前面的某个提交有错误,那么咱们能够直接删除,git rebase --onto startPoint endPoint <要删除提交的分支> 这里的startPoint是取不到,可是endPoint是能够取到的,因此是一个前开后闭的区间,如:

// 删除master分支上(5af873,5305f6]之间的提交
> git rebase --onto 5af873 5305f6 master

③ 拷贝提交到某个分支上
git rebase startPoint endPoint --onto <目标分支>
首先切换到拷贝的源分支上,找到要拷贝的提交区间,而后经过--onto指定目标分支,执行命令后会产生一个游离分支,这个游离分支和咱们要的最终结果是同样的,可是尚未拷贝到目标分支上,而后切换到目标分支上,此时会提示目标分支遗留了一段提交,即咱们拷贝的那段提交,而后会有游离分支的head位置,咱们在目标分支上将分支指针重置到游离分支的head便可

// 切换到master分支
> git checkout master
// 将master分支上(5af873,89da14]的提交复制到bug分支上
> git rebase 5af873 89da14 --onto bug
// 查看产生的游离分支
> git branch
* (HEAD detached from 838de4f)
 bug
 master
// 切换到目标分支
> git checkout bug
Warning: you are leaving 2 commits behind, not connected to
any of your branches:
 7a48d4c commit-4
 78c3b66 commit-3
If you want to keep them by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 7a48d4c
// 能够看到git提示目前分支遗留了从master上拷贝的两个提交,而且头部在7a48d4c,而后重置目标分支指针到7a48d4c便可
> git reset --hard 7a48d4c

④ 合并多个commit为一个完整commit
rebase 提供了一个-i参数,可让咱们进入交互操做界面,git rebase -i startPoint endPoint找到要合并为一个提交的区间段(startPoint,endPoint],而后会进入一个交互操做界面,如:

> git rebase -i 5af873 89da14
pick 82a82f3 commit-3
pick 89da145 commit-4
// 如今咱们能够将这两个提交合并为一个,好比将commit-4合并到commit-3中,那么咱们能够将commit-4前面的pick改为s,s表示合并提交
pick 82a82f3 commit-3
s 89da145 commit-4
> :wq退出编辑模式
// 而后会再弹出一个交互界面用于修改提交合并后的信息,默认有能够不改
> :wq退出编辑模式
// 查看提交历史能够看到两个提交合并成了一个
> git log

【Git】rebase 用法小结
⑤ 修改好久以前的某一次提交
对于最近的一次提交,咱们能够经过git commit --amend命令进行修改,那么好久以前的修改咱们能够经过rebase命令进行修改,git rebase -i startPoint

// 由于起点取不到,因此找到要修改的哪次提交的上一次提交做为起点
> git rebase -i 5af873
pick f42d811 commit-3
pick 07fe470 commit-5
// 将要修改的那次提交前的pick修改成e,表示要编辑
e f42d811 commit-3
pick 07fe470 commit-5
> :wq退出编辑模式
// 进入修改模式
> git commit --amend
// 修改好以后保存
> :wq退出
// 此时查看log指针只在修改的位置,由于rebase还没结束,须要继续
> git rebase --continue
// 再次查看log,能够看到以前好久的一次提交被修改了

5、rebase变基的实际应用

① 多人协同开发的时候避免出现钻石链
在实际开发中,并非全部人都会用rebase净化提交历史的,因此会常常出现钻石链的状况,假若有一个开发进行了一次提交并推送到了云端;而后另外一个开发者也进行了一次修改,可是两者修改的不是同一个文件,而后其经过git pull将上一个开发者提交的内容拉下来,因为git pull会进行merge操做,会自动建立一个合并提交,因此第二个开发者将获得一个钻石链,而后将其推送到云端;而后另一个开发者也进行了一次修改,他修改的也不是同一个文件,即便他使用的是git pull --rebase命令,那么前面两个开发者产生的钻石链也还在并无消失,这个时候怎么办呢?首先用git log --graph找到分叉点的提交id,而后使用git rebase -i <分叉的位置提交id>表示对分叉点以后的提交进行变基操做,以后会弹出交互窗口,因为三我的的提交修改的都不是同一个文件,那么此时不用进行任何操做,直接退出交互窗口的编辑便可净化提交历史。

// 第三个开发者获得了第一个和第二个开发者合并产生的钻石链
> git log --graph
// 找到分叉点提交id
> git rebase -i 5af873
// 打开交互命令窗口,直接退出不用进行任何操做
> :wq 退出编辑模式
// 再次查看提交历史是否已经被净化
> git log --graph

若是三个开发者修改的都是同一个文件,因为变基操做会重播一遍,因此这三个提交都会在当前分支上重播一遍,而且是一个一个轮播的,因为三我的修改的都是同一个文件,因此会有两次合并冲突,每解决一次冲突,将改好的文件加入到暂存区中,而后执行git rebase --continue,解决完两次冲突后再执行git log --graph查看提交历史是否已经被净化了。

相关文章
相关标签/搜索