版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。Git是协同开发的最常用工具。
理论部分主要是狂神git视频的笔记
第5节主要记录实习过程遇到的诸多git问题
最常用的命令 没有之一
1 | git checkout -b feature/wuzhikang #新建分支并切换到该分支 |
1 什么是版本控制
版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。
- 实现跨区域多人协同开发
- 追踪和记载一个或者多个文件的历史记录
- 组织和保护你的源代码和文档
- 统计工作量
- 并行开发、提高开发效率
- 跟踪记录整个软件的开发过程
- 减轻开发人员的负担,节省时间,同时降低人为错误
简单说就是用于管理多人协同开发项目的技术。Git是最优秀的分布式版本控制系统,最初Linus Benedic Torvalds开发的目的是为了Linux内核系统的版本控制。
2 Git的安装和基本命令
Git安装
- 下载地址淘宝镜像网站http://npm.taobao.org/mirrors/git-for-windows/
- 我这里下载了一个Git-2.33.0-64-bit.exe的安装包。选择开发软件安装路径,然后无脑安装(查看器可以选择notepad++)。会自动帮你配置环境变量
- 配置账号名和邮箱,任意路径下点开git bush
1 | git config --global user.name "wukang" |
实际上git的配置都保存在本地
- Git\etc\gitconfig # Git 安装目录下的 gitconfig –system 系统级
- C:\Users\Administrator\ .gitconfig #只适用于当前登录用户的配置 –global 全局
常用的Linux命令
1 | cd #切换目录 |
3 Git基本理论(重要)
1 Git的工作区域
Git本地有三个工作区域:工作目录(Working Directory)、暂存区(Stage/Index)、资源库(Repository或Git Directory)。远处有一个远程git仓库(Remote Directory)。状态变化最主要的几个操作分别是add、commit、push和pull、fetch。

- Remote:远程仓库,就是git上面的所有已经上传过的代码,所有协同开发人员都可以随时获取(即pull或fetch)
- Repository:资源库(我更喜欢叫本地库),就是你本地这个主机修改过的代码经过提交后的版本(未提交的不算)。也就是idea右下角点开分支目录,这些分支都是你本地的版本
- Working Directory:工作目录,就是本地这个主机的未提交的代码的版本(就是我们工作的地方!!正在码代码的这个版本)
- Index:暂存区,新修改或者新建的代码通过add后放入暂存区。但不重要(但在我都是add&commit一起执行)
2 文件的几种状态
git管理的文件有几种状态:未跟踪(Untracked),已修改(modified),已暂存(staged),已提交(committed)

Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过git add 状态变为Staged.
Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为Modified. 如果使用git rm移出版本库, 则成为Untracked文件
Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处, 通过git add可进入暂存staged状态, 使用git checkout 则丢弃修改过, 返回到unmodify状态, 这个git checkout即从库中取出文件, 覆盖当前修改 !
Staged: 暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify状态. 执行git reset HEAD filename取消暂存, 文件状态为Modified
查看文件的状态:
1 | 查看指定文件状态 |
忽略文件:有些时候我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等。需要在主目录下建立”.gitignore”文件。此文件夹内适用如下规则
1 | 为注释 |
4 Git的使用
1 Git的基本使用
- 1 先登录码云,然后为本地机器添加一个SSH公钥。码云上创建一个空项目作为远程仓库。

- 2 将该项目克隆到本地,比如C:\IdeaProjects文件夹下。记得选择SSH链接的形式克隆
- 3 idea新建spring项目比如hello-01,将克隆
gitstudy
下所有文件复制进hello-01项目目录 - 4 观察学习idea中git的命令和按钮。
- 右上角有update、commit和push
- 左下角有git图标,可以查看版本信息
- Terminal标签窗口可以执行git命令
- 任意文件右键,有git命令选择
1 | 命令行常见操作 |
2 Git常用命令

