前言
在9月的最后一天,面临着即将的国庆小长假,内心就像脱缰的野马在草原上肆无忌惮地放肆。永伟大哥似乎发现了这一点,因此打算给我们几个新人讲一下关于Git的事情,也算是补上之前挖的坑。特此记录。
简介
Git目前是世界上最先进的分布式版本控制系统(没有之一)。
所谓的版本控制系统,简而言之则是对于需要改动的文件每次修改的版本进行一个记录的系统。
写论文的时候经常会出现一个情况:想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后一篇论文变成了:
过了几天,再想找回被删除的段落,但是早已记不住忘记删除前保存在哪个文件里,只好一个个凭借记忆去找,非常麻烦。这还只是一个文档,且仅有自己一个人参与编辑,如果有多个人编辑多个文档的情况下,想要找回之前的内容,非常困难。
而所谓的版本控制系统,则是可以记录每一次文件的改动,待日后需要找到之前的版本时,非常的清晰:
版本 | 文件名 | 用户 | 说明 | 日期 |
---|---|---|---|---|
1 | paper.doc | 陈默 | 删除了软件服务条款5 | 7/12 10:38 |
2 | paper.doc | 陈默 | 增加了License人数限制 | 7/12 18:09 |
3 | paper.doc | 陌尘 | 财务部门调整了合同金额 | 7/13 9:51 |
4 | paper.doc | 陈默 | 延长了免费升级周期 | 7/14 15:17 |
这样,当我们想要管理多个版本的时候,非常容易。
历史
Git的历史
很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。
Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?
事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!
你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。
不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。
安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。
Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:
Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。
Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。
历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。
——廖雪峰
集中式与分布式
集中式
集中式版本控制系统,版本库是集中存放在中央服务器的。客户端需要先从中央服务器获取最新的版本,然后再开始干活。当工作完成了,再将自己的版本推送到一个中央服务器。
集中式版本控制系统,主要以SVN
,CVS
等为主。
优点:
- 版本统一:大家都是从中央服务器获取版本,因此版本都是统一的。
缺点:
- 需要网络:集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,效率非常慢。
- 安全性低:中央服务器坏掉了就完蛋了。
分布式
分布式与集中式最大的区别就是没有中央服务器,每个人的电脑都具备一个完整的版本库。
这样的好处就是,即使在没有网络的情况下,也可以获取版本然后工作。即使是多人协作,也只需要把对方修改的版本推送给对方,然后就可以看到彼此的修改。
分布式主要有Git, Mercurial,Bazaar,以及IBM的ClearCase(收费)。
优点:
- 安全性高:即使某一个人的电脑坏掉了,也可以从其他人复制过来;
缺点:
版本不一致:由于分布式可以在不用联网的情况下工作,因此可能导致同一份工作不同人的版本大相径庭。
- 在实际使用分布式版本控制系统的时候,通常都会有一台充当“中央服务器”的电脑,但是这服务器的作用仅仅是方便大家交换大家的修改。需要注意的是,没有它也可以继续工作,只是交换修改的时候不是很方便,因此也算不上缺点。
由于并非所有人工作的时候都可以有多台电脑,然后使用其中一台充当“中央服务器”,纵然我们也可以在一台电脑上的不同文件夹充当不同的电脑。但是当电脑硬盘坏掉的时候,所有的数据还是都消失了,所以这样意义不大。
因此,Github
应运而生(当然国内的Gitee
也不错),大家可以把Github
当成自己分布式版本控制系统的“中央服务器”。Github
还有像社区一般的功能,大家可以在上面分享自己的工作成果。有人可能会想到,为什么要把自己的代码交给第三方的平台保管,不能放在自己的服务器上自己保管吗?答案是当然可以,因此也就是以Gitlab
为首的“Github私有云
”版本。大部分公司应该都是使用的Gitlab
将公司的代码保存在公司的服务器上,以确保数据安全性。
分支管理
Git中除了安全性与无需网络这些优点以外,最不可忽视的就是强大而分支管理系统,纵然其他版本控制系统,例如SVN也有分支管理系统,但是这些版本控制系统创建分支和切换分支比蜗牛还慢,简直让人无法忍受,最后成了摆设,大家都不用。
简述Git工作流程
在介绍分支管理系统之前,需要简单简述一下Git的工作流程。
- 创建Git仓库;
- 新增,修改文件;
- 将文件添加到暂存区;
- 将文件提交到版本库;
- 将文件推送到远程仓库(Github,Gitlab)
当然实际工作过程中的步骤,在不同的场景下远不止这些,但这些可以构成基础的Git工作流程。
分支管理
分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。
如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
分支模型
根据上述描述,应该可以知道分支是多么的重要。但由于不同人有着不同的工作习惯,即使是对于创建一个文件的名字,也会出现各种各样。例如大驼峰,小驼峰,下划线,中划线等。因此对于复杂的分支管理,也应当有清晰的流程与规划。
Vincent Driessen 为了解决这个问题提出了 A Successful Git Branching Model
以下是基于Vincent Driessen提出的Git Flow
流程图
Git flow
核心分支
Git flow模型的最最核心的分支其实只有两个,Develop分支
与Master(Production)分支
.
- Master(Production)分支:这个分支最近发布到生产环境的可以稳定运行的代码。
- Develop分支: 开发分支,用于开发时提交代码的分支。
对于一些小的项目或是小的开发者,有这两个分支就已经可以胜任工作了。开发一个项目时,只需要在Develop分支中开发,等到要发布版本时,再将版本发布后,Develop分支的代码合并到Master。
规范的项目上线流程:
注意:项目上线时,务必要按照黑色箭头流程进行,这样即使当项目上线后,如若立马发现紧急bug,还可以将此时Master的代码(此时Master为上个版本的代码)重新再上线。因此在3-4之间应当保持一定时间间隔。
辅助分支
Git flow模型的辅助分支有三大类:Feature分支
,Release分支
,Hotfix分支
。
- Feature分支:开发新需求时可以使用,等需求开发完成合并会develop,此时删除Feature分支;
- Release分支:开发完成时可以基于Develop分支创建,创建后可以在Release分支上测试和修改bug。发布Release分支是,合并Release到Master和Develop,同时在Master分支上打个Tag记住Release版本号,然后可以删除Release分支了。
- HotFix分支:当Master上出现了紧急bug时,需要基于Master分支创建hotfix分支,开发完成之后合并回Master分支和Develop分支,随后删掉hotfix分支。
Git Flow 命令示例
创建 Develop
xxxxxxxxxx
21git branch develop
2git push -u origin develop
开始 Feature
xxxxxxxxxx
91# 通过develop新建feaeure分支
2git checkout -b feature develop
3# 或者, 推送至远程服务器:
4git push -u origin feature
5
6# 修改md文件
7git status
8git add .
9git commit
完成 Feature
xxxxxxxxxx
131git pull origin develop
2git checkout develop
3
4#--no-ff:不使用fast-forward方式合并,保留分支的commit历史
5#--squash:使用squash方式合并,把多次分支commit历史压缩为一次
6
7git merge --no-ff feature
8git push origin develop
9
10git branch -d some-feature
11
12# 如果需要删除远程feature分支:
13git push origin --delete feature
开始 Release
xxxxxxxxxx
11git checkout -b release-0.1.0 develop
完成 Release
xxxxxxxxxx
151git checkout master
2git merge --no-ff release-0.1.0
3git push
4
5git checkout develop
6git merge --no-ff release-0.1.0
7git push
8
9
10git branch -d release-0.1.0
11git push origin --delete release-0.1.0
12
13# 合并master/devlop分支之后,打上tag
14git tag -a v0.1.0 master
15git push --tags
开始 Hotfix
xxxxxxxxxx
11git checkout -b hotfix-0.1.1 master
完成 Hotfix
xxxxxxxxxx
151git checkout master
2git merge --no-ff hotfix-0.1.1
3git push
4
5
6git checkout develop
7git merge --no-ff hotfix-0.1.1
8git push
9
10git branch -d hotfix-0.1.1
11git push origin --delete hotfix-0.1.1
12
13
14git tag -a v0.1.1 master
15git push --tags
Git常用命令
常用操作:
xxxxxxxxxx
121# 创建git仓库
2git init
3# 创建README文件
4touch README
5# 将README文件添加至暂存区
6git add README
7# 提交信息
8git commit -m 'first commit'
9# 添加远程仓库
10git remote add origin git@github.com:xxxx.git
11# 将代码推送到远程仓库的master分支
12git push -u origin master
远程仓库:
xxxxxxxxxx
231# 克隆仓库:
2git clone git://github.com/jquery/jquery.git
3
4# 查看远程仓库:
5git remote -v
6
7# 添加远程仓库:
8git remote add [name] [url]
9
10# 删除远程仓库:
11git remote rm [name]
12
13# 修改远程仓库:
14git remote set-url --push [name] [newUrl]
15
16# 拉取远程仓库:
17git pull [remoteName] [localBranchName]
18
19# 推送远程仓库:
20git push [remoteName] [localBranchName]
21
22# 提交本地test分支作为远程的test分支
23git push origin test:test
分支命令:
xxxxxxxxxx
271# 查看本地分支:
2git branch
3
4# 查看远程分支:
5git branch -r
6
7# 创建本地分支: ----注意新分支创建后不会自动切换为当前分支
8git branch [name]
9
10# 切换分支:
11git checkout [name]
12
13# 创建新分支并立即切换到新分支:
14git checkout -b [name]
15
16# 删除分支: ---- -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项
17git branch -d [name]
18
19# 合并分支: ----将名称为[name]的分支与当前分支合并
20git merge [name]
21
22# 创建远程分支(本地分支push到远程):
23git push origin [name]
24
25# 远程分支:
26git push origin :heads/[name]
27git push origin :[name]
标签(tag)操作:
xxxxxxxxxx
261# 查看版本:
2git tag
3
4# 创建版本:
5git tag [name]
6
7# 删除版本:
8git tag -d [name]
9
10# 查看远程版本:
11git tag -r
12
13# 创建远程版本(本地版本push到远程):
14git push origin [name]
15
16# 删除远程版本:
17git push origin :refs/tags/[name]
18
19# 合并远程仓库的tag到本地:
20git pull origin --tags
21
22# 上传本地tag到远程仓库:
23git push origin --tags
24
25# 创建带注释的tag:
26git tag -a [name] -m 'yourMessage'
Stash
工作中经常会出现一种情况:目前的需求未完成,突然来了一个非常紧急的需求要求立马做。此时由于之前的需求并未完成,因此最好不要执行commit提交操作。这种情况下,就可以使用stash将目前进度存储到堆栈(“抽屉”)里。
xxxxxxxxxx
271# 存储到堆栈:
2git stash save -u "备注信息"
3
4# 查看堆栈中的 stash 列表
5git stash list
6
7# 查看stash内容
8git stash show
9git stash show stash@{id}
10
11# 将堆栈中的 stash 应用到工作区
12# 将堆栈中的指定 stash 应用到工作区(保留堆栈的内容)
13git stash apply stash@{id}
14# 将堆栈中的最近一次 stash,应用到工作区(保留堆栈的内容)
15git stash apply
16# 等价于上面的一条命令
17git stash apply stash@{0}
18# 将堆栈中的最近一次 stash,应用到工作区(删除堆栈的内容)
19git stash pop
20
21# 删除堆栈中的 stash
22# 指定id
23git stash drop stash@{id}
24# 删除最近一次的 stash
25git stash drop
26# 删除所有的 stash
27git stash clear
参考文献
- Git Flow 的正确使用姿势 – 简书 (jianshu.com)
- Git 常用命令大全dengsilinming的专栏-CSDN博客git常用命令
- 分支管理 – 廖雪峰的官方网站 (liaoxuefeng.com)