Git 基础知识--分支操作

分支操作

几乎所有的版本控制系统都支持分支。使用分支意味着你可以把你的工作从开发主线上分离出来,以免影响开发主线。

在分支中,在基于主分支已有代码的基础上,你可以进行任意的操作,并且完全不用担心会污染主分支,相当于是拉了一个主分支的副本出来。在实际工作中,当想实验性的开发一个功能时,可以利用分支来完成该实验功能的实现,若功能实现效果达到预期需求,则可以将该分支合并到主分支中作为一个功能的完善;就算分支上开发的实验功能效果不完美,也尽可以删除该分支,而相关无事。

在很多版本控制系统中,分支是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大型项目来说,这样的过程会耗费很长时间。(SVN就是如此,往往从 SVN 中切换一个分支要等许久)

而 Git 的分支是极其轻量且高效的,是 Git 的一大必杀技,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。

分支的本质

分支的本质 :多个 commit 提交对象的集合,单个提交对象是一个快照。分支在某个时刻只会指向其中一个提交【快照】,而默认的分支别名为 master

HEAD:是一个可变指针,所有的分支都有机会被 HEAD 所引用(HEAD 一个时刻只会指向一个分支,默认为 master 分支),当项目有新的提交时,HEAD 会携带当前持有的分支往前移动。切换分支时其实就是改变 HEAD 文件的 refs 内容,从而指向不同的分支(提交对象)

分支原理

.git/refs 目录

这个目录中保存了分支及其对应的提交对象

HEAD 引用

当运行类似于 git branch (branchname) 这样的命令时,Git会取得当前所在分支最新提交对应的 SHA-1 值,并将其加入你想要创建的

任何新分支中。当你执行 git branch (branchname) 时,Git 如何知道最新提交的 SHA-1 值呢? 答案是 HEAD 文件。

HEAD 文件是一个符号引用(symbolic reference),指向目前所在的分支。 所谓符号引用,意味着它并不像普通引用那样包含一个 SHA-1 值。它是一个指向其他引用的指针。

分离头指针

Git 基础知识--分支操作_第1张图片

解决方案

解决的办法是:在当前提交上重新创建一个分支,让 HEAD 指向 新分支,新分支指向 commit ,完成统一

Git 基础知识--分支操作_第2张图片

分支类别

长期分支

Git 基础知识--分支操作_第3张图片

许多使用 Git 的开发者都喜欢使用这种方式来工作,比如只在 master 分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。 他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master 分支了。等待下一次的发布。随着你的提交而不断右移的指针。稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。

Git 基础知识--分支操作_第4张图片

production 分支

(主线分支用于发版,不会直接改)

master 分支

​ 这个分支只能从其他分支合并,不能在这个分支直接修改

develop 分支

(开发分支)

​ 这个分支是我们是我们的主开发分支,包含所有要发布到下一个 release 的代码,这个主要合并与其他分支,比如 feature分支。

feature 分支

(新功能分支)

​ 这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回 develop 分支进入下一个 release 。

release 分支

(偏向测试)

​ 当你需要一个发布一个新 release 的时候,我们基于develop分支创建一个 release 分支,完成 release 后,我们合并到 master 和develop 分支。

hotfix 分支

(紧急bug发布)

​ 当我们在 production 发现新的Bug时候,我们需要创建一个 hotfix, 完成 hotfix 后,我们合并回 master 和 develop 分支,所以 hotfix的改动会进入下一个 release。

特性分支 (topic)

特性分支是一种短期分支,它被用来实现单一特性或其相关工作。 即 基于某个分支创建来实现某种功能的子分支

branch 命令

创建分支

$git branch [分支名]

作用:创建了一个可以移动的新指针,这会在当前的提交对象上创建一个指针。默认以 master 为主分支衍生出的分支。

但该指令并不会自动的切换到新分支去,该命令仅仅是创建一个当前分支的副本出来给新分支引用。

此时的 HEAD 还是指向 master,而 master 内部保存的是最后一次的提交对象。

Git 基础知识--分支操作_第5张图片

查看分支

本地分支
$ git branch

作用:列出本地仓库的所有已创建分支

底层

git init 创建一个新项目,并书写了第一次工作区时,此时还并未提交,由于没有生成任何 commit 提交对象,所以 .git/refs/heads 内部是空的【并没有想象中的 master】;而也正因为此,由于 master 主分支没有指向任何 commit 提交对象的 hash ,所以当执行 git branch 查看时,并没有任何分支列表可供查看。

Git 基础知识--分支操作_第6张图片

  • 由此图可以理解到,其实 git branch 列出的都是每个已提交的分支

原因:任何分支的内部都是依靠着 commit 提交对象来指向的,HEAD >>> 分支 >>> commit 提交对象

这点,可以从 git log 中查看到:

