Wlgls 冲鸭!

Git分支

2019-06-30
Git

昨天学习了在本地使用git的方法,我们了解到,在git中,我们的每次提交都会被串成一条时间线,这条时间线就是一条分支

在我们初始化的事情,git默认给我们创建了一个master的分支,也就是主分支

版本回退的时候,我们使用HEAD严格来说不是指向提交,而是指向mastermaster才指向提交。也就是说HEAD指向的是当前分支。

我极度相信图形化的解释更能让人理解,所以还是推荐廖雪峰的博客-git分支

简单的分支操作

创建分支

  • git branch <name>

    创建分支


git在初始状态下,只存在master分支,首先我们创建dev分支

$: git branch dev

这样就创建了一个分支dev,我们可以使用git branch来查看当前分支

$: git branch
  dev
* master

可以发现,多出了一条分支,其中*表示处于当前分支,当前分支依然是master

切换分支

  • git checkout <name>

    切换分支


现在我们创建了dev分支,我们可以直接使用git checkout命令进行切换分支

$: git checkout dev
切换到分支 'dev'
$: git branch
* dev
  master

创建并切换

  • git checkout -b <name>

    创建并切换分支


如果你想要在创建的时候就直接切换了分支可以直接使用git checkout -b dev

$: git checkout -b abc
切换到一个新分支 'abc'
$: git branch
* abc
  dev
  master

删除分支

  • git branch -d <name>

    删除一个分支


在git中删除分支是十分迅速的,直接使用git branch -d dev即可

$: git branch -d abc
已删除分支 abc(曾为 815bc52)。

合并分支

  • git merge <name>

    合并指定分支到当前分支


注意: 因为我学习分支是在间隔了一段时间,所以源文件被我删除,重新建立了git仓库

我们创建了一个分支,并且在这条分支上进行了提交,使用git branch检查当前处于的分支

$: git branch
* dev
  master

我们在文本中添加了一行forth,然后在本分支上进行一次commit提交

$: echo "forth" >> readme.txt
$: cat readme.txt
first
second
third
forth
$: git add readme.txt
$: git commit -m "forth"
[dev 449796e] forth
 1 file changed, 1 insertion(+)

我们可以使用git log查看一下提交记录,我们可以看出在第四次提交的时候,HEAD指向了dev

 $: git log
 commit 449796ed9c4459653f2f32ebfd2a0d18166880a1 (HEAD -> dev)
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:23:00 2019 +0800

    forth

commit 2c48555843e19cfe9c86ceff886e5c1536afe5ec (master)
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:21:19 2019 +0800

    third

commit a8aa4a763b1fcd24232ea38e62fcb941ce200561
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:20:54 2019 +0800

    second

commit 852d56db6edb8826a9a9bd8621457e21d366260f
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:20:28 2019 +0800

    first

现在我们切回master分支,然后检查readme.txt,我们会发现,现在readme.txt中只有三行

$: git checkout master
切换到分支 'master'
$: cat readme.txt
first
second
third

廖雪峰老师在博客中的图片十分形象

2019-10-16-23-00-35.png

此时我们虽然提交了,但是却是提交在了dev的分支上,而对于master分支,没有进行任何修改

因为master分支在之后并没有提交,也就是说,虽然存在分支,但是master主分支并没有分叉,所以我们可以直接使用git merge合并

git merge <branchneme>命令用于合并指定分支到当前分支。

$: git merge dev 
更新 2c48555..449796e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)
$: cat readme.txt
first
second
third
forth

可以看出,现在readme.txt又回来了。需要注意的是Fast-forward代表这次合并是’快进模式‘,也就是说直接将master指向dev的当前提交

我们甚至可以使用git log来检查一下。

$: git log
commit 449796ed9c4459653f2f32ebfd2a0d18166880a1 (HEAD -> master, dev)
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:23:00 2019 +0800

    forth

commit 2c48555843e19cfe9c86ceff886e5c1536afe5ec
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:21:19 2019 +0800

    third

