1. 集中式VS分布式
- 集中式:CVS、SVN
速度慢,必须联网,版本库在中央服务器中,使用简单 - 分布式:Git
速度快,无需联网,安全性高,每个人的电脑都有完整的版本库,中央服务器用来交换大家的改动,分支管理强大,使用较复杂
注: GitHub:免费提供Git存储。提供Git仓库托管服务,充当用于交换的免费中央服务器。
远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改
2. 版本回退和前进
- 版本回退/前进(重置HEAD指针)
git reset --hard HEAD~
回退到上一个版本
git reset --hard 91588fb
回退到指定版本
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向某个版本即可。
git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本. - 查看历史操作记录
git reflog
用git reflog查看命令历史,以便确定要回到未来的哪个版本 git log -n 3
查看最新三条提交日志
3.工作区和暂存区
工作区有一个隐藏目录.git
,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage
(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master的一个指针叫HEAD
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
-
第一步是用
git add
把文件添加进去,实际上就是把文件修改添加到暂存区; -
第二步是用
git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master
分支,所以,现在,git commit
就是往master分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
4. 管理修改
Git跟踪并管理的是修改,而非文件。
你会问,什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。
Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
git diff HEAD -- xx.txt
比较xx.txt文件本地和仓库最新的区别。
Git是跟踪修改的,每次修改,如果不用git add到暂存区,那就不会加入到commit中,所以提交的过程是本地工作区->暂存区stage->版本库
4.撤销修改
git checkout -- readme.txt
让这个文件回到最近一次git commit或git add时的状态(git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。)
--
很重要,没有--
,就变成了“切换到另一个分支”的命令git reset HEAD xx.txt
取消暂存区的修改到工作区。
git rese
t命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版
5.分支管理
git checkout -b dev
我们创建dev分支,然后切换到dev分支,等价于git branch dev
和git checkout dev
两条命令git branch
命令查看当前分支git checkout master
切换到master分支git merge dev
命令用于合并指定分支到当前分支,dev分支的最新提交是完全一样的。
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快git merge --no-ff -m "merged bug fix 101" issue-101
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式--no-ff
,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息git branch -d dev
就可以放心地删除dev分支了
我们注意到切换分支使用git checkout ,而前面讲过的撤销修改则是git checkout – filename,同一个命令,有两种作用,确实有点令人迷惑
git log --graph --pretty=oneline -abbrev-commit
看到分支的合并情况,可以看到分支树结构。
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成
。
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。git log --graph
命令可以看到分支合并图,如下图,内容比较丰富。
- 分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
git stash
当前工作现场“储藏”起来,等以后恢复现场后继续工作,之后,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。git stash list
查看保存起来的工作现场- 恢复工作现场:
- 用
git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除 - 用
git stash pop
,恢复的同时把stash内容也删了 git stash apply stash@{0}
有多次stash,选择性恢复
git cherry-pick 4c805e2
复制(合并)一个特定的提交内容到当前分支;一般用于bug修复完成后需要把这个改动合并到其他分支。git remote
orgit remote -v
查看远程库的信息。你从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
git push origin master
把该分支上的所有本地提交推送到远程库git pull
获取最新提交
6.标签
发布一个版本时,我们通常先在版本库中打一个标签(tag)
,这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照
。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动
),所以,创建和删除标签都是瞬间完成的.
tag就是一个让人容易记住的有意义的名字
,它跟某个commit绑在一起
git tag v1.0
默认标签是打在最新提交的commit上的,默认为HEADgit tag v0.9 f52c633
对某次提交打标签git tag
查看标签git show tagName
查看某个标签的信息git tag -a v0.1 -m "version 0.1 released" 1094adb
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字git tag -d v0.1
删除标签git push origin <tagname>
推送某个标签到远程git push origin --tags
一次性推送全部尚未推送到远程的本地标签- 如果标签已经推送到远程,要删除远程标签就麻烦一点
先从本地删除:git tag -d v0.9
然后,从远程删除。删除命令也是push,但是格式如下:git push origin :refs/tags/v0.9
7.忽略特殊文件
有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status
都会显示Untracked files ...
,有强迫症的童鞋心里肯定不爽。
好在Git考虑到了大家的感受,这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore
文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
- 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。