$ git log --oneline
13f3587 (HEAD -> develop, master, damu) deleted README.md
dca2ba8 dw
6591a86 adw
764c56f dwdjiwjdoiwj :

当前指向的分支 develop,意味着当前期间每次 commit 提交之后更新的是 develop 分支中的代码。而其他的 masterdamu 分支都是保持着其最后一次的提交记录。

初始化的 master 主分支由于没有指向任何 commit 提交对象,所以 git branch 分支列表为空。

而如果是 master 分支中有提交记录的,那么从 master 分支中创建出的子分支, 执行 git branch 也是可以看到的。因为从 master 分支创建的子分支其指向的就是 master 分支所指向的 commit 提交对象。【子承父业

Git 基础知识--分支操作_第7张图片

当项目提交时,HEAD 会带着指向的 master 找到本次提交对象的 hash ,并保存到 master 内部中更新为最新一次的版本记录。

查看本地和远程分支
git branch -a     # 查看本地和远程的所有分支
查看远程分支
git branch -r # 查看远程所有分支

删除分支

-d 已合并分支
$git branch -d [分支名]

作用:删除指定的已修改且已合并 / 未修改的子分支。

注意:如果删除的子分支内的代码在原主分支基础上有所修改,但未合并。则会报如下错误:

$ git branch -d develop
error: Cannot delete branch 'develop' checked out at 'C:/Users/10461/Desktop/SkillsWork/Git/test5'

Git 规定,已修改、但未合并的分支视为一个谨慎操作的节点,不应该随意删除,所以需要 -D 参数来强制删除。

-D 未合并分支
$git branch -D [分支名]

作用:强制删除指定的未合并分支。

-v 查看分支最新提交

$ git branch -v
* develop 5a7bcf9 init develop
  master  44ea4da develop base

作用:说白了,就是查看每个分支指向的最后一次的 commit 提交对象。

底层

假设有 developdamu 两个分支,damu 分支是从 develop 分支中衍生出来的,则 branch -v 展示出来的 damu 分支指向的提交对象与 develop 分支所指向的提交对象一致

10461@HKJ-PC MINGW64 ~/Desktop/SkillsWork/Git/test5 (damu)
$ git branch -v
* damu	  1a11547 add file
  develop 1a11547 add file
  master  ad51a5d init

若第一次在 develop 分支中修改了文件,再切换到 damu 分支是可行的,此时会提示:

$ git checkout damu
Switched to branch 'damu'
M       a.dada

而如果将在 develop 分支中修改的代码,切换到 damu 分支进行第一次的 commit -m 提交,那么此时 damu 就会指向该提交对象,从而 damu 分支也有了第一次自己的提交记录。

这点,可以从 git branch -v 中看出:

10461@HKJ-PC MINGW64 ~/Desktop/SkillsWork/Git/test5 (damu)
$ git branch -v
* damu    8be850d 213
  develop 1a11547 add file
  master  ad51a5d init

而之后若再想要实现此操作,就会报错。

$ git checkout damu
error: Your local changes to the following files would be overwritten by checkout:
        a.dada
Please commit your changes or stash them before you switch branches.
Aborting
总结

由此可以理解到:由主分支创建的子分支,一开始指向的是同一个提交对象;这意味着主分支是子分支是完全一致的;可以实现在主分支下修改代码,再切换到子分支下提交的,这样更新的代码就会提交到子分支上,并指向最新的提交对象,此时主分支才与子分支是真正意义上的不一致。再之后想要实现此操作,则不可行,会报错。

$ git checkout damu
error: Your local changes to the following files would be overwritten by checkout:
     a.dada
Please commit your changes or stash them before you switch branches.
Aborting

版本穿梭

$git branch [分支名] [提交对象的 hash]

作用:新建一个分支并使该分支指向指定的提交对象。意为版本穿梭,也叫“时光机”。

  • 该命令最大的作用:可以通过指定某个历史版本的提交 hash 来回滚到指定的项目记录,从而实现自由穿梭在众多版本记录中。
10461@HKJ-PC MINGW64 ~/Desktop/SkillsWork/Git/example3 (damu)
$ git log --oneline --decorate --graph --all
* 32a4ec2 (HEAD -> damu) del a.txt
* 13f3587 (master, develop) deleted README.md
* dca2ba8 dw
* 6591a86 adw
* 764c56f dwdjiwjdoiwj :
$ git branch name 13f3587 # 穿梭到上一个版本

查看已合并的分支

git branch --merged

作用:查看哪些分支已经合并到当前分支

描述:在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉

查看未合并的分支

git branch --no-merged

作用:查看所有包含未合并工作的分支

一旦出现在此列表中的分支,应该观察一下是否可合并。

尝试使用 git branch -d 命令删除在这个列表中的分支时会失败。如果真的想要删除分支并丢掉那些工作,可以使用 -D 选项强制删除它。