commit a8aa4a763b1fcd24232ea38e62fcb941ce200561
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:20:54 2019 +0800

    second

commit 852d56db6edb8826a9a9bd8621457e21d366260f
Author: smithgua <206007768@qq.com>
Date:   Mon Jul 1 21:20:28 2019 +0800

    first

对于第四次提交是想对于masterdev分支同时存在的(因为我也没有深入的学习过,通过廖雪峰老师的博客,我是这么认为的)

2019-10-16-23-01-15.png

特殊的分支合并

解决冲突

我们将dev分支删掉,重新创建一个feature1分支,然后就继续进行提交

$: git checkout -b feature1
切换到一个新分支 'feature1'
$: echo "fifth" >> readme.txt
$: git add readme.txt
$: git commit -m "fifth"
[feature1 00205b0] fifth
 1 file changed, 1 insertion(+)

然后我们切换会master分支,并且在master分支上新建一个提交

$: git checkout master
切换到分支 'master'
$: echo "sixth" >> readme.txt
$: git add readme.txt
$: git commit -m "sixth"
[master b3c0296] sixth
 1 file changed, 1 insertion(+)

我们还可以使用git log来做简单的查看

$: git log
commit b3c02961f177ba115b3e315efe2c028161a1072f (HEAD -> master)
Author: smithgua <@qq.com>
Date:   Mon Jul 1 22:00:58 2019 +0800

    sixth

commit 0e8b7e3e9fb97cad0c41a8818cc757bb839a8b8b
Author: smithgua <@qq.com>
Date:   Mon Jul 1 21:54:05 2019 +0800

    forth

commit 2c48555843e19cfe9c86ceff886e5c1536afe5ec
Author: smithgua <@qq.com>
Date:   Mon Jul 1 21:21:19 2019 +0800

    third

commit a8aa4a763b1fcd24232ea38e62fcb941ce200561
Author: smithgua <qq.com>
Date:   Mon Jul 1 21:20:54 2019 +0800

    second

commit 852d56db6edb8826a9a9bd8621457e21d366260f
Author: smithgua <8@qq.com>
Date:   Mon Jul 1 21:20:28 2019 +0800

    first

可以看出来在master分支上,只有第六次提交,而没有第五次提交,这是因为第五次提交是在feature1上建立的

现在masterfeature1分支各自都有了新的提交,也就是说,产生了分叉,引用廖雪峰老师的博客中图片

2019-10-16-23-01-43.png

这种情况是无法进习惯执行Fast-forward的,我们可以尝试一下

$: git merge feature1 
自动合并 readme.txt
冲突(内容):合并冲突于 readme.txt
自动合并失败,修正冲突然后提交修正的结果。

也就是说当前情况是存在冲突的,必须手动解决冲突才可以继续提交。我们可以使用git status来检查冲突的文件

$: git status
位于分支 master
您有尚未合并的路径。
  (解决冲突并运行 "git commit")
  (使用 "git merge --abort" 终止合并)

未合并的路径:
  (使用 "git add <文件>..." 标记解决方案)

	双方修改:   readme.txt

