git 分布式版本管理工具学习笔记
声明:本文作为自学廖雪峰老师的 Git 教程后,筛选摘抄出工作中常用的操作命令和技巧,补充了部分教程中没有提到的知识点,编辑而成,仅供学习参考使用。完整教程,欢迎访问廖雪峰老师的官方教程,教程地址:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
Git 诞生
BitMover公司发现Linux社区有人试图破解BitKeeper,BitMover公司将收回Linux社区的免费使用权。
Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了。
Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。
配置
Git 的设置文件为 .gitconfig ,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)。
git config --list //显示当前 Git 配置
git config -e [--global] //编辑 Git 配置,vim 编辑,中括号[--global]表示可加可不加,加了的话表示全局
git config [--global] user.name "[name]" //设置提交代码的用户信息
git config [--global] user.email "[email address]"
创建版本仓库
- 版本库又名仓库,英文名 repository。选择一个合适的地方,创建一个空目录。
- 通过 git init 命令把这个目录变成 Git 可以管理的仓库(初始化仓库)。
把文件添加到版本库
- 使用命令 git add 告诉git,把文件 添加 到仓库。
- 使用命令 git commit -m “XXX” 告诉git,把文件 提交 到仓库,-m 后面跟的是本次提交的注释说明,注释说明不要用中文。因为 commit 一次可以提交很多文件,所以可以使用多次 add 不同的文件。
常用commit提交注释规范
- 用一空行分隔标题与正文。
- 标题使用大写字母。
- 标题不超过50个字符。
- 标题使用祈使语气。
- 标题不要使用句号结尾。
- 正文解释是什么和为什么,而不是如何做。
常用关键字:
- Add 新增
- Mod 修改
- Del 删除
- Fixed 修复
查看仓库的当前状态
git status 命令可以查看仓库当前的状态,随时掌握工作区的状态,谁被修改过了,还没有提交等等。
修改文件内容,未提交到暂存区,此时输入 git status 后:
changes not staged for commit //改变 没有 进行 提交
(上面的命令告诉我们,readme.txt 被修改过了,但是还没有准备提交(暂存))
将修改后的文件通过 git add 命令提交到暂存区后,此时输入git status 后:
(上面的命令告诉我们,将要被提交的修改包括 readme.txt)
将文件通过 git commit 命令提交到仓库后 ,此时输入git status后:
(上述命令告诉我们当前没有需要提交的修改,而且工作目录是干净的)
git diff 命令可以看到工作区和暂存区的区别;git diff head – readme.txt 命令可以查看工作区和版本库里面的区别。
查看提交的历史记录
git log 命令可以查看所有提交的比较详细的历史纪录。
如果不想要太详细,可以使用 git log --pretty=oneline
时光穿梭机
6280d3……e547 是 commit id (版本号)。
在 git 中,用 HEAD 表示当前版本,上一个版本就是 HEAD^ ,上上个版本就是 HEAD^^ ,往前一百个版本为 HEAD~100。
回到上一个版本为 git reset --hard “HEAD^” (实测不区分大小写)
回到上上个版本为 git reset --hard “head^^” (实测不区分大小写)
假如知道版本号,回到某一个版本为 git reset --hard 126378(版本号,写前几位就行)
假如因为一些原因,关闭了命令行窗口,暂时无法知道某一次代码的版本号,可以使用命令 git reflog ,这个命令记录了你之前操作的每一次命令,可以查看版本号,知道了版本号就可以随意回退版本了。
工作区和暂存区
Git 和其他版本控制系统如 SVN 的一个不同之处就是有暂存区的概念。
工作区(Working Directory):就是你电脑里面能看到的目录。
版本库(Repository):工作区有一个隐藏目录 .git ,这个不算工作区,而是 Git 的版本库。Git 的版本库里存了很多东西,其中最重要的是称为 stage(或者叫做 index)的暂存区,还有 Git为我们自动创建的第一个分支 master,以及指向 master 的一个指针叫做 HEAD。
把文件往 Git 版本库里面添加的时候,是分两步执行的:
- 用 git add 把文件添加进去,实际上就是把文件添加到暂存区;
- 用 git commit 提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建 Git 版本库时候,Git 自动为我们创建了唯一一个 master 分支,所以,现在, git commit 就是往 master 分支上提交更改。
git commit 只负责把暂存区的修改提交到仓库,不管工作区的内容。
撤销修改
git checkout – file 可以丢弃工作区的修改:
git cheout -- readme.txt
命令 git checkout – readme.txt 意思就是,把 readme.txt 文件在工作区的修改全部撤销,这里有两种情况:
- 一种是 readme.txt 自修改后还 没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态。
- 一种是 readme.txt 已经 添加到暂存区后,又做了修改,现在,撤销修改就回到添加到暂存区后的状态。
git checkout – file 命令中的 – 很重要,没有 – ,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到 git checkout 命令。
假如修改的内容已经被 git add 到了暂存区,可以使用 git reset HEAD file 可以把暂存区的修改撤销掉,重新放回工作区。
git reset 命令可以回退版本,也可以把暂存区的修改退回到工作区。当我们用 HEAD 时,表示最新的版本。
再用 git status 查看一下,发现暂存区是干净的,工作区又修改。
然后再使用 git checkout – file ,撤销掉工作区的操作。
假如修改的内容从暂存区 git commit 到了版本库,回退的话可以参照时光穿梭机,会回退到上一版本,git reset --hard “HEAD^” 。(不过这是有条件的,就是此时本地的代码没有被推送到远程仓库)
远程仓库
Git 是分布式版本管理系统,同一个 Git 仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以克隆这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
实际情况是这样的,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。GitHub 就类似于“服务器”的角色,托管代码。
查看远程仓库
查看远程分支:git branch -a
注意:这条命令并没有每一次都从远程更新仓库信息,这样子做是为了效率,我们可以手动更新一下远程分支:
git fetch origin //更新远程仓库
git branch -a //查看远程分支
拉取远程分支、并创建本地分支
方法一:git checkout -b 本地分支XXX origin/远程分支XXX
git checkout -b gps origin/gps //将远程的分支 gps 拉取到本地 gps 分支,并且切换到本地 gps 分支
使用该方式会在本地新建分支x,并自动切换到该本地分支x。
方法二:git fetch origin 远程分支XXX:本地分支XXX
git fetch origin gps:gps //将远程的分支 gps 拉取到本地 gps 分支,但是不会自动切换到本地的 gps 分支
使用该方式会在本地新建分支x,但是不会自动切换到该本地分支x,需要手动checkout。
添加远程仓库
远程仓库建立的方法略过,假定现在已经有了线上的远程仓库地址。
在本地的 learngit 仓库下运行命令:
git remote add origin git@github.com:albert-guo/learngit.git
上面的 albert-guo 替换成自己的 GitHub 帐户名,否则你在本地关联的就不一定是你的仓库,关联错了也没关系,但是以后是无法推上去的,因为你的 SSH Key 公钥不在这个帐户的列表中。
添加后,远程仓库的名字就是 origin ,这是 Git 默认的叫法,也可以改成别的,但是 origin 这个名字一看就知道是远程仓库。
把本地库的所有内容推送到远程仓库上:
git push -u origin master
把本地库的内容推送到远程,用 git push 命令,实际上是把当前分支 master 推送到远程。
由于远程仓库是空的,我们第一次推送的 master 分支时候,加上了 -u 参数,git 不但会把本地的 master 分支推送到远程新的 master 分支,还会把本地的 master分支和远程的 master 分支关联起来,在以后的推送或者拉取的时候可以简化命令。
从现在起,只要本地做了提交,就可以通过命令:
git push origin master
把本地 master 分支的最新修改推送到 GitHub,现在就拥有了真正的分布式版本库。
分布式版本系统最大的好处之一就是在本地工作的时候不需要考虑远程仓库的存在,也就是没有联网也可以工作,而 svn 在没有联网的时候是拒绝干活的,当有网络的时候再把本地提交推送一下就完成了同步,比较方便。
从远程仓库克隆
要克隆一个仓库,首先必须知道仓库地址,然后使用 git clone 命令克隆。
git clone git@github.com:albert-guo/learngit.git
Git 支持多种协议,包括 https ,但是通过 ssh 支持的原生 git 协议最快。默认的 git:// 使用 ssh,但也可以使用 https 等其他协议。使用 https 除了速度慢以外,还有个最大的麻烦就是每次推送必须输入口令,只是在某些只开放 http 端口的公司内部就无法使用 ssh 协议而只能用 https。
创建与合并分支
首先创建 dev 分支,然后切换到 dev 分支。
git checkout -b dev
git checkout 命令上加 -b 参数表示创建并且切换,相当于下面两条命令:
- git branch dev 创建一个叫 dev 的分支。
- git checkout dev 切换到 dev 分支。
git branch dev //创建一个名为 dev 的分支
git checkout dev //切换到 dev 分支
查看当前所在分支命令 git branch 。该命令会列出所有分支,并且在当前的分支前面加 * 。
在 dev 分支上提交几次代码后,尝试将 dev 分支和 master 分支合并。
git merge 命令用于合并指定分支到当前分支。比如当前在 master 分支上,然后通过命令 git merge dev 可将 dev 分支合并到当前的 master 分支。
git merge --abort 表示撤销刚才的合并。
git merge dev //将 dev 分支合并到当前的 merge 分支上
git merge --abort //将刚才合并操作撤销
注意上面的 Fast-forward 信息,表示这次合并是“快进模式”,也就是直接把 master 指向 dev 的当前提交,所以合并速度非常快。当然也不是每一次合并都能 Fast-forward ,后续还会有其他方式合并。
合并之后,可以放心删除 dev 分支。删除命令 git branch -d dev 。
小结:
查看分支:git branch
创建分支:git branch
切换分支:git checkout
创建+切换分支:git checkout -b
合并某分支到当前分支:git merge
删除分支:git branch -d
强制删除分支(没有合并):git branch -D
解决冲突
假如两个分支编辑的内容区块相同,各分支都有了自己新的提交。这种情况下,git 无法执行“快速合并”,只能试图把各自的修改合并起来,但是这种合并就会有冲突。
git 告诉我们,合并失败,必须手动解决冲突后再提交。
此时,用命令 git diff 可以查看冲突的地方。也可以直接在冲突的文件里,找到相关的冲突的位置。
git 会用 <<<<<<< , ======= , >>>>>>> 标记出不同分支的内容,然后在文件中解决了冲突后保存。
用带参数的 git log --graph --pretty=oneline ,简短显示分支的合并情况。
git log --graph 命令可以看到详细分支合并图,git log --graph --pretty=oneline 命令可以简单看到分支合并情况。
解决完冲突后,可将 dev 分支删除,命令为 git branch -d dev。
git branch -d dev //删除 dev 分支
禁用快速合并
通常情况下,合并分支时,如果可能,git 会用 Fast Forward 模式,但是这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用 Fast Forward 模式,git 就会在 merge 时生成一个新的 commit ,这样,从分支历史上就可以看出分支信息。
比如我们从现在在 master 分支上,要把 dev 分支合并到当前的 master 分支。
可以用 git merge --no-ff -m "强制合并,禁用快速合并” dev。
因为本次合并要创建一个 commit ,所以加了一个 -m 参数,把 commit 描述进去。
gitignore
要养成在项目开始就创建 .gitignore 文件的习惯,否则一旦 push,处理起来会非常麻烦。
有时候在项目开发过程中,突然心血来潮想把某些目录或文件加入忽略规则,按照上述方法定义后发现并未生效,原因是 .gitignore 只能忽略那些原来没有被 track 的文件,如果某些文件已经被纳入了版本管理中,则修改 .gitignore 是无效的。解决方法就是先把本地缓存删除(改变成未 track 状态),然后再提交:
git rm -r --cached .
git add .
git commit -m 'update .gitignore'