-m 重命名分支

需要将一个分支的名称修改为指定名称,执行如下步骤:

1、先切换到旧分支:

git checkout [old branch]

2、将旧分支最新的远程仓库代码 pull 下来,与本地仓库同步:

git pull origin [old branch]

3、重命名旧分支:

git branch -m [old branch] [new branch]

4、将创建的本地新分支 push 到远程仓库

git push --set-upstream [new branch]

5、删除远程仓库的旧分支:

git push origin --delete [old branch]

原理:

  • 本次仓库:建立一个与旧分支代码同步的新分支,再推送到远程仓库
    • 此时同时存在两个代码相同的分支
  • 远程仓库:直接删除不用的旧分支,这样只剩下改名后的新分支

log 查看分支结构

理解为:查看当前项目所衍生的所有子分支的提交记录的历史组织结构。

当前分支

$git log --oneline --decorate

作用:查看当前分支的结构(包括主分支、子分支的切换、提交过程)。

Git 基础知识--分支操作_第8张图片

注:前面有() 代表刚切换了分支后提交的,没有 () 的代表是同一个分支下提交的多个() 来划分每个分支的提交记录范围

HEAD -> 则是指向当前提交的分支,后面紧随着的则是没有任何改动、未提交的分支。

所有分支

$git log --oneline --decorate --all

作用:查看当前项目所有分支的结构(包括主分支、所有子分支的切换、提交过程)。

Git 基础知识--分支操作_第9张图片

图形所有分支

$git log --oneline --decorate --graph --all

作用:以更友好的图形来查看当前项目所有分支的组织结构(包括主分支、所有子分支的切换、提交过程)。

Git 基础知识--分支操作_第10张图片

由此结构图可以看到:分为 masterdevelopdamudatasdata 分支。其中除了 data 分支没有任何改动,其他的分支都是已改动且提交的新项目记录。

checkout 命令

切换分支

Git 提供了 checkout 命令来进行分支的切换,每个分支代表一个工作目录,每次切换分支后都会更改以下三个文件:

  • HEAD 文件指向更改为切换的分支名,分支指向最新的 提交对象
  • 暂存区 变为切换的分支所属的暂存区
  • 工作目录 变为切换的分支对应的工作区
git checkout [分支名]

内部工作流:

  • git checkout testing

Git 基础知识--分支操作_第11张图片

  • 做出修改,再提交

Git 基础知识--分支操作_第12张图片

  • 切回 master

Git 基础知识--分支操作_第13张图片

注意:分支切换会改变你工作目录中的文件在切换分支时,一定要注意你工作目录里的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果Git 不能干净利落地完成这个任务,它将禁止切换分支。

故:每次在切换分支前提交一下当前分支

-b 创建/切换新分支

checkout 命令除了能切换分支之前,还可以给定 -b 参数基于当前分支来创建一个新分支并切换

git checkout -b [新分支名]

注意:当切换的分支名包含有 # 等特殊字符时,需要使用 "" 双引号包裹。

git checkout -b "#53"

分支污染【踩坑】

分支的污染主要体现在第一次初始化分支未提交的时候,当创建一个新分支并初始化,但并未 add -> commit 跟踪提交暂存后,又切换到了其他分支,虽然可以切换成功,但是上一个分支所创建的文件会存在于切换的分支区域中,从而形成分支污染。

建议:在每次切换分支之前都建议先提交,否则会造成对其他分支的污染。

切换分支的注意点

在切换分支的时候一定要保证当前分支是干净的!!!

  • 允许切换分支:
    • 分支上所有的内容处于 已提交 状态
    • (避免)分支上的内容是初始化创建,处于 未跟踪 状态
    • (避免)分支上的内容是初始化创建,已跟踪 > 已暂存 > 未提交 状态
  • 不允许切换分支:
    • 分支上所有的内容处于 已修改 状态,或 已提交过的 => 已暂存状态

merge 命令

场景

Git 分叉分支

所谓 “分叉分支” 即在一个 master 分支上建立了多个分支并提交的状态

(master) --> commit
	checkout -b (develop)  --> commit
<- checkout master
	checkout -b (hotbug) --> commit
  • 直接下游: 即按照不同子分支提交的顺序,最后一个提交的分支即是 master 分支的直接下游而该子分支的 直接上游 就是 master 主分支

Git 基础知识--分支操作_第14张图片

合并分支

采用 git merge 命令可将指定分支合并到当前分支中。

git merge [branch Name]

在实际开发工作中,为保证两个分支代码同步,减少冲突,一般首先会 pull 一下最新的远程代码,再 merge 合并指定的分支。

git merge --abort

中止合并

实际案例

工作流:

​ 1.开发某个网站。

​ 2.为实现某个新的需求,创建一个分支。

​ 3.在这个分支上开展工作。