修改尚未加入提交(使用 "git add" 和/或 "git commit -a"

我们可以查看一下readme.txt中的文件

$: cat readme.txt
first
second
third
forth
<<<<<<< HEAD
sixth
=======
fifth
>>>>>>> feature1

git使用<<<<<<<,=======, >>>>>>>来标记不同分支的内容,现在我们修改内容保存

$: vim readme.txt

现在文本中是如下的内容

first
second
third
forth
<<<<<<< HEAD
sixth
=======
fifth
>>>>>>> feature1

我们将其修改为如下内容

first
second
third
forth
seventh

现在我们可以再次提交了

$: git add readme.txt
$: git commit -m "conflict fixed"
[master 02c590f] conflict fixed

现在,master分支和feature1分支就变成了下图所示,引用廖雪峰老师博客中的图片

2019-10-16-23-02-36.png

我们也可以使用带有参数的git log来查看分支的合并情况

$: git log --graph --pretty=oneline --abbrev-commit
*   02c590f (HEAD -> master) conflict fixed
|\  
| * 4d5aa1d (feature1) fifth
* | 2e910ea sixth
|/  
* 021ef09 forth
* f83dd14 third
* 06283ca second
* 805e981 first

总结: 在git分支存在分叉的情况下,我们需要首先解决冲突,之后再提交。

解决冲突就是将合并失败的文件手动编译为我们希望的内容,再提交。

分支管理策略

git merge --no-ff -m "merge with --no-ff" dev


通常,git在合并分支的时候,会使用Fast-forward模式,这种模式下,删除分支后会丢掉分支信息。

所以我们如果强制禁用Fast forward模式,git就会在merge时,生成一个新的commit,这样,从分支历史上就可以看出分支信息

使用--no-ff

我们先删除feature1分支,然后新创建一个分支,并提交一个新的commit

$: git branch -d feature1
$: git checkout -b dev
切换到一个新分支 'dev'
$: echo "eighth" >> readme.txt
$: git add readme.txt
$: git commit -m "eighth"
[dev 5f2e123] eighth
 1 file changed, 1 insertion(+)

然后我们尝试合并一下,首先将分支转到master,而且,在使用--no-ff时,会创建一个新的节点,所以要加上-m参数,并将commit描述写进去

$: git checkout master
切换到分支 'master'
$: git merge --no-ff -m "merge with --no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

我们可以使用git log来查看分支历史

$: git log --graph --pretty=oneline --abbrev=commit
*   2e2604d (HEAD -> master) merge with --no-f
|\  
| * 5f2e123 (dev) eighth
|/  
*   02c590f conflict fixed
|\  
| * 4d5aa1d fifth
* | 2e910ea sixth
|/  
* 021ef09 forth
* f83dd14 third
* 06283ca second
* 805e981 first

现在,merge后就变成下图这样,引用廖雪峰的博客中的图片

2019-10-16-23-04-00.png

需要注意的是,在这个时候,master所指的那个节点是dev节点的复制

分支策略

在实际开发中,我们应当按照几个基本原则

  1. master分支应该是非常稳定的,仅仅用来发布新版本,平时不在上面干活
  2. 干活都在dev分支上,也就是说,dev分支是不稳定的,当我们要发布某个版本的时候,才把dev分支合并到master分支上。
  3. 所有成员都在dev分支上工作,每个人都有自己的分支,时不时地往dev分支上合并(这个合并应该是要解决冲突的)

我比较好奇dev是怎么往master分支上合并的,所以我尝试了一下

当前是这样的

2019-10-16-23-05-59.png

现在我们给dev分支新添加了一个节点,也就是说变成了下图那样

2019-10-16-23-06-30.png

使用命令实现这一情况

$: git checkout dev
切换到分支 'dev'
$: echo "ninth" >> readme.txt
$: git add readme.txt
$: git commit -m "ninth"
[dev 14ade7e] ninth
 1 file changed, 1 insertion(+)
$: git checkout master
切换到分支 'master'

然后我们使用git merge来合并分支,并且使用git log来查看一下

$: git merge --no-ff -m "merge with --no-ff two" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)
$: git log --graph --pretty=oneline --abbrev-commit
*   a0758fa (HEAD -> master) merge with --no-f two
|\  
| * 14ade7e (dev) ninth
* |   2e2604d merge with --no-f
|\ \  
| |/  
| * 5f2e123 eighth
|/  
*   02c590f conflict fixed
|\  
| * 4d5aa1d fifth
* | 2e910ea sixth
|/  
* 021ef09 forth
* f83dd14 third
* 06283ca second
* 805e981 first

可以看出来又新建了一个节点,而且master所指的那个节点和dev所指的那个节点仍旧是复制的

2019-10-16-23-06-38.png

现在我们尝试一下删除dev节点,并再次使用git log检查一下

