版本控制-Git 概述 安装配置 Git基础使用 工作区和暂存区 远程仓库 分支管理 标签管理

Git是目前世界上最先进的分布式版本控制系统(没有之一)。Linus花了两周时间自己用C写了一个分布式版本控制系统,Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。

安装配置

Mac直接从AppStore安装Xcode,Xcode集成了Git。

设置git config

因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

查看git config

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

Git基础使用

查看提交历史

$ git log 
$ git log --pretty=oneline
版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示。

版本回退

首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,上一个版本就是HEAD,上上一个版本就是HEAD,当然往上100个版本写100个比较容易数不过来,所以写成HEAD~100。

$ git reset --hard HEAD^

指定回到未来的某个版本

git reset --hard 1094a

版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?

在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到add distributed版本时,再想恢复到append GPL,就必须找到append GPL的commit id。Git提供了一个命令git reflog用来记录你的每一次命令:
版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

工作区和暂存区

Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

撤销修改

撤销工作区的修改

$ git checkout -- readme.txt

丢弃工作区的修改,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。

撤销暂存区的修改

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

$ git reset HEAD readme.txt

删除文件

执行rm file删除一个文件,git把这次操作理解为一次工作区文件的修改。
版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

确实要从版本库中删除该文件,那就用命令git rm readme.txt 删掉,并且git commit

远程仓库

Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。

GitHub是提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。

GitHub远程仓库的准备

本地Git仓库和GitHub仓库之间的传输是通过SSH加密的。所以,需要一点设置:

添加远程仓库

在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步

$ ssh-keygen -t rsa -C "youremail@example.com"

你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。

登陆GitHub,打开“Account settings”,“SSH Keys”页面:

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。

当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。

GitHub远程仓库克隆

在GitHub创建了一个空的仓库GitTest

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

目前,在GitHub上的这个learngit仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。

git remote add origin git@github.com:coderketao/GitTest.git

请千万注意,把上面的michaelliao替换成你自己的GitHub账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表中。
添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:

git push -u origin master

把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。

由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

分支管理

分支的基础使用

每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。

常见的分支管理命令

$ git branch dev  创建分支
$ git checkout dev 切换分支
$ git checkout -b dev 创建分支 加上-b参数表示创建并切换
$ git branch 查看当前分支
$ git merge dev dev分支的工作成果合并到当前分支上
$ git branch -d dev 删除分支

有时合并分支时,Git会提示这是一次Fast-forward,意思是合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

当然,也不是每次合并都能Fast-forwardmaster分支和feature1分支各自都分别有新的提交,变成了这样:

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突

合并后,master分支和feature1分支变成了下图所示:

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

“快进模式”合并,用git log --graph也可以看到分支的合并情况:

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

Bug分支

软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交,并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash

现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:

  • 一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
  • 另一种方式是用git stash pop,恢复的同时把stash内容也删了

Feature分支

软件开发中,总有无穷无尽的新的功能要不断添加进来。
添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
现在,你终于接到了一个新任务:开发代号为Vulcan的新功能,该功能计划用于下一代星际飞船。

git checkout -b feature-vulcan

5分钟后,开发完毕:切回dev,准备合并。
一切顺利的话,feature分支和bug分支是类似的,合并,然后删除。就在此时,接到上级命令,因经费不足,新功能必须取消!
虽然白干了,但是这个包含机密资料的分支还是必须就地销毁:

$ git branch -d feature-vulcan

销毁失败。Git友情提醒,feature-vulcan分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数。

现在我们强行删除:

$ git branch -D feature-vulcan

多人协作

当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin

要查看远程库的信息

$ git remote
git remote -v 显示更详细的信息 可以显示抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

推送分支

送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

git push origin 即将推送的本地分支

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要时刻与远程同步;
  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

抓取分支

多人协作时,大家都会往master和dev分支上推送各自的修改。
默认情况下,git clone只能看到本地的master分支。可以用git branch命令看看,要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是可以使用这个命令创建本地dev分支

$ git checkout -b dev origin/dev

现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

$ git push origin dev

标签管理

发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。

创建标签

版本控制-Git
概述
安装配置
Git基础使用
工作区和暂存区
远程仓库
分支管理
标签管理

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,可以找到历史提交的commit id,然后打上就可以了。

$ git tag v0.9 f52c633

还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:

$ git tag -a v0.1 -m "version 0.1 released" 1094adb

操作标签

如果标签打错了,也可以删除:

$ git tag -d v0.1

因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。

如果要推送某个标签到远程,使用命令git push origin <tagname>:

$ git push origin v1.0
$ git push origin --tags  一次性推送全部尚未推送到远程的本地标签

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:

$ git tag -d v0.9

然后,从远程删除。删除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9

git 获取指定的tag处代码

先 git clone 整个仓库,然后 git checkout tag_name 就可以取得 tag 对应的代码了。

$ git checkout v0.1.0

但是这时候 git 可能会提示你当前处于一个“detached HEAD" 状态,因为 tag 相当于是一个快照,是不能更改它的代码的,在切换回主线时如果没有合并,之前的修改提交基本都会丢失。如果要在 tag 代码的基础上做修改,你需要一个分支:

git checkout -b branch_name tag_name

这样会从 tag 创建一个分支,然后就和普通的 git 操作一样了。