Git
分布式的版本控制系统。
笔记来源于廖雪峰老师Git教程:
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
Git原理
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。
名词解释
工作区
就是你在你的电脑里能看到的目录,比如创建的learngit文件夹。
版本库
工作区中有一个隐藏目录
.git
,这个不算工作区,而是Git的版本库。版本库中分为
stage
(暂存区),还有Git为我们创建的第一个分支master
,以及指向master
的一个指针HEAD.分支
创建一个属于自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
Git为我们创建第一个分支
master
,为主分支。HEAD指针指向当前分支。
创建版本库
版本库又名仓库,可以简单理解为一个仓库,这个目录里的所有文件可以被Git管理起来。
创建版本库:
$ mkdir learngit // 创建一个空目录
$ cd learngit
$ git init // 通过git init命令把这个目录变成Git可以管理的仓库
此时当前目录下多了一个.git
的隐藏目录,是Git用来跟踪管理版本库的(没事不要手动修改)。
添加文件到Git仓库,分两步:
$ git add <file> // 添加工作区的文件至暂存区
$ git commit -m "log" // 把暂存区的所有内容提交到当前分支
时光机穿梭
一些常用命令:
$ git status // 显示仓库当前的状态
$ git diff <file> // 如果git status告诉你文件有修改过,那么使用该命令显示file具体修改的内容
$ git log // 显示从最近到最远的提交日志
$ git log --pretty=oneline // 让信息整齐显示的命令
// 命令显示格式: commit_id log
$ git reflog // 记录你的每一次命令
$ cat readme.txt // 查看版本内容
版本回退
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD
表示当前版本,也就是最新的提交。上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100
。
当回退版本时,Git仅仅是把HEAD
指向改变,然后顺便把工作区的文件更新了。
则版本回退命令为:
$ git reset --hard HEAD^ // 回退到上一个版本
$ git reset --hard HEAD~100 // 回退到上第100个版本
$ git reset --hard 3628164 // 回退到指定id的commit
要重返未来,用git reflog
查看命令历史,以便确定要回到未来的哪个版本。
再使用版本回退命令。
撤销修改
丢弃工作区的修改:
$ git checkout -- file
有两种情况:
一种是文件自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态;
一种是文件已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
把暂存区的修改撤销掉:
$ git reset HEAD file
撤销情景
当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时
$ git checkout -- file
当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改
$ git reset HEAD file // 先把暂存区的修改撤销掉 $ git checkout -- file // 再丢弃工作区的修改
已经提交了不合适的修改到版本库时,想要撤销本次提交(前提是没有推送到远程库)
$ git reset --hard HEAD^ // 版本回退
删除文件
在工作区删除文件后,工作区和版本库不一致。
此时有两种情况:
确定要从版本库删除文件
$ git rm file $ git commit -m "log"
误删,要恢复工作区文件
$ git checkout -- file // git checkout 是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
远程仓库
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。
最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
先在GitHub上创建远程仓库,此时在GitHub上的这个仓库是空的。现在有两个选择:
可以把一个已有的本地仓库与之关联(先有本地库,后有远程库)
可以从这个仓库克隆出新的仓库(先有远程库,后有本地库)
然后,把本地仓库的内容推送到GitHub仓库。
添加远程库
先有本地库,后有远程库的时候,如何关联远程库。
现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。
在本地的仓库下运行命令:
$ git remote add origin git@server-name:path/repo-name.git
$ git remote add origin git@github.com:YangZhaobin/learngit.git
添加后,远程库的名字就是origin
,这是Git默认的叫法,也可以改成别的。
下一步,就可以把本地库的所有内容推送到远程库上:
$ git push -u origin master
把本地库的内容推送到远程,用git push
命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地作了提交,就可以通过命令把本地master
分支的最新修改推送至GitHub:
$ git push origin master
从远程库克隆
先有远程库,后有本地库的时候,如何关联远程库。
克隆一个仓库,首先必须知道仓库的地址,然后使用git clone
命令克隆:
$ git clone git@github.com:YangZhaobin/learngit.git
分支管理
创建和合并分支
每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。
创建 + 切换分支:
$ git checkout -b <name>
相当于以下两条命令:
$ git branch <name> // 创建分支
$ git checkout <name> // 切换分支
查看当前所有分支:
$ git branch
* dev // 当前分支前面会标一个*号
master
合并某分支到当前分支:
$ git checkout master // 先切换回master分支
$ git merge <name> // 合并指定分支到当前分支
删除指定分支:
$ git branch -d <name>
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
在合并时禁用Fast forward
模式,--no-ff
参数,表示禁用Fast forward
:
$ git merge --no-ff -m "merge with no-ff" dev
合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward
合并就看不出来曾经做过合并。
使用fast forward
合并后为:
不使用fast forward
合并后为:
解决冲突
master
分支和feature
分支各自都分别有新的提交时,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
带参数的git log
也可以看到分支的合并情况(分支合并图):
$ git log --graph --pretty=oneline --abbrev-commit
分支管理策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;干活都在
dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;每个人都在
dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了
BUG分支
由于分支的强大,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
Git提供一个stash
功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
$ git stash
在修复完成之后工作区是干净的,用git stash list
命令看看:
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply
恢复,但是恢复后,stash
内容并不删除,你需要用git stash drop
来删除;
另一种方式是用git stash pop
,恢复的同时把stash
内容也删了。
恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
总结: 当手头工作没有完成时,先把工作现场git stash
一下,然后去修复bug,修复后,再git stash pop
,回到工作现场。
创建分支修复BUG步骤:
首先确定要在哪个分支上修复bug,假定需要在
master
分支上修复,就从master
创建临时分支:$ git checkout master $ git checkout -b issue-101
修复bug然后提交:
$ git add file $ git commit -m "fix bug 101"
修复完成后,切换到
master
分支,并完成合并,最后删除issue-101
分支:$ git checkout master $ git branch -d issue-101
Feature分支
每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>
强行删除。
$ git branch -D <name>
多人协作
查看远程库的信息:
$ git remote
origin
或者,用git remote -v
显示更详细的信息:
$ git remote -v
origin git@github.com:YangZhaobin/learngit.git (fetch)
origin git@github.com:YangZhaobin/learngit.git (push)
如果没有推送权限,就看不到push的地址。
本地新建的分支如果不推送到远程,对其他人就是不可见的。
推送分支
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上。
$ git push origin master
抓取分支
当从远程库clone时,默认情况下只能看到本地的master
分支。
要在dev分支上开发,就必须创建远程origin的dev分支到本地:
$ git checkout -b dev origin/dev
当最新提交和你试图推送的提交有冲突,先用git pull
把最新的提交从origin/dev
抓下来,然后,在本地合并,解决冲突,再推送:
git pull
时,要指定本地dev
分支与远程origin/dev
分支的链接:
$ git branch --set-upstream dev origin/dev
从远程抓取分支,使用git pull
,如果有冲突,要先处理冲突
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。
其实它就是指向某个commit的指针
创建标签
首先,切换到需要打标签的分支上:
$ git checkout master
然后,敲命令
git tag <name>
就可以打一个新标签:$ git tag v1.0 // 默认是给最新的一次commit设定标签 $ git tag v0.9 6224937 // 给特定id的commit设定标签
查看标签信息
$ git show v0.9
创建带有说明的标签
$ git tag -a v0.1 -m "version 0.1 released" 3628164
操作标签
删除标签:
$ git tag -d v0.1
推送某个标签到远程:
$ git push origin v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
$ git tag -d v0.1
然后,从远程删除:
$ git push origin :refs/tags/v0.9