git命令之branch
1 | 列出所有本地分支 |
git命令之add、commit、restore
1 | 添加指定文件到暂存区 |
git命令之checkout、reset
1 | 恢复暂存区的指定文件到工作区 |
git命令之查看信息
1 | 显示有变更的文件 |
3 Commit message格式
Git的核心是commit,所以写好commit信息极其重要。
方式一:提交信息只能有一行
git commit [fileName] -m “commit message”
方式二:使用vim编辑方式,提交现信息可以多行(推荐这种,可以按规范来)
git commit [fileName]
Commit message规范如下:Header 第一行是必需的,Body 和 Footer 可以省略。不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。
1 | <type>(<scope>): <subject> |
(1)type
type
用于说明 commit 的类别,只允许使用下面7个标识。
- feat:新功能(feature)
- fix:修补bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- test:增加测试
- chore:构建过程或辅助工具的变动
如果type
为feat
和fix
,则该 commit 将肯定出现在 Change log 之中。其他情况(docs
、chore
、style
、refactor
、test
)由你决定,要不要放入 Change log,建议是不要。
(2)scope
scope
用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
(3)subject
subject
是 commit 目的的简短描述,不超过50个字符。
- 以动词开头,使用第一人称现在时
- 第一个字母小写,结尾不加句号(
.
)
(4)body
Body 部分是对本次 commit 的详细描述,可以分成多行。
有两个注意点。
使用第一人称现在时
应该说明代码变动的动机,以及与以前行为的对比。
(5)Footer
Footer 部分只用于两种情况:不兼容变动、关闭 Issue
- 如果当前代码与上一个版本不兼容,则 Footer 部分以
BREAKING CHANGE
开头,后面是对变动的描述、以及变动理由和迁移方法。 - 如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。
Revert撤销commit
还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:
开头,后面跟着被撤销 Commit 的 Header。
1 | revert: feat(pencil): add 'graphiteWidth' option |
Body部分的格式是固定的,必须写成This reverts commit <hash>.
,其中的hash
是被撤销 commit 的 SHA 标识符。
5 Git开发流程
5.1 Git Flow
参考了博文,关于分支的介绍和主要的工作流程都介绍的很好
Git官方给的流程:

