Git是什么?——Git是目前世界上最先进的分布式版本控制系统(没有之一)。
最近重新学习了Git,相比以前感觉自己对Git有了全新的认识,于是将学习Git时的笔记整理成文,分享给有需要的朋友。本文面向的读者是了解过Git的,而不是对Git一无所知(即对小白可能有阅读压力)。更适合温习提升阅读。
本文主要介绍Git的工作流程和使用方法。主要内容有:什么是版本控制,搭建Git环境,Git的一些历史背景;本地仓库,远程仓库,仓库的分支管理;
目录
什么是Git
Git是什么?——Git是目前世界上最先进的分布式版本控制系统(没有之一)。
版本控制系统:设计师在设计的时候做了很多版本
经过了数天去问设计师每个版本都改了些啥,设计师此时可能就说不上来了。这个时候如果能有一个软件能记录每次的文件改动,并且还能协调多用户编辑,那岂不是美滋滋?这个软件应用起来应该像这个样子:
版本 | 文档名 | 操作用户 | 日志 | 修改时间 |
1 | shejigao.txt | zhangsan | 修改标题 | 2019-10-01 10:10:31 |
2 | shejigao.txt | lisi | 删除备注信息 | 2019-10-01 10:11:49 |
3 | shejigao.txt | lisi | 增加了许可协议 | 2019-10-03 11:31:00 |
4 | shejigao.txt | zhangsan | 修改版权信息 | 2019-10-05 09:32:11 |
恰好,Git就能实现这样的功能!
分布式&集中式
Linus一直痛恨的CVS及SVN都是集中式的版本控制系统,而Git是分布式版本控制系统,集中式和分布式版本控制系统有什么区别呢?
集中式版本控制系统:
版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活,推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,这还不得把人给憋死啊。
分布式版本控制系统:
首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。
既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。
在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
一些后话:
CVS作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的SVN修正了CVS的一些稳定性问题,是目前用得最多的集中式版本库控制系统。
除了免费的外,还有收费的集中式版本控制系统,比如IBM的ClearCase(以前是Rational公司的,被IBM收购了),特点是安装比Windows还大,运行比蜗牛还慢,能用ClearCase的一般是世界500强,他们有个共同的特点是财大气粗,或者人傻钱多。
微软自己也有一个集中式版本控制系统叫VSS,集成在Visual Studio中。由于其反人类的设计,连微软自己都不好意思用了。
分布式版本控制系统除了Git以及促使Git诞生的BitKeeper外,还有类似Git的Mercurial和Bazaar等。这些分布式版本控制系统各有特点,但最快、最简单也最流行的依然是Git!
Git与Github
Git是一个分布式版本控制系统,简单的说其就是一个软件,用于记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的软件。
Github(https://www.github.com)是一个为用户提供Git服务的网站,简单说就是一个可以放代码的地方(不过可以放的当然不仅是代码)。Github除了提供管理Git的web界面外,还提供了订阅、关注、讨论组、在线编辑器等丰富的功能。Github被称之为全球最大的基友网站。
Git环境搭建
Windows安装
- 去Git官网下载:https://git-scm.com/
- 安装exe可执行文件
- 当安装完毕后在桌面右键可以看到以下内容,表明安装成功:
配置Git
- 在开始菜单里找到“Git”->“Git Bash”、或者在某个文件夹右键打开“Git Bash Here”,打开一个命令行窗口
- 配置用户名和电邮。
- git config --global user.name "Your Name"
- git config --global user.email "email@example.com"
配置文件
当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:C:\Users\KYLE\.gitconfig
配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
配置文件放哪了?每个仓库的Git配置文件都放在.git/config文件中
忽略特殊文件
在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
- 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
常见规则写法有如下几种:
1)/mtk/ 过滤整个文件夹
2)*.zip 过滤所有.zip文件
3)/mtk/do.c 过滤某个具体文件
4) !index.php 不过滤具体某个文件
在文件中,以#开头的都是注释。
Git GUI工具
Github for Desktop
- Github出品的软件,功能完善,使用方便。
- 界面干净,用起来非常顺手,顶部的分支时间线非常绚丽。
Source tree
号称是最好用的Git GUI工具。功能丰富,基本操作和高级操作都非常流畅,适合初学者上手。
TortoiseGit
与SVN相似却是针对Git的GUI工具
它与其前辈TortoiseSVN都是非常优秀的开源版本控制客户端软件。
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了。
上面是对Git的初步认识,接下来开始正式使用Git来管理代码了!Enjoy.^.^.
本地仓库
仓库,英文名repository,又叫版本库,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
本地仓库的三个操作区域和工作流程:
创建仓库
创建一个仓库
- 选择一个合适的地方,创建一个空目录。如果你使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。
- 在这个空目录处右键,点击“Git Bush Here”或者“Git GUI Here”。
- 通过git init命令把这个目录变成Git可以管理的仓库。
文件提交
交给仓库管理:
- 在该文件夹创建你想要让Git管理的文件。强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
- 用命令git add告诉Git,把文件添加到仓库:git add 文件名
- 用命令git commit告诉Git,把文件提交到仓库:git commit -m "提交时的说明"。另外运行git status看看当前仓库的状态
此时,我们可以解释提交文件到仓库在Git中的执行过程了:
- 正在编辑的文件存放在工作区,使用git add命令提交到暂存区
- 暂存区的文件使用git commit命令提交的仓库存储做真正的存储
文件版本前进回退
版本回退前进:
- HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。
- 版本回退:用git log可以查看提交历史,以便确定要回退到哪个版本。
- 重返未来:用git reflog查看命令历史,以便确定要回到未来的哪个版本。
版本回退实例:
1、进行三次提交
2、查看提交历史
3、回退到第一次提交时的内容:
4、前进到以前的最终版本:
操作工作区
丢弃工作区的修改,用命令git checkout -- 文件名
操作暂存区
丢弃暂存区中的修改:
- 用命令git reset HEAD 文件名,就回到了工作区
- 再用工作区的丢弃操作即可
改名文件,并且将这个改名放入暂存区:git mv [file-original] [file-renamed]
标签
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支(branch)可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
Git有commit,为什么还要引入tag?
- “请把上周一的那个版本打包发布,commit号是6a5819e...”
- “一串乱七八糟的数字不好找!”
- 如果换一个办法:
- “请把上周一的那个版本打包发布,版本号是v1.2”
- “好的,按照tag v1.2查找commit就行!”
- 所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
- 命令git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定一个commit id;
- 命令git tag -a <tagname> -m "blablabla..."可以指定标签信息;
- 命令git tag可以查看所有标签。
标签:就是给某个commit设置容易记住且有意义的名字;
对分支创建标签
- 切换到需要打标签的分支上:git branch <name>
- 打标签:git tag <name>
- 查看所有标签:git tag
对以前的commit打标签:
- 先得到历史提交的commitID(git log --pretty=oneline --abbrev-commit)
- 再打标签:git tag <name> commitID
- 查看某个标签:git tag <tag_name>
操作标签
- 删除本地(即未提交到远程/GitHub)的标签:git tag -d <标签名>
- 删除已经提交了远程的标签:
- 先从本地删除:git tag -d <标签号>
- 然后,从远程删除。删除命令也是push,但是格式如下: git push origin :refs/tags/标签号
远程仓库
- 内网仓库
- GitHub
- Gitee
使用步骤:
- 在合适的文件夹内,右键,打开Git命令行窗口,使用clone指令克隆线上仓库到本地:git clone 线上仓库地址
- 自己写完代码后的提交操作:git push
- 拉取别人写的代码更新操作:git pull
- 在每天工作的第一件事就是先git pull拉取线上最新的版本;每天下班前要做的是git push,将本地代码提交到线上仓库。
本地仓库上传至远程仓库(基于SSH协议):
- 建立连接SSH key:ssh-keygen -t rsa -C "kyleshawe@outlook.com"
- 关联远程仓库:git remote add origin git@github.com:michaelliao/learngit.git 注意:git@后面的完全取决于你的github的网址,然后追加".git";
- 将本地仓库推送至github:git push -u origin master (首次使用);git push origin master
分支管理
什么是分支:
在开发的时候往往是团队协作,多人进行开发,因此光有一个分支是无法满足多人同时开发的需求的,并且在分支上工作并不影响其他分支的正常使用,会更加安全,Git鼓励开发者使用分支去完成一些开发任务。
分支相关指令(增删改查):
- 创建分支:git branch 分支名
- 删除分支:git branch -d 分支名
- 合并某分支到当前分支:git merge 被合并的分支名
- 查看分支:git branch
- 创建+切换分支:git checkout -b 分支名
- 切换分支:git checkout 分支名
分支合并冲突
冲突的产生原因:
- 现在本地仓库文件a.txt的版本为V100。我提交到远程仓库后,该文件在远程仓库的版本为V100。
- 某位同事拉取了版本为V100的a.txt文件,做了修改,并提交到远程仓库,此时版本为V101。
- 此时,我也对a.txt文件进行了修改,提交时则会有冲突。此时我这里的版本是V101,远程仓库的版本理应是V100,由此提交失败,有冲突产生。
解决冲突:
- 先把自己的修改内容做个备份。
- 执行git pull,拉取远程仓库上的最新代码。
- 再根据最新的代码进行修改,也即基于现在最新的代码上重新写。
- 教训:上班第一件事先git pull,可以在一定程度上避免冲突的产生。
分支实例
分支合并
- 在已有master分支的基础上,创建一个dev分支:git checkout -b dev
- 查看当前分支:git branch (当前分支前面会标一个*号。)在当前分支上做了修改,添加(git add <file>)并提交(git commit -m <message>);
- 切换回master分支:git checkout master
- 把dev分支的工作成果合并到master分支上:git merge dev
- 合并完成后,就可以放心地删除dev分支了:git branch -d dev
BUG临时分支:
- 背景:在一个abab分支上进行自己的工作时,突然要去处理BUG;则需要保存现在的abab分支,然后去处理BUG;
- 把当前工作区储藏:git stash
- 转至BUG所在的分支:git checkout <BrunchName>
- 修改完毕后,添加(git add <file>)提交(git commit -m <message>)
- 回到master分支,合并分支:git merge --no-ff -m <message> <BrunchName>
- 回到自己的abab分支:git checkout dev
- 显示工作现场:git stash list
- 恢复工作现场:一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;另一种方式是用git stash pop,恢复的同时把stash内容也删了
Feature分支:
- 背景:临时添加需求,然后又取消了需求;
- 新需求名为featureA(在分支dev下):git checkout -b featureA
- 开发完毕:git add <file>; git commit -m "featureA finished."
- 切回dev,准备(还未合并)合并:git checkout dev
- 就在此时,接到上级命令,因经费不足,新功能必须取消!虽然白干了,但是这个包含机密资料的分支还是必须就地销毁:git branch -D featureA;(由于没有被merge,所以-D删除后就找不回了)