$: git branch -d dev
已删除分支 dev(曾为 14ade7e)。
$: git log --graph --pretty=oneline --abbrev-commit
*   a0758fa (HEAD -> master) merge with --no-f two
|\  
| * 14ade7e ninth
* |   2e2604d merge with --no-f
|\ \  
| |/  
| * 5f2e123 eighth
|/  
*   02c590f conflict fixed
|\  
| * 4d5aa1d fifth
* | 2e910ea sixth
|/  
* 021ef09 forth
* f83dd14 third
* 06283ca second
* 805e981 first

也就是说,即便删除了dev节点,但是其合并还是有所显示

分叉合并

分支-保护现场

  • git stash

    暂存当前工作状态

  • git stash pop

    来弹出最近一次stash的内容并且把将其删除


软件开发中,bug就像家常便饭一样。有了bug就要修改,而在git中,我们可以通过新建一个临时分支来修复bug,修复后,合并分支,然后将临时分支删除

现在我们修复一个bug的任务时,我们首先创建一个issue分支来修复它,但是,我们正在dev分支上进行的开发还没有结束

$: git status
位于分支 dev
尚未暂存以备提交的变更:
  (使用 "git add <文件>..." 更新要提交的内容)
  (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

	修改:     readme.txt

修改尚未加入提交(使用 "git add" 和/或 "git commit -a"

这个时候,我们因为种种原因而无法提交,但是又必须理解修复bug,这个时候我们可以使用stash功能,将工作区存储起来,然后之后可以继续恢复现场

$: git status
保存工作目录和索引状态 WIP on dev: a0758fa merge with --no-f two

这个时候再次使用git status就可以看出工作区是干净的了。然后就可以创建分支进习惯你修复bug了

首先确定在哪个分支上修复bug,假定需要在master分支上修复,我们就在master分支上创建临时分支

$: git checkout master
切换到分支 'master'
$: git checkout -b issue
切换到一个新分支 'issue'

加入修复bug就是把readme.txt中的seventh修改为fifth,然后提交

$: cat readme.txt
first
second
third
forth
seventh
eighth
ninth
$: vim readme.txt
$: git add readme.txt
$: git commit -m "fix bug"
[issue 6438fab] fix bug
 1 file changed, 1 insertion(+), 1 deletion(-)

现在我们切换到master分支,并完成合并,最后删除issue分支

$: git checkout master
$: git merge --no-ff -m "merged fix bug" issue
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

完成合并了,现在回到dev分支继续工作。我们可以使用git stash list来查看刚刚存储的工作区

$: git checkout dev
$: git status
位于分支 dev
无文件要提交,干净的工作区
$: git stash list
stash@{0}: WIP on dev: a0758fa merge with --no-f two

现在我们需要恢复工作区,有两种方法:

1、使用git stash apply来恢复,但是这种恢复,stash内容并不删除,需要使用git stash drop来删除

2、直接使用git stash pop,恢复的同时,也删除了stash内容

$: git stash pop
位于分支 dev
尚未暂存以备提交的变更:
  (使用 "git add <文件>..." 更新要提交的内容)
  (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

	修改:     readme.txt

修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
丢弃了 refs/stash@{0} (d9f26ca4f7ce76802cfa1822bf3bbe0fa98623a1)

强制删除

当我们在某个分支上工作时,突然被告知这条分支要被舍弃,这个时候,我们就需要强制删除该分支

举例说明

加入我们在dev上有一个提交

$: git checkout dev
$: vim readme.txt
$: git add readme.txt
$: git commit -m "seventh"
[dev f401acc] seventh
 1 file changed, 1 insertion(+), 1 deletion(-)

然后我们回到master分支,并且正准备合并他,但是这时候突然被要求要删除该分支。 我们可以使用git branch -D dev来强制删除

$: git branch -d dev
error: 分支 'dev' 没有完全合并。
如果您确认要删除它,执行 'git branch -D dev'$: git branch -D dev
已删除分支 dev(曾为 f401acc)。