上一次我们安装了git,并且了解到了git的基本使用。但是我们还没有体会git真正的功能。接下来,我们开始一点一点的揭开git的神秘面纱。
说的直白些git就是一个存储文件(代码)的仓库,它不仅仅存储了代码,还存储了我们对代码进行的所有的操作。所有的数据在git中会被存储到一个树形结构中。像我们前边的例子中,我们一共commit了三次(初始化一次、修改index.html一次、添加.gitignore一次),我们数据的存储大概是这样一个结构。
每一次的存储就是树上的一个节点,a1表示第一次存储,a2表示第二次存储,a3表示第三次存储,每次存储都会又一个id以便我们可以快速定位,像我们之前通过git log查看时那一长串的字符就是节点的id。
git中存在一个指针被称为HEAD,HEAD表示当前所在的节点。你可以这么理解,树形结构中的一个一个节点就相当于玩单机游戏时存储的一个一个进度,HEAD指向的是当前进度,HEAD指向哪个节点,项目就会变成哪个节点的状态。比如我们在a3时存储了.gitignore,如果我们将HEAD移动到a2节点,由于a2节点时还没有创建文件,所以你会发现项目中的.gitignore会消失不见。(git log中的某个记录后边会跟着一个HEAD,HEAD的位置就表示当前所在的位置)
如何移动HEAD的位置呢?很简单使用git checkout
命令,checkout的使用方式有很多,比如可以直接在checkout后边跟着节点的id,即可直接将HEAD移动到指定的节点。当然id不需要写完整,只需要写个4-5位即可。如果我们想切换到a2节点,通过log查看可以得知a2节点的id为11210385…。可以通过执行命令git checkout 1121
切换到a2节点。
通过这种方式可以切换到项目的不同状态,就像单机游戏中的加载进度一样,如此一来我们就可以将代码方便快捷的恢复到不同的状态。
上面说过,git中文件的结构就像是一棵大树,每一次我们对文件的操作就会在树上添加一个节点。默认情况下这棵文件树只有一个主干,这个主干我们称之为master,也就是git中的主分支。当我们checkout 分支名时,会自动回到当前分支的最新节点。所以如果使用git checkout master
就可以回到主分支的最新节点。
使用git status
可以查看当前所在的分支。
也可以通过git log
来查看。
场景一 创建分支
回到虚拟场景中,现在假设我的网站已经编写完毕,正常上线。现在我想在原来网站的基础上开发一些新的功能,但是有不想影响原来的代码,要怎么做?这里我们可以直接创建一个新的分支,所有新的功能都在新分支上进行,好处就是主干于分支是相互独立的,对分支的修改不会影响到主干的内容。
创建新分支:
git branch 分支名
使用git branch new_site 创建一个新的分支,然后调用git checkout new_site
切换到新的分支上。
如此一来,我们所有的修改都是在新的分支上进行,不会影响到主分支master上的内容,接下来创建一个新的about.html并提交到新分支上。
此时new_site分支上添加了about.html,在master分支上并没有添加,换句话说new_site分支比master要更快一步,项目在git仓库中发生了分叉,又之前的一个主分支分为了两个分支。如果你愿意可以通过checkout在两个分支间进行切换,直接使用git checkout 分支名
即可。
场景二 合并分支
好了现在新版本的网站开发完毕,现在我需要将新添加的内容合并到master(主分支)中,由于项目本身结构简单所以合并起来也是非常容易。首先需要先使用checkou切换回主分支。然后调用git merge 分支名
来将其合并到主分支中。
合并后new_site分支已经没有存在的意义,可以使用git branch -d 分支名
将其删除。
场景三 更加复杂的情况
上边的场景是合并分支最简单的场景,两个分支中的文件没有任何的冲突(没有同时修改同一个文件),现在我们看一种相对复杂一些的情况。
现在我们通过git checkout -b 分支名
创建一个新分支,该指令用户创建并切换到一个新的分支,相当于branch+checkout。
然后在test分支中对index.html进行修改并提交。
然后切换回master分支,同样对index.html进行修改并提交。
现在我们在两个分支中,都对index.html进行了修改,由此情况变得稍微复杂一些,存储结构大致如下:
虽然情况变得复杂,但是步骤还是大体类似,先切换到master分支,然后调用merge进行合并。
合并时提示错误,CONFLICT (content): Merge conflict in index.html,conflict表示冲突,git尝试合并文件时发现冲突(因为两个分支都对该文件进行了修改)。首先git回尝试自动合并。Automatic merge failed; fix conflicts and then commit the result.表示自动合并失败,git无法处理这个问题,我们需要手动处理,此时打开发生冲突的文件index.html,会出现如下内容:
在<<<<<<< HEAD
和>>>>>>>>test
中间的内容就表示发生冲突的内容,====上边的是当前分支中的代码,====后边的是被合并分支的代码,这是这段内容的冲突导致git不知道如何处理,这里我们需要手动解决冲突,根据不同需要处理方式也不同,而我期望保留两次的修改所以我直接将冲突的标识删除,修改为如下的样子:
手动处理以后,需要调用git add
文件名来标识冲突已解决。然后调用git commit -m
提交一次即可完成合并。