​ 正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:

​ 1.切换到你的线上分支(production branch)。

​ 2.为这个紧急任务新建一个分支,并在其中修复它。

​ 3.在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。

​ 4.切换回你最初工作的分支上,继续工作。

Git 流:

​ 1、假设在 master 分支上,已经做出了一些提交。

​ 2、你开设了一个 iss53 分支用于一些 master 分支上的功能检验。

此时的 iss53 分支是 master 分支的直接上游。

Git 基础知识--分支操作_第15张图片

​ 3、此时,突然接到一个 #53 问题,需要紧急修复。这时你只需要提交 iss53 分支的修改,使分支环境足够干净之后,再切回到 master 分支来对 #53 问题进行处理即可。

注:当你切换分支时,Git 会重置你的工作目录、使其看起来像回到了你在那个分支上最后一次提交的样子。Git 会自动添加、删除、修改文件以确保此时你的工作目录与这个分支最后一次提交的样子一模一样。

​ 4、接下来,你需要在 master 分支基础上建立一个 hotfix 分支用来紧急处理 #53 的 bug 问题,最后运行测试完毕之后,merge 合并到 master 分支并部署到线上。

合并前:

Git 基础知识--分支操作_第16张图片

合并后:【之后即可删除 hotfix 分支,因为 master 已经指向该位置,且不再需要它了】

Git 基础知识--分支操作_第17张图片

快进合并

以上这种形式叫做 “快进” 合并:由于当前 master 分支所指向的提交是你当前提交的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)。

Git 基础知识--分支操作_第18张图片

简单来说:在一条开发线上的分支就叫快进合并。

典型合并

不在一条开发线上的分支叫典型合并

一般情况下,工作分支体系中,会存在有更早的分支,即存在分叉分支。早期的分支会与当前分支拥有一个分叉的起点,即共同祖先

如下图:

Git 基础知识--分支操作_第19张图片

当想要将多个早期分支与当前分支合并在一起时,Git 不得不做一些额外的工作。

出现这种情况时,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并

三方合并

Git 会自动选取哪一个提交作为最优的共同祖先,并以此作为合并的基础。

Git 基础知识--分支操作_第20张图片

Git 会将此次三方合并的结果做成一个新的快照并且自动创建一个提交来指向它,这个被称作 一次合并提交 (C4、C5、C6),它的特别之处在于它有不止一个父提交。

三方:C6 是一个空提交,它包含了 C4、C5 两个分支的提交内容。

合并冲突

出现场景:如果你在两个不同的分支中,对同一个文件的同一部分进行了不同的修改提交,Git 就没法干净的合并它们,在合并它们的时候就会产生冲突

例如 C4 和 C5 分支都对 a.txt 文件进行了同一个地方的修改,且都进行了提交,在合并它们的时候就会产生冲突。

Git 会出现如下提示:

Git 基础知识--分支操作_第21张图片

冲突(内容): 合并冲突在a.txt # 表示两个分支的 a.txt 文件有不同的修改,需要进行择优处理。
自动合并失败;修复冲突,然后提交结果。

此时 Git 做了合并,但是没有自动地创建一个新的合并提交。Git 会暂停下来,等待你去解决合并产生的冲突。

解决冲突/择优处理

Unmerged 未解决状态

可以在任意时刻使用 git status 命令来查看哪些因包含合并冲突而未合并的(unmerged) 状态的文件,任何因为包含合并冲突而有待解决的文件,都会以未合并状态标识出来。

Git 基础知识--分支操作_第22张图片

  • git merge --abort :中止本次合并

  • both modified : 表示两个分支都修改了 a.txt 文件而由此产生了合并冲突。

    红色:表示为 冲突未合并文件

对比两个分支的冲突

我们需要做的就是 vim a.txt 来查看 a.txt 文件内的冲突位置并解决。

Git 基础知识--分支操作_第23张图片

<<<<< HEAD

表示当前 merge 主动合并的分支所修改的 a.txt 的冲突位置内容。

>>>>> develop

表示被动合并的分支所修改的 a.txt 的冲突位置内容。

我们要做的就是择优处理将它们两个不同的地方根据业务需求进行删改,并最终形成一个不影响业务功能的干净文件

最后,删除 <<< HEAD>>>> develop 两个文本内容。

merged 已解决状态

当按照上面的处理,解决了冲突之后,可通过 git status 看到如下文件状态:

Git 基础知识--分支操作_第24张图片

  • modified :绿色,表示合并冲突已经解决,uncommit 待提交状态。

流程:使用 git add . 将解决了冲突的文件重新标记并跟踪,最后 git commit 提交即可完成本次的合并冲突。

(master|MERGING) 也会变为 (master)

分支提交结构:

Git 基础知识--分支操作_第25张图片

你可能感兴趣的:(Git,git,svn,github)