Git已经成为我们日常接触最多的工具,借助日常Git使用经验积累和Google搜索想必大家对它了如指掌了吧,然而事实真的是如此吗?
为什么会出现这种现象呢?网上大多数资料是操作过程以及如何使用的范例,极少会深入分析其原理过程。这里我就跟大家图解分析一下几个常用命令的原理,并且通过对比更容易的理解什么样的场景对应什么命令。
Merge vs Rebase and 合并多个commit
Git的版本回退:revert vs reset
Comparing Workflow
附录
I. 关于GIT的一些经验
II. GIT的版本控制
III. 常见错误
1Merge vs Rebase and 合并多个commit
先来说下Merge和Rebase这两条最最常用的命令吧;
我们合并分支经常会用到merge,其实rebase能达到殊途同归的作用;
话不多说,这里直接上图分析一下两者有何不同。
场景:
Featrue是我们创建的一个分支开发功能,现在另外一个人在master上添加了两个提交;
为了将master上新增的这两个提交并入Feature分支,我们可以选择merge or rebase;
merge
git checkout feature
git mergemaster
另外也可以用一条命令表示
git merge master feature
Feature所在点就是新的合并提交;
每次merge合并更改后feature会产生一个合并提交历史,如果merge很多整个分支的提交历史看起来不怎么清晰干净,不容易理解。
rebase
git checkout feature
git rebase master
rebase执行的操作相当于先回到Fork之前的共同commits点(也就是白色共同的区域),再根据featrue之后提交绿色点的commits,在master分支的最后提交添加绿色点的commits,生成新的Feature分支。
说完对比了,趁热打铁我们再说下rebase的更重要的一个功能,这就是交互式的rebase;
交互式的rebase解决了一个开发非常头痛的问题,每次功能分支开发完,有时会产生很多的commit,如果一个个合并到主干上,不仅混乱而且不容易revert回退;此时rebase命令可以帮助开发合并这些commit,使得提交历史一目了然,更容易理解;
(注:发起pull request之前,合并commit,可以保证主要分支commit提交历史清晰。)
这里可以使用git rebase命令
git rebase -i
以下是具体的一个实例——合并多个commit场景
开发分支dev2
现在我们要把dev2分支的最后三条commit合并,执行
git checkout dev2
git rebase -i HEAD~3
通过指定HEAD~3作为新的提交,并没有做分支的操作,只是把最后的3次commit提交重写。
紧接着我们进入互动界面
此时会列出了dev2分支的最新的3个commit,排列顺序是越下面越新,默认是pick操作命令。
往下就是命令的注释了,这里详细说明一下各个命令注释:
• p,pick 选中使用的commit
• r,reword 选中使用该commit,并且修改commit提交信息;
• e,edit 选择,rebase时暂停,,允许修改该commit
• s,squash 选中,该commit会合并到前一个commit中,并且保留该commit的提交信息
• f,fixup 与squash一样,但是不会保留该commit的提交信息
• x,exec 执行shell命令
• d,drop 去除该commit
我们这里默认把最新的两个commit合并到第一个里面,然后保留提交信息,把最新的两个commit的操作命令修改为squash。
(如果不需要最新的一个commit提交信息,可以修改其操作命令为fixup即可。)
保存后会显示出这三个commit合并后的详细信息,确认没问题了保存退出即可;
以上显示合并后的commit历史注释以及文件的改变信息;
好了,我们wq保存退出;
Successfully rebased and updated dev2
提示rebase成功;
查看一下git log,可以看到最新的一条commit包含了之前三个的commit提交信息;大功告成。
到了最后一步,我们推送到远程仓库;
这里需要使用--force排除有可能与远程分支不兼容的情况;
git push --force origin dev2
提交commit信息之后,有时会发现跟预期的效果不一样,想要回退到之前的状态,这时我们就可以搬出Git的另外两个常用命令,这就是revert和reset。
2Git的版本回退:revert vs reset
经常有这种情况发生,一个cherry-pick做好之后测试不通过需要回滚,这时就会用到 reset revert命令了;现在通过例子对比一下这两条命令。
reset
在提交的时候,reset将该分支的HEAD从末端往前移动,用来移除当前分支的某些提交;直接删除指定的commit;
git checkout hotfix
git reset HEAD~2
hotfix分支最后两个提交会被扔掉,也就是下次提交的时候,Hotfix的最新的两个提交会从分支提交历史中删除;
另外reset还有两种实用的方法:
git reset —mixed HEAD
将你当前的改动从缓存区中移除,但是改动仍然留在工作目录;
git reset —-hard HEAD
完全舍弃你没有提交的改动。
revert
revert撤销一个提交会创建一个新的提交,版本会递增,这样做提交历史就会比较干净容易处理;
git checkout hotfix
git revert HEAD~2
git revert 不会改变已有的提交历史,git revert更加安全,更适合用在公共分支上;
git reset会直接删除指定的commit,把HEAD指向移动,更适合私有分支 ;
git revert 撤销已经提交的更改,git reset HEAD可以用于撤销没有提交的更改;
目前针对项目进行的撤销我们一般使用revert命令进行;
既然讲到撤销,这里就不得不说下revert撤销时用法:
命令 作用
git revert HEAD 撤销前一个commit
git revert HEAD^ 撤销前前一次commit
git revert HEAD~3 撤销最新的三个commit,把状态HEAD指向第四个commit,并创建一个新的commit提交记录
git revert commit 撤销指定的commit提交,撤销并创建一个新的commit提交记录
这里简单的说一个撤销的实例:
场景:
开发分支dev2,需要在开发分支上撤销最新的一个commit
使用git log查看最新的一个提交历史,接下来我们撤销最新的这个 f3a08e0
git revert HEAD
界面会显示出 Revert + 最新的commit提交历史的注释信息以及该commit带来的文件改变;
没有问题后我们wq保存一下;
显示出撤销带来的最新的commit提交历史, 然后是改变的文件状态;
现在撤销生效了,我们git log确认一下提交历史,如果无误开发分支会生成一个新的提交历史记录信息;
这里最新的一个commit会显示出撤销所创建的提交历史,下面即是撤销前最新的一条commit;你也可以这样理解,最新的一条是+1,撤销创建的是-1,加起来相当于这两个commit对该分支没有任何作用;
接下来git push origin dev2即可。
3Comparing Workflows
说完了Git的命令,接下来说下Git分支的工作流程,我们使用的Git工作流程大致可以分为三类,即是Git flow,Githubflow以及Gitlab flow,结合自己的使用经验本来打算写,但是网络上已经出现了针对这三种工作流程作出的详细文章,所以就不再讲解,如有兴趣可以看一下两篇即可;这里针对这三种不同的工作流程做一下简要对比:
Git的工作流程 http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
Asuccessful Git branching model
开源中国社区翻译中文版地址 http://www.oschina.net/translate/a-successful-git-branching-model
Git flow | Github flow | Gitlab flow | |
主要分支 | master develop | master | master |
版本周期 | 时间较长 | 时间短,可持续发布 | 稳定版本周期性发布 |
优点 | 多分支类型,主分支和开发分支并列,功能分支补丁分支预发布分支;分支类型细化清晰 | 持续发布的优势,单分支简单维护 | 持续发布和版本发布两种开发流程,分支结构明确,持续发布性不错 |
缺点 | 同时维护两个分支比较复杂,持续发布性较差 | 只有单分支,版本发布时间比较固定 | 大型项目分支比较吃力 |
由于工作中很多时间都在跟Git打交道,这里也来总结一下经常遇见的错误和Git的一些使用经验,希望对大家有用。
4附录
I. 关于GIT的一些经验
(i) push.default 设置为simple模式
git的全局配置有一个push.default属性,决定了push操作的默认行为;在Git 2.0之前,这个属性默认是’matching’,2.0以后修改为了’simple’
通过git config —global push.default ‘option'设置默认行为
push.default有以下可选值:
nothing,current,upstream,simple,matching
这里就说明一下最常用的simple和matching
• simple simple必须保证本地分支和远程upstream分支同名,否则拒绝push操作
• matching push所有本地和远程两端都存在的同名分支.(相比simple不安全)
(ii) 本地开发尽量使用gitfetch,不要使用gitpull,gitfetch获取更新后,手动进行merge
git pull= git fetch +merge
git pull从远程获取最新版本到本地merge到本地仓库;而fetch无疑更加安全,从远程获取版本后,手动执行进行合并;
(iii) merge节点
Git有两种形式的合并,fast forward 和none fast-forward;
前者不生成单独的合并节点,后者会生成单独节点;
前者不利于保持commit提交信息,不利于回滚;建议采用后者,发生合并产生一个单独的合并节点。
(iv) Squash多个commit
为了方便cherry-pick或回撤代码的提交变化,发起pull request之前,把多个commit合并成一个;开发发出来的cherry-pick的commit有些甚至多达七八个,
这样处理既让分支提交信息混乱不清晰,而且不利于回撤;使用rebase手动处理-具体方法已在文章开头作详细介绍;
GIT的版本控制
(i) 经常性提交
这样使得你得提交更小,相比团队其他人更容易整合更新,避免merge到主分支的冲突;如果极少的提交更新,单个更新会变得更大,别人合并冲突也不好解决
(ii) 不要进行未完成的功能提交
当代码的逻辑块完成后尽早提交和经常提交,临时需要提交的可以考虑Git的Stash功能暂时提交。
III. 常见错误
(i) 冲突处理
手动解决冲突
冲突符号<<<<<<<HEAD 与 =======之间的内容和 =======与>>>>>>> 6853e5ff9之间的内容产生冲突,只需要保留正确的内容并且去除冲突标识行即可;
(ii) 帐号问题不能执行git操作
情景一:
git操作执行返回
fatal:Authentication failed for ……
这种情况下一般是因为之前的操作帐号认证有问题,或者是帐号连接不上,即使帐号没问题后,依然会如此报错,这时你需要重新设置下个人帐号信息。
解决:
git config —global user.name “xxx”
git config —global user.email “xxx@xxx.com"
情景二:
在做git pull 或git push时传输文件比较大的时候,容易出现:
error: RPC failed; result=22, HTTP code = 413
fatal: The remote end hung up unexpectedly
fatal: The remote end hung up unexpectedly
Everything up-to-date
解决:
git config http.postBuffer 524288000
情景三:
如果The requested URL returned error:400,极有可能是DNS问题,请查看自己的网络DNS确定正确,一般即可解决。
文章中有不足或任何问题,欢迎交流,谢谢!
原文来自:点融黑帮
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等
支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景
涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。
根据给定的手机号、姓名、身份证、人像图片核验是否一致
通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。