最近公司来了不少小伙伴,一开始我只是以为他们对Git使用不熟练,我跟他们零零散散讲过几次Git的一些使用方法,让他们回去自己上网找些资料看看,感觉对他们来说这些不是什么问题。直到有一天,我让他们提交代码的时候,他们半天都提交不上来。我过去打听原因的时候,惊讶地发现他们在磁盘里面的项目都是按日期命名文件夹存放的,合并代码是通过手工对比工具完成的。经过交流,我发现他们认为Git的门槛比我想象中还要高。貌似有SVN使用经验的人学习Git会感觉很不适应。我想只要把Git的一些概念搞清楚,学习起来应该也不难。于是,我想结合自己粗浅的一些理解,聊聊Git。首先从Git是什么说起吧。
Git是什么
事情是这样的,自2002年开始,林纳斯·托瓦兹决定使用BitKeeper作为Linux内核主要的版本控制系统用以维护代码。到了2005年,Linux内核开发团队的安德鲁·垂鸠写了一个简单程序,可以连接BitKeeper的存储库。这时BitKeeper著作权拥有者拉里·麦沃伊不高兴了,认为这事对BitKeeper内部使用的协议进行逆向工程,于是决定收回Linux内核开发团队无偿使用BitKeeper的许可。这点小事肯定难不倒大神林纳斯,仅仅10天之后,第一个git版本就写出来了。看到这里大家应该都知道git是一个版本控制管理系统了。对,Git就是一款开源的分布式版本控制系统(Distributed Version Control System)。
版本控制系统
为什么需要版本控制?举个栗子,某天产品经理提了一个新需求,程序员阿秃经过连续几个晚上通宵奋战,终于用方案A完美实现了。然后高兴都跑去让产品经理验收。产品经理看完后,丢给阿秃一句,这是什么狗屎?不行,给我换方案B重做。阿秃只好把方案A的代码都删了,按照方案B,再经过几个晚上的奋战,用方案B把需求实现了。阿秃又高兴地跑去让产品经理验收。这时产品经理说,好像还是方案A合理一点,你给我改回方案A吧……此时阿秃是奔溃的,因为他没有使用版本控制工具。如果使用Git做版本控制的话,只需在键盘上敲入几行命令就能找回方案A的代码,阿秃自然不用再通宵了。版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。运用版本控制系统,我们可以对代码进行版本管理,可以随时查看之前版本的内容,随时回溯到之前版本中。团队合作的时候也可以自动合并代码,而不需要用一个共享文件,或者是进行定期的备份。
集中式和分布式版本控制系统
要理解SVN和Git的使用差别为什么那么大,最直接的办法是搞清楚集中式和分布式版本控制系统之间的差别。集中式版本控制系统有单一的集中管理服务器,保存所有文件的修订版本,所有的文件。客户端的电脑并不会保存历史版本,所以如果想查看历史版本,必须联网才能查看,并且如果集中管理的服务器出现故障,可能导致历史版本丢失,最多只能从本地的文件恢复到最后的版本。另外网速直接影响提交和下载文件的速度,很多时候很影响使用体验。
分布式版本控制系统没有集中管理的服务器,每个人的电脑都有一个完整的版本库,我们可以在本地进行修改提交,查看历史版本。这里很多人有个疑问,我们平时工作中,经常把代码推到“远程仓库”,这个“远程仓库”不就是集中式版本控制系统里面的集中管理服务器嘛?其实,这只是分布式版本控制系统里面的一个节点而已,这和你自己的电脑还有你同事阿秃的电脑一样,都是可以理解为其中一个节点。为了协作方便,我们都在这个“远程仓库”上面交换“修改”,所以容易给大家造成中央服务器的错觉。
基本概念
- 工作拷贝(工作目录):用于存放产品开发数据本地工作目录。
- 暂存区 (Index):用于存放待提交数据的缓存区。
- 本地仓库:远端库的一个完整的拷贝,包括所有文件的修改记录,分支等。
- 远程仓库:本地仓库clone来源。
- 快照(snapshot):版本库某个时间点所有文件集合。
- 全球版本号(commitID):Git库的版本号是通过SHA-1算法根据库中的所有内容计算出一个40位的哈希值,这个哈希值是全球唯一的,基本只要前六位就可以唯一标识了。
理解修改文件在Git的流动
我们所有修改都是在工作目录进行的,修改完以后需要先添加到暂存区,然后再提交到本地仓库。提交完以后会产生commitID,标识当次提交。在之后的操作中,可以通过commitID回滚到某次提交状态。最后,我们还需要把本地的仓库的提交记录推送到远程仓库。
基本操作
初始化仓库
仓库的初始化有两种方式,第一张是直接在当前目录初始化,执行如下命令:
git init
另外一种是从远程仓库克隆,执行命令如下:
git clone 中心库名称地址 本地工作目录名称
添加文件到暂存区
实际工作中,我们在本地初始化仓库以后,就会往工作目录里面新增文件或者修改仓库里面已有的文件,当我们完成我们的新增或者修改后,需要把新增或者修改的文件添加到暂存区,执行命令如下:
git add 文件名
添加所有文件可以执行以下命令:
git add .
提交到本地仓库
接下来,我们就需要把暂存区的文件提交到本地仓库,执行命令如下:
git commit -m '提交信息'
更新本地分支
在我们把本地仓库的提交记录push到远程仓库之前,最好先pull远程仓库对应的“追踪分支”的更新。我们需要用到git pull 命令。git pull 命令的作用是拉取远程主机某个分支的更新,并与本地指定分支合并。很多时候代码冲突是需要在这时候解决的。git pull的完整使用方法如下:
git pull <远程主机名> <远程分支名>:<本地分支名>
其实Git本地分支和远程分支之间可以建立一种追踪关系(tracking)。在使用git clone初始化仓库的时候,所有本地分支默认与远程仓库的同名分支建立追踪关系。比如,本地的master分支自动追踪origin/master分支。
注意:origin一般指原始仓库地址的别名
另外也可以手动建立这种追踪关系,命令如下:
git branch --track <本地分支名> <远程主机名>/<远程分支名>
如果当前本地分支和远程分支建立了追踪关系,使用git pull就可以省略远程分支名,命令如下:
git pull origin
如果当前分支只有一个追踪分支,那么远程主机名也可以省略,命令如下:
git pull
本地仓库提交记录推送到远程仓库
为了实现团队协作,我们的更改,最终肯定需要推送到远程仓库,让团队中的其他同学合并的。这里我们使用git push命令,完整命令如下:
git push <远程主机名> <本地分支名>:<远程分支名>
和git pull一样,如果本地分支和远程分支之间存在“追踪关系”,我们可以省略远程分支名,命令如下:
git push <远程主机名> <本地分支名>
这里需要注意,如果省略的是本地分支名,就相当于把一个空的本地分支推送到远程分支,这个操作就是删除远程分支。
git push <远程主机名> :<远程分支名>
#相当于
git push <远程主机名> --delete <远程分支名>
查看提交历史
很多时候由于各种原因,我们需要回滚到某次历史提交,这时候可以通过一下命令查看:
git log
代码回滚
代码回滚主要有两种方式,git revert和git reset。
- git revet是提交回滚,即忽略指定的版本,然后提交一个新的版本。新的版本中会删除指定的版本。注意:git revert是会产生一次新的提交的。
git revert < commitID >
- git reset的作用是重置到指定的版本。通常是配合以下两种参数一起使用的: --soft 和 --hard。
默认参数 --soft, 所有commit的修改都会退回到git缓冲区。
参数--hard,所有commit的修改直接丢弃。
命令如下:
#回退到上个版本
git reset --hard HEAD^
#退到/进到 指定commitID
git reset --hard < commitID >
人生总是无常的,有时候你回滚完代码后,可能你又后悔了,你又想回滚到你回滚之前,怎么办?git reflog可以帮到你。你用git reflog打印你的每一次操作,找到你的操作id,就可以轻松回到你回滚之前的版本了。具体命令如下:
#查看你回滚操作,找到回滚操作的commitID
git reflog
#回退到指定的回滚版本
git reset --hard < commitID >
总结
git之所以如此强大,是因为它可以通过各种命令灵活使用,应对各种复杂的场景。机械地记忆几条命令的组合是没办法满足各种使用场景的,所以有时间还是需要慢慢理解各个命令的使用细节。熟练掌握上面那几条命令应该可以应付日常工作的大部分使用场景了,当然git还有很多其他很强大的命令,大家可以深入学习一下。
网友评论