由上图可知,GitFlow的常用分支有:master、develop、feature、release、hotfix。分支介绍如下:
master
- 主分支 , 产品的功能全部实现后 , 最终在master分支对外发布
- 该分支为只读唯一分支 , 只能从其他分支(release/hotfix)合并 , 不能在此分支修改
- 另外所有在master分支的推送应该打标签做记录,方便追溯
- 例如release合并到master , 或hotfix合并到master
develop
- 主开发分支 , 基于master分支克隆
- 包含所有要发布到下一个release的代码
- 该分支为只读唯一分支 , 只能从其他分支合并
- feature功能分支完成 , 合并到develop(不推送)
- develop拉取release分支 , 提测
- release/hotfix 分支上线完毕 , 合并到develop并推送
feature
- 功能开发分支 , 基于develop分支克隆 , 主要用于新需求新功能的开发
- 功能开发完毕后合到develop分支(未正式上线之前不推送到远程中央仓库!!!)
- feature分支可同时存在多个 , 用于团队中多个功能同时开发 , 属于临时分支 , 功能完成后可选删除
release
- 测试分支 , 基于feature分支合并到develop之后 , 从develop分支克隆
- 主要用于提交给测试人员进行功能测试 , 测试过程中发现的BUG在本分支进行修复 , 修复完成上线后合并到develop/master分支并推送(完成功能) , 打Tag
- 属于临时分支 , 功能上线后可选删除
hotfix
- 补丁分支 , 基于master分支克隆 , 主要用于对线上的版本进行BUG修复
- 修复完毕后合并到develop/master分支并推送 , 打Tag
- 属于临时分支 , 补丁修复上线后可选删除
- 所有hotfix分支的修改会进入到下一个release
公司的项目流程:
作为开发人员,我们公司的大致流程就是:
- 1 master分支poll更新拉取最新的代码,新建分支fix或feature
- 2 本地开发并调试,完成后commit、pull、push,push完成后请求合并test分支,更新测试环境并进行测试
- 3 将分支代码merge合并到master分支,并上线
5.2 场景最佳实践
此部分参考博客
先介绍一下最常用的3个Git指令!!
1 | git status # 充分、及时了解当前的 git 状态可以避免一些误操作 |
我通常在 写完代码后、做任何git操作前、做复杂的git操作的途中(比如 rebase、merge遇到冲突时)和 做完任何git操作后,都会使用 git status去查看当前的状态 —— 哪些文件还在工作区(还没 git add)、哪些文件还在暂存区(还没 git commit)或者 rebase、merge 的进展等。
1 | git log --oneline # 打印出最近的commit messages,每条message只占一行,使界面更干净、美观。 |
我通常会在 切换分支的前后,拉取线上分支的前后 使用git log –oneline,它能帮我根据 commit message 确认我当前的分支以及当前分支是否是最新的 (前提是每次 commit 时一定要认真填写 commit message)
1 | git commit # commit 是git 设计中的核心 |
在日常开发工作中,我通常会在 写完一部分代码后,准备暂时休息前或确认某个功能/bug处理完成后 使用 git add 和 git commit 来保存代码到本地仓库。
记住:只要正确地 commit了,代码就几乎永远不会丢失。
场景一:新任务(开发新特性 / 修bug)
一般的业务需求都是这个场景
1 git status 查看当前项目的状态,如果有未保存的修改,就保存和提交修改
1
2
3git status
git add .
git commit [fileName] #使用vim方式提交message2 切换到开发主分支develop,并确认主分支是最新的版本
1
2git checkout develop
git pull origin develop3 创建新的开发分支feature/newFunctionWK,并在该分支上进行开发
1
git checkout -b feature/newFunctionWK
4 开发过程如果当前任务比较复杂,代码量比较多,通常会多次使用 git add . 和 git commit
1
2
3
4git status
git add .
git commit [fileName] #使用vim方式提交message
message需要按照规范写清楚,第一行为标题,空格行后写详细的备注5 代码开发结束,整理合并 commit message,代码步骤如下:
前面说过多次 commit 是为了防止意外丢失代码,但是在推到远端之前,最好把一次开发的 commit 合并成一个,避免污染远端的 git commit message,
- 首先git status确保所有代码都提交了
- 然后git logl查看当前分支下最近所有的commit,比如最近我有两个关于fix:规范代码的提交
- 执行git rebase -i HEAD命令
1
2
3
4
5
6
7
8
9
10git status
git log --oneline #oneline可加可不加
git rebase -i HEAD~2 #合并本地最近两次的commit
git rebase -i [startpoint] [endpoint] #也可以指定两个commit的点如下
git rebase -i 23cc2cabb387f4ba43b7b0daa4d0aa40847a7cc6 22e4245da8330644f865c5eb582fc857ed109685
正常应该是在控制台vim编辑修改,但我这个会弹窗Nodepad++,如下图,要更新的代码如下
git rebase --continue #再执行这个
然后继续编辑合并两条message,原图如下,要修改部分如下代码
git log #查看日志,发现两条commit已经合并为1条1
2pick 22e4245 fix:代码规范
squash 23cc2ca fix:代码规范1
2
3
4fix:代码规范&更新时可以指定初稿状态
更优雅的判断新建文章和更新文章的判断
添加更新文章时指定初稿状态仍然可以作为初稿的逻辑判断6 push本分支到远端仓库,然后上git上请求合并
- 这里为了避免冲突,一般还有其他的实施方案:
- 1 先切换到develop分支,poll拉最新的代码
- 2 然后在本地将feature/newFunctionWK分支合并到本地的develop分支,如果出现冲突就解决冲突
- 3 将本地的develop分支push到远端仓库(此时远端develop就是最新的已经合并的代码了)
1
git push origin develop
场景二:开发进行一半,需要远端主分支的最新代码
背景:有些时候,你在本地开发某个功能,代码写到一半,某个同事将某些重要代码合进了**远端的主分支(如 develop 分支)**里。这些重要代码可能是可以极大提升本地开发效率,可能是加入了某些规范检查或者是跟你当前开发相关的代码 —— 总之需要你将那部分代码融入你当前的本地开发环境里。
三个步骤如下:
- git status 查看当前项目的状态,如果有未保存的修改,就git add . 和 git commit 保存下来
- git pull –rebase origin develop 使用这个指令将远端的主分支develop以 rebase 的形式 “合进”当前分支
- 执行这条命令之前,需要保证没有文件处于modified状态
- 如果有文件处于modified而这个修改又不应该commit,比如application.yml,就先移除这个修改:git restore
- git log –oneline 查看当前分支下的 commit message 是否符合预期
为什么用 –rebase 呢?
因为这么做,可以让git历史最干净、整洁 —— 所有本地开发的 commit 都会出现在远端主分支里的 commit 之后;并且可以避免额外引入一次 merge 的 commit(避免本地多执行一次merge)
场景三:希望把某个分支中的 commit 对应的代码复制到当前分支
有时我会创建一些实验性的分支,写一些实验性的代码,如果代码不可行,我可以直接废弃掉这个分支,返回原本的开发分支中;如果这些实验性代码可行,我会返回开发分支中,然后把实验性分支的那部分代码“复制”过来
具体操作如下:
git status 查看当前项目的状态,如果有未保存的修改,就git add . 和 git commit 保存下来
假设我们需要的是 feature/newFunctionWK 这个分支的某个 commit ,使用 git log –oneline feature/newFunctionWK 查看最近这个分支的所有 commit 记录
使用 git cherry-pick 50dd556 将这个 commit 对应的代码复制到当前分支
1 | git status |
5.3 使用git的原则和可能的问题
1 check分支之前需要先下拉代码
2 如何解决merge代码冲突的问题
3 为什么出现merge代码不完整的问题
使用git的原则(习惯)
- 永远记得 git st 和 git logl 来确认当前分支的状态
- 宁愿临时制造一些无用的 commit 来保证代码不会丢失,也不要轻信自己的记忆力
- 谨慎(最好能避免)使用 git stash ,极易造成代码丢失
- 认真对待、编写每次的 commit message —— 它们能在关键时刻救你一命
- 必要的时候可以创建一些临时的分支写实验性的代码,而不是依赖 git reset 撤销 commit —— 大多数人在 git reset 的时候容易犯错误
出现冲突如何解决(git merge conflict)
参考博文https://blog.csdn.net/u012150179/article/details/14047183
一个解决的方法就是push之前先poll