摘要
在这段时间和队友合作写一个项目的过程中,合代码一直是我学习过程中迈不过去的大石头,绕也绕不过去的那种,这次痛定思痛决定研究一下git的原理,它的那些命令到底对应着git内部的哪些操作?它的工作原理是什么?为什么合并冲突有时候把我自己本地的代码毁掉了?如何正确高效地合并冲突?开发的时候应该如何进行多人合作?它的哪些好用的命令我还没有使用过?
下面我将就以下几个方面来完成这次分享。
- 第一,git的基本介绍(它是什么它有哪些基本性质等)
- 第二,git的基本工作原理
- 第三,通过一个解决冲突的案例来巩固对git基本工作原理的理解
- 第四,具体工作流程
- 第五,开发过程中分支的作用及其工作原理
此外由于时间原因,以下的一些内容没有涉及到,但是在后续学习过程中会完善博客内容。
- 多人合作共同开发的工作流程
- 如何进行版本控制
- 具体命令对应的各种原理性操作
1、git的基本介绍
目录
- git是什么
- git能够解决哪些问题
- git的一些基本性质
1.1 git是什么?
git是一种分布式版本控制系统
1.1.1 什么是版本控制?
版本控制这个说法多少有一点抽象。事实上,版本控制这件事儿我们一直在做,只是平时不这么称呼。举一个栗子,boss让你写一个策划案,你先完成了一稿,之后又有了一些新的想法,但是并不确定新的想法是否能得到boss的认可,于是你保存了一个初稿,之后在初稿的基础上另存了一个文件,做了部分修改完成了一个修改稿。OK,这时你的策划案就有了两个版本——初稿和修改稿。如果boss对修改稿不满意,你可以很轻易的把初稿拿出来交差。
在这个简单的过程中,你已经执行了一个简单的版本控制操作——把文档保存为初稿和修改稿的过程就是版本控制。
学术点说,版本控制就是对文件变更过程的管理。说白了,版本控制就是要把一个文件或一些文件的各个版本按一定的方式管理起来,目的是需要用到某个版本的时候可以随时拿出来
1.1.2 为什么是分布式?
这里的“分布式”是相对于“集中式”来说的。把数据集中保存在服务器节点,所有的客户节点都从服务节点获取数据的版本控制系统叫做集中式版本控制系统,比如svn就是典型的集中式版本控制系统。
与之相对,Git的数据不止保存在服务器上,同时也完整的保存在本地计算机上,所以我们称Git为分布式版本控制系统。
Git的这种特性带来许多便利,比如你可以在完全离线的情况下使用Git,随时随地提交项目更新,而且你不必为单点故障过分担心,即使服务器宕机或数据损毁,也可以用任何一个节点上的数据恢复项目,因为每一个开发节点都保存着完整的项目文件镜像。
1.2 为什么要使用git?
在未接触版本控制系统之前,很多人会通过保存项目或文件的备份来达到版本控制的目的。通常你的文件或文件夹名会设置成“XXX-v1.0”、“XXX-v2.0”等。
这是一种简单的办法,但过于简单。这种方式无法详细记录版本附加信息,难以应付复杂项目或长期更新的项目,缺乏版本控制约定,对协作开发无能为力。如果你不慎使用了这种方式,那么稍稍过一段时间你就会发现连自己都不知道每个版本间的区别,版本控制形同虚设。
- 能够对文件版本控制和多人协作开发
- 拥有强大的分支特性,所以能够灵活地以不同的工作流协同开发
- 分布式版本控制系统,即使协作服务器宕机,也能继续提交代码或文件到本地仓库,当协作服务器恢复正常工作时,再将本地仓库同步到远程仓库。
- 当团队中某个成员完成某个功能时,通过
pull request
操作来通知其他团队成员,其他团队成员能够review code
后再合并代码。
诸如此类,数不胜数。然而实现这些功能的基础是对文件变更过程的存储。
所以,如果问“Git能够解决哪些问题?”我们可以简单的回答:Git解决了版本控制方面的很多问题,但最核心的是它很好的解决了版本状态存储(即文件变更过程存储)的问题。
1.3 git的一些基本性质
1.3.1 直接记录快照,而非差异比较
Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。 概念上来区分,其它大部分系统以文件变更列表的方式存储信息。 这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。存储每个文件与初始版本的差异,如下图所示 -
Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。如下图所示 -
这是 Git 与几乎所有其它版本控制系统的重要区别。 因此 Git 重新考虑了以前每一代版本控制系统延续下来的诸多方面。 Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。 稍后我们在 Git 分支讨论 Git 分支管理时,将探究这种方式对待数据所能获得的益处。
1.3.2 近乎所有操作都是本地执行
在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。 如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git 在这方面会让你感到速度之神赐给了 Git 超凡的能量。 因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。
举个例子,要浏览项目的历史,Git 不需外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。 你能立即看到项目历史。 如果想查看当前版本与一个月前的版本之间引入的修改,Git 会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。
这也意味着你离线或者没有 VPN 时,几乎可以进行任何操作。 如你在飞机或火车上想做些工作,你能愉快地提交,直到有网络连接时再上传。 如你回家后 VPN 客户端不正常,你仍能工作。 使用其它系统,做到如此是不可能或很费力的。 比如,用 Perforce,你没有连接服务器时几乎不能做什么事;用 Subversion 和 CVS,你能修改文件,但不能向数据库提交修改(因为你的本地数据库离线了)。 这看起来不是大问题,但是你可能会惊喜地发现它带来的巨大的不同。
1.3.3 git保证完整性
Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
1.3.4 git一般只添加数据
你执行的 Git 操作,几乎只往 Git 数据库中增加数据。 很难让 Git 执行任何不可逆操作,或者让它以任何方式清除数据。 同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容;但是一旦你提交快照到 Git 中,就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
这使得我们使用 Git 成为一个安心愉悦的过程,因为我们深知可以尽情做各种尝试,而没有把事情弄糟的危险。 更深度探讨 Git 如何保存数据及恢复丢失数据的话题,请参考撤消操作。
2、git工作流程
目录
从时间先后来讲:
- 工作目录的内容是你当前看到的,也是最新的;
- index区标记了你当前工作目录中,哪些内容是被git管理的;
- 而本地仓库保存了对象被提交 过的各个版本,比起工作目录和暂存区的内容,它要更旧一些;
远程仓库是本地仓库的异地备份,远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库 修改,因此它可能与本地仓库同步,也可能不同步,但是它的内容是最旧的。
任何对象都是在工作目录中诞生和被修改;
- 任何修改都是从进入index区才开始被 版本控制;
- 只有把修改提交到本地仓库,该修改才能在仓库中留下痕迹;
- 而要与协作者分享本地的修改,可以把它们push到远程仓库来共享。
图最上方的 add、commit、push等,展示了git仓库的产生过程。反过来,我们可以从远程历史仓库中获得本地仓库的最后一个版本,clone到本地,从本 地检出对象的各个版本到index暂存区或工作目录中,从而实现任何对象或整个仓库的任意阶段状态的”回滚”。
一开始接触这些概念可能比较绕,其实在git入门阶段,可以先抛开远程仓库不看,只了解管理本地仓库的”3棵树”就够了。如下图:
2.2 文件的三种状态
对任何一个文件,在git内都有三种状态:
- 已提交(committed):表示该文件已经被安全的保存在本地数据库中了
- 已修改(modified):表示修改了某个文件,但还没有提交保存
- 已暂存(staged):表示把已修改的文件放在下次提交时要保存的清单中
我们可以从文件所处的位置来判断状态:
- Git 目录中保存着的特定版本文件—–>已提交状态
- 作了修改并已放入暂存区域—–>已暂存状态
- 自上次取出后,作了修改但还没有放到暂存区域—–>已修改状态
2.3 基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
3、通过一个解决冲突的案例来巩固对git基本工作原理的理解
目录
- 提交代码到远程仓库
- 将远程仓库代码更新到本地
- 更新到本地仓库时, 出现冲突,解决冲突
3.1 提交代码到远程仓库
将一个新建的工程提交至远程仓库,关键的git指令如下:1
2
3
4
5git init
git add README.md
git commit -m "首次提交代码"
git remote add origin https://github.com/wteam-xq/testGit.git
git push -u origin master
指令解释
git init
表示在当前的项目目录中生成本地的git管理;git add README.md
将“README.md”文件保存至缓存区;git commit -m "first commit"
将代码从缓存区保存至本地仓库;git remote add origin https://github.com/wteam-xq/testGit.git
将本地仓库与指定的远程仓库创建 联系;push -u origin master
将本地仓库代码推送至远程仓库
原理图
3.2 将远程仓库代码更新到本地
首先我们新建一文件夹:copyTestGit,进入该文件夹后使用git 指令:1
git clone
指令执行完毕后, 就在该文件夹下生成一份副本啦
原理图
接下来, 讨论git pull
、 git fetch
、 git merge
的关系
先抛简单结论:
1 | git pull |
等同于下面命令1
2git fetch
git merge
实际项目:我们在testGit工程中修改README.md,然后更新、提交下代码 执行以下git 指令(日常使用中会用git status
看看是否有文件需要git add
):1
2git commit -am 'update readme.md'
git push origin master
原理图
远程仓库代码更新后, 我们进入另一本地仓库:copyTestGit\testGit,将远程仓库的代码更新至该本地仓库。
在该目录下输入以下git指令:1
2git fetch
git merge origin/master
以上指令别的原理图
3.3 更新到本地仓库时出现冲突
首先在本地修改文件,现在副本工程修改完了代码打算提交,提交前先将远程仓库最新代码更新至本地仓库, 惯例使用指令:1
git pull
指令执行之后会发现以下冲突提示:
出现以上提示, 说明本次更新代码失败;主要在于本地工作区间跟远程仓库的新代码冲突了, 图解如下:
接下来,有两种方式处理冲突: 放弃本地修改或 解决冲突后提交本地修改
3.3.1 放弃本地修改
放弃本地修改意味着将远程仓库的代码完全覆盖本地仓库以及本地工作区间, 如果对git的指令不熟悉那大可以将本地工程完全删除,然后再重新拷贝一次(git clone
)。
当然, git如此强大没必要用这么原始的方法,可以让本地仓库代码覆盖本地修改,然后更新远程仓库代码;
本地仓库代码完全覆盖本地工作区间,具体指令如下:1
git checkout head .
(注意: 别遗漏 “head” 后的 “ .” )
然后更新远程仓库的代码就不会出现冲突了:1
git pull
原理图如下:
3.3.2 解决冲突后提交本地修改
缓存区 除了开始出现外,后续提交代码、更新代码篇章都在打酱油;终于,这次冲突解决事件, 它将会是主角!
解决冲突后提交本地修改的思路大概如下:
将本地修改的代码放在缓存区, 然后从远程仓库拉取最新代码,拉取成功后再从缓存区将修改的代码取出, 这样最新代码跟本地修改的代码就会混杂在一起, 手工解决冲突后, 提交解决冲突后的代码。
原理图:
对应到我们实际项目中, 进入 copyTestGit/testGit 执行指令git pull
出现 (重回到上述冲突场景)1
2
3
4error: Your local changes to the following files would be overwritten by merge:
README.md
Please, commit your changes or stash them before you can merge.
Aborting
将本地修改放入缓存区(成功后本地工作区间的代码跟本地仓库代码会同步), 具体指令:1
git stash
从远程仓库获取最新代码,具体指令:1
git pull
然后, 取出本地修改的代码, 具体指令:1
git stash pop
然后, git 自动合并冲突失败, 冲突的代码就很清晰的展现在我们面前了,手动解决冲突之后告诉git, 这个文件(README.md)的冲突 已经解决:1
git add README.md
提交代码(细节参考前述流程):1
2git commit -am '终于解决冲突啦!'
git push origin master
于是本地有冲突的代码就提交成功啦!
4、git具体的工作流程
在基于以上案例的基础上,我们可以总结出git工作的大致过程,具体内容参见另一篇博文
5、开发过程中分支的作用及其工作原理
目录
- git分支管理机制
- 原理图
- 主分支(master)
- 开发分支(develop)
- 功能分支(feature)
- 预发布分支(release)
- 修补bug分支(hotfixes)
5.1 git分支管理机制
在了解git分支的具体分类及使用之前,先要了解一下git分支管理机制,具体内容参见另一篇博文
5.2 原理图
5.3 主分支(master)
代码库应该有且只有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。
Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。
5.4 开发分支(develop)
主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做Develop。
这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行”合并”(merge)。
Git创建Develop分支的命令1
git checkout -b develop master
将Develop分支发布到Master分支的命令
1 | git checkout master |
–no-ff参数:默认情况下,Git执行”快进式合并”,会直接将Master分支指向Develop分支。强推。少用!!
5.5 临时性分支
前面讲到版本库的两条主要分支:Master和Develop。前者用于正式发布,后者用于日常开发。其实,常设分支只需要这两条就够了,不需要其他了。
但是,除了常设分支以外,还有一些临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种:
功能分支 (feature)
预发布分支 (release)
修补bug分支 (fixbug)
这三种分支都属于临时性需要,使用完以后,应该删除,使得代码库的常设分支始终只有Master和Develop。
5.6 功能分支(feature)
为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。
功能分支的名字,可以采用feature-*的形式命名。
创建一个功能分支1
git checkout -b feature-x develop
合并到develop分支
1 | git checkout develop |
删除feature分支1
git branch -d feature-x
5.7 预发布分支(release)
指发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。
预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。它的命名,可以采用release-*的形式。
创建一个预发布分支:1
git checkout -b release-x develop
确认没有问题后,合并到master分支:1
2git checkout master
git merge --no-ff release-x
对合并生成的新节点,做一个标签1
git tag -a x
再合并到develop分支1
2git checkout develop
git merge --no-ff release-x
最后记得删除预发布分支:1
git branch -d release-x
5.8 修补bug分支(hotfixes)
软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。
修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。它的命名,可以采用fixbug-*的形式。
创建一个修补bug分支:1
git checkout -b fixbug-xx master
修补结束后,合并到master分支:1
2
3git checkout master
git merge --no-ff fixbug-xx
git tag -a xx
再合并到develop分支:1
2git checkout develop
git merge --no--ff fixbug-xx
最后,删除”修补bug分支”:1
git branch -d fixbug-xx
ending 以下内容完善ing
6、多人合作共同开发的工作流程
在基于以上对文件在被git管理过程中所处的四个阶段、三种状态以及多种分支的功能作用以及原理的了解下,可以学习多人合作共同开发的工作流程了。
参考文章1:git多人协作工作流程
Git 多人协作开发的过程
需要继续研究的
参考文章
深入理解Git的实现原理
git 的工作流程(纯干货)
git工作原理
git原理图解
Git的思想和基本工作原理2
Git 思想和工作原理
Git——基本原理