Git 使用教程
什么是 Git
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git 与常用的版本控制工具 CVS,Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。
Git 与 SVN 区别
Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。
如果你是一个具有使用 SVN 背景的人,你需要做一定的思想转换,来适应 Git 提供的一些概念和特征。
Git 与 SVN 区别点:
- Git 是分布式的,SVN 是集中式的:这是 Git 和其它非分布式的版本控制系统,例如 SVN,CVS 等,最核心的区别。
- Git 把内容按元数据方式存储,而 SVN 是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等的文件夹里。
- Git 分支和 SVN 的分支不同:分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
- Git 没有一个全局的版本号,而 SVN 有:目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
- Git 的内容完整性要优于 SVN:Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
Git 的安装和配置
Git 下载地址(Linux/Unix,Mac,Windows 等相关平台)
1 | ## 配置所有 Git 仓库的 用户名 和 email |
Git 工作流程
以上包括一些简单而常用的命令,但是先不关心这些,先来了解下面这 4 个专有名词。
- Workspace:工作区
- Index/Stage:暂存区
- Repository:仓库区(或本地仓库)
- Remote:远程仓库
工作区
程序员进行开发改动的地方,是你当前看到的,也是最新的。
平常我们开发就是拷贝远程仓库中的一个分支,基于该分支进行开发。在开发过程中就是对工作区的操作。
暂存区
.git 目录下的 index 文件,暂存区会记录 git add
添加文件的相关信息(文件名、大小、timestamp...),不保存文件实体,通过 id 指向每个文件实体。可以使用 git status
查看暂存区的状态。暂存区标记了你当前工作区中,哪些内容是被 git 管理的。
当你完成某个需求或功能后需要提交到远程仓库,那么第一步就是通过 git add
先提交到暂存区,被 git 管理。
本地仓库
保存了对象被提交过的各个版本,比起工作区和暂存区的内容,它要更旧一些。
git commit
后同步 index 的目录树到本地仓库,方便从下一步通过 git push
同步本地仓库与远程仓库的同步。
远程仓库
远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库修改,因此它可能与本地仓库同步,也可能不同步,但是它的内容是最旧的。
小结
- 任何对象都是在工作区中诞生和被修改;
- 任何修改都是从进入
index
区才开始被版本控制; - 只有把修改提交到本地仓库,该修改才能在仓库中留下痕迹;
- 与协作者分享本地的修改,可以把它们
push
到远程仓库来共享。
下面这幅图更加直接阐述了四个区域之间的关系,可能有些命令不太清楚,下面将会详细介绍。
Git 常用命令
版本与配置
1 | git 查看 git 的相关命令(git --help) |
初始化本地仓库
Git 使用 git init
命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init
是使用 Git 的第一个命令。
在执行完成 git init
命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。
1 | git init 创建本地仓库 |
添加文件到仓库
1 | git add <file> 如: git add readme.txt |
查看仓库目前状态
1 | git status 查看项目是否有修改、添加、未追踪的文件等 |
查看修改
1 | git diff 查看工作区(work dict)和暂存区(stage)的区别 |
查看提交日志
1 | git log |
版本回退
1 | git reset --hard HEAD^ |
查看命令历史
1 | git reflog |
撤销修改
丢弃工作区(Working Directory)的修改
1 | git restore <file> (建议使用,如: git restore readme.txt) |
丢弃暂存区(stage/index)的修改
1 | 第一步: 把暂存区的修改撤销掉(unstage),重新放回工作区 |
小结
当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git restore <file>
。当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令
git restore --staged <file>
,就回到了场景 1,第二步按场景 1 操作。已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
删除文件
1 | git rm <file> |
远程仓库
创建 SSH Key
1
2ssh-keygen -t rsa -C "youremail@example.com"
邮件地址换成你自己的邮件地址,然后一直回车,使用默认值即可,无需设置密码。在用户主目录下,看看有没有 .ssh 目录,如果有,再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件,如果已经有了,可直接跳到下一步。如果没有,创建 SSH Key
如果一切顺利的话,可以在用户主目录里找到 .ssh 目录,里面有 id_rsa 和 id_rsa.pub 两个文件,这两个就是 SSH Key 的秘钥对,id_rsa 是私钥,不能泄露出去,id_rsa.pub 是公钥,可以放心地告诉任何人。
登录 GitHub,在 Settings 中找到 SSH 设置项中添加新的 SSH Key,设置任意 title,在 Key 文本框里粘贴 id_rsa.pub 文件的内容
关联远程仓库(先有本地仓库)
1
2git remote add origin git@github.com:username/repo.git
后面的地址换成自己的 GitHub 仓库地址推送到远程仓库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16git remote 查看远程库信息
git remote -v 查看远程库详细信息
git remote rm origin 删除已关联的远程库 origin
git push -u origin master #第一次推送
git push origin master 推送本地 master 分支到远程库
git push origin dev 推送本地 dev 分支到远程库
除了第一次推送,不需要添加 -u 参数
一个本地库关联多个远程库,例如同时关联 GitHub 和 Gitee:
1. 先关联 GitHub 的远程库:(注意:远程库的名称叫 github,不叫 origin)
git remote add github git@github.com:username/repo.git
2. 再关联 Gitee 的远程库:(注意:远程库的名称叫 gitee,不叫 origin)
git remote add gitee git@gitee.com:username/repo.git
3. 推送到远程库
git push github master
git push gitee master加上了
-u
参数,Git 不但会把本地的 master 分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来从远程仓库克隆(先有远程库)
1
2git clone git@github.com:username/repo.git
GitHub 支持多种协议,上面是 ssh 协议,还有 https 协议
Git 分支
1 | git branch 查看分支列表及当前分支 |
1 | git log --graph 查看分支合并图 |
Git 标签
1 | 切换到对应的分支 branch 上,查看或者操作对应的标签 tag |
Commit message 格式
Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。但是,一般来说,Commit message 应该清晰明了,说明本次提交的目的。
每次提交,Commit message 都包括三个部分:header,body 和 footer。
1 | <type>(<scope>): <subject> |
其中,header 是必需的,body 和 footer 可以省略。
不管是哪一个部分,任何一行都不得超过 72 个字符(或 100 个字符)。这是为了避免自动换行影响美观。
Header
Header 部分只有一行,包括三个字段:type
(必需)、scope
(可选)和subject
(必需)。
type
用于说明 commit 的类别,只允许使用下面 7 个标识。
feat
:新功能(feature)fix
:修补 bugdocs
:文档(documentation)style
:格式(不影响代码运行的变动)refactor
:重构(即不是新增功能,也不是修改 bug 的代码变动)test
:增加测试chore
:构建过程或辅助工具的变动
如果 type 为 feat
和 fix
,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入 Change log,建议是不要。
scope
scope 用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
例如在 Angular,可以是 $location
, $browser
, $compile
, $rootScope
, ngHref
, ngClick
, ngView
等。
如果你的修改影响了不止一个 scope
,你可以使用 *
代替。
subject
subject 是 commit 目的的简短描述,不超过 50 个字符。
其他注意事项:
- 以动词开头,使用第一人称现在时,比如 change,而不是 changed 或 changes
- 第一个字母小写
- 结尾不加句号(.)
Body
Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。
1 | More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. |
有三个注意点:
- 使用第一人称现在时,比如使用 change 而不是 changed 或 changes。
- 永远别忘了第 2 行是空行
- 应该说明代码变动的动机,以及与以前行为的对比。
Footer
Footer 部分只用于以下两种情况:
不兼容变动
如果当前代码与上一个版本不兼容,则 Footer 部分以 BREAKING CHANGE
开头,后面是对变动的描述、以及变动理由和迁移方法。
1 | BREAKING CHANGE: isolate scope bindings definition has changed. |
关闭 Issue
如果当前 commit 针对某个 issue,那么可以在 Footer 部分关闭这个 issue 。
1 | Closes #234 |
Revert
还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以 revert:
开头,后面跟着被撤销 Commit 的 Header。
1 | revert: feat(pencil): add 'graphiteWidth' option |
Body 部分的格式是固定的,必须写成 This reverts commit <hash>.
,其中的 hash 是被撤销 commit 的 SHA 标识符。
如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的 Reverts 小标题下面。