Git 如何优雅地回退代码
前言
从接触编程就开始使用 Git 进行代码管理,先是自己玩 Github,又在工作中使用 Gitlab,虽然使用时间挺长,可是也只进行一些常用操作,如推拉代码、提交、合并等,更复杂的操作没有使用过,看过的教程也逐渐淡忘了,有些对不起 Linus 大神。
出来混总是要还的,前些天就遇到了 Git 里一种十分糟心的场景,并为之前没有深入理解 Git 命令付出了一下午时间的代价。
先介绍一下这种场景,我们一个项目从 N 版本升到 A 版本时引入了另一项目的 jar 包,又陆续发布了 B、C 版本,但在 C 版本后忽然发现了 A 版本引入的 jar 包有极大的性能问题,B、C 版本都是基于 A 版本发布的,要修复 jar 包性能问题,等 jar 包再发版还得几天,可此时线上又有紧急的 Bug 要修,于是就陷入了进退两难的境地。
最后决定先将代码回退到 A 版本之前,再基于旧版本修复 Bug,也就开始了五个小时的受苦之路。
转载随意,文章会持续修订,请注明来源地址:https://zhenbianshu.github.io 。
基础试探
revert
首先肯定的是 revert,git revert commit_id 能产生一个 与 commit_id 完全相反的提交,即 commit_id 里是添加, revert 提交里就是删除。
但是使用 git log 查看了提交记录后,我就打消了这种想法,因为提交次数太多了,中途还有几次从其他分支的 merge 操作。”利益于”我们不太干净的提交记录,要完成从 C 版本到 N 版本的 revert,我需要倒序执行 revert 操作几十次,如果其中顺序错了一次,最终结果可能就是不对的。
另外我们知道我们在进行代码 merge 时,也会把 merge 信息产生一次新的提交,而 revert 这次 merge commit 时需要指定 m 参数,以指定 mainline,这个 mainline 是主线,也是我们要保留代码的主分支,从 feature 分支往 develop 分支合并,或由 develop 分支合并到 master 的提交还好确定,但 feature 分支互相合并时,我哪知道哪个是主线啊。
所以 revert 的文案被废弃了。
Reset
然后就考虑 reset 了, reset 也能使代码回到某次提交,但跟 revert 不同的是, reset 是将提交的 HEAD 指针指到某次提交,之后的提交记录会消失,就像从没有过这么一次提交。
但由于我们都在 feature 分支开发,我在 feature 分支上将代码回退到某次提交后,将其合并到 develop 分支时却被提示报错。这是因为 feature 分支回退了提交后,在 git 的 workflow 里,feature 分支是落后于 develop 分支的,而合并向 develop 分支,又需要和 develop 分支保持最新的同步,需要将 develop 分支的数据合并到 feature 分支上,而合并后,原来被 reset 的代码又回来了。
这个时候另一个可选项是在 master 分支上执行 reset,使用 --hard 选项完全抛弃这些旧代码,reset 后再强制推到远端。
master> git reset --hard commit_id
master> git push --force origin master
但是还是有问题,首先,我们的 master 分支在 gitlab 里是被保护的,不能使用 force push,毕竟风险挺大了,万一有人 reset 到最开始的提交再强制 push 的话,虽然可以使用 reflog 恢复,但也是一番折腾。
另外,reset 毕竟太野蛮,我们还是想能保留提交历史,以后排查问题也可以参考。
升级融合
rebase
只好用搜索引擎继续搜索,看到有人提出可以先使用 rebase 把多个提交合并成一个提交,再使用 revert 产生一次反提交,这种方法的思路非常清晰,把 revert 和 rebase 两个命令搭配得很好,相当于使用 revert 回退的升级版。
先说一下 rebase,rebase 是”变基”的意思,这里的”基”,在我理解是指[多次] commit 形成的 git workflow,使用 rebase,我们可以改变这些历史提交,修改 commit 信息,将多个 commit 进行组合。
介绍 rebase 的文档有很多,我们直接来说用它来进行代码回退的步骤。
- 首先,切出一个新分支 F,使用 git log 查询一下
要回退到的 commit 版本 N。 使用命令
git rebase -i N, -i 指定交互模式后,会打开 git rebase 编辑界面,形如:pick 6fa5869 commit1
pick 0b84ee7 commit2
pick 986c6c8 commit3
pick 91a0dcc commit4
- 这些 commit 自旧到新由上而下排列,我们只需要在 commit_id 前添加操作命令即可,在合并 commit 这个需求里,我们可以选择
pick(p)最旧的 commit1,然后在后续的 commit_id 前添加squash(s)命令,将这些 commits 都合并到最旧的 commit1 上。 - 保存 rebase 结果后,再编辑 commit 信息,使这次 rebase 失效,git 会将之前的这些 commit 都删除,并将其更改合并为一个新的 commit5,如果出错了,也可以使用
git rebase --abort/--continue/--edit-todo对之前的编辑进行撤销、继续编辑。 - 这个时候,主分支上的提交记录是
older, commit1, commit2, commit3, commit4,而 F 分支上的提交记录是older, commit5,由于 F 分支的祖先节点是 older,明显落后于主分支的 commit4,将 F 分支向主分支合并是不允许的,所以我们需要执行git merge master将主分支向 F 分支合并,合并后 git 会发现 commit1 到 commit4 提交的内容和 F 分支上 commit5 的修改内容是完全相同的,会自动进行合并,内容不变,但多了一个 commit5。 - 再在 F 分支上对 commit5 进行一次 revert 反提交,就实现了把 commit1 到 commit4 的提交全部回退。
这种方法的取巧之处在于巧妙地利用了 rebase 操作历史提交的功能和 git 识别修改相同自动合并的特性,操作虽然复杂,但历史提交保留得还算完整。
rebase 这种修改历史提交的功非常实用,能够很好地解决我们遇到的一个小功能提交了好多次才好使,而把 git 历史弄得乱七八糟的问题,只需要注意避免在多人同时开发的分支使用就行了。
遗憾的是,当天我并没有理解到 rebase 的这种思想,又由于试了几个方法都不行太过于慌乱,在 rebase 完成后,向主分支合并被拒之后对这些方式的可行性产生了怀疑,又加上有同事提出听起来更可行的方式,就中断了操作。
文件操作
这种更可行的方式就是对文件操作,然后让 git 来识别变更,具体是:
- 从主分支上切出一个跟主分支完全相同的分支 F。
- 从文件管理系统复制项目文件夹为 bak,在 bak 内使用
git checkout N将代码切到想要的历史提交,这时候 git 会将 bak 内的文件恢复到 N 状态。 - 在从文件管理系统内,将 bak 文件夹下
除了 .git文件夹下的所有内容复制粘贴到原项目目录下。git 会纯从文件级别识别到变更,然后更新工作区。 - 在原项目目录下执行
add 和 commit,完成反提交。
这种方式的巧妙之处在于利用 git 本身对文件的识别,不牵涉到对 workflow 操作。
小结
最后终于靠着文件操作方式成功完成了代码回退,事后想来真是一把心酸泪。
为了让我的五个小时不白费,复盘一下当时的场景,学习并总结一下四种代码回退的方式:
- revert 适合需要回退的历史提交不多,且无合并冲突的情景。
- 如果你可以向 master 强推代码,且想让 git log 里不再出现被回退代码的痕迹,可以使用
git reset --hard + git push --force的方式。 - 如果你有些 geek,追求用”正规而正统”的方式来回退代码,rebase + revert 满足你的需求。
- 如果你不在乎是否优雅,想用最简单,最直接的方式,文件操作正合适。
git 真的是非常牛逼的代码管理工具,入手简单,三五个命令组合起来就足够完成工作需求,又对 geeker 们非常友好,你想要的骚操作它都支持,学无止境啊。
Git 如何优雅地回退代码的更多相关文章
- GIT如何根据历史记录回退代码
ps: 因为使用这种方式回退后,回退的目标版本之后提交的代码都没了,所以建议先把当前代码打个tag 首先找到分支的提交记录 git log 将代码回退到历史版本 git reset --hard 0f ...
- git add.后回退 代码丢失
记录一次操作git丢失代码的过程: 写完代码后:git staus git add. git status 发现有一堆.class 文件不想提交,想着代码回退到add 之前,使用了 git log 开 ...
- git merge了错误分支,如何优雅的回退到merge前的状态?
git merge了错误分支,如何优雅的回退到merge前的状态? 没push的话 git reset --hard (a分支5点那个时候commit之后的sha1) 然后重新 git merge
- SourceTree 如何下载git 管理的代码-如何创建分支,删除分支,提交代码,回退代码
把用户给的链接拿过来,然后输入浏览器,然后在左侧会有Actions 中有个Clone;点击Clone之后,有个 Clone in Source Tree 点击,打开你的本地Source Tree,然后 ...
- git回退代码到某次commit
回退命令: $ git reset --hard HEAD^ 回退到上个版本 $ git reset --hard HEAD~3 回退到前3次提交之前,以此类推,回退到n次提交之前 $ git res ...
- Git命令回退代码并同步到远程仓库
git log 找到要回退的commit版本号并复制 git reset --hard [commitid] 本地库版本回退 git push -f origin [branchName] 同步到远端 ...
- Git 安装及用法 github 代码发布 gitlab私有仓库的搭建
版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 这个版本控制软件,有 svn还有git,是一个工具. git是由linux的作者开发的 git是一个分布式版本控制系统 ...
- [原]git的使用(一)---建立本地仓库、add和commit、status和git diff、版本回退使用git reset
在window下已经安装了git的环境 1.建立本地仓库 mkdir test #建立test目录 cd test #进入目录 git init # ...
- Git学习-Git时光机之版本回退(二)
Git,是Linus花了两周时间用C写的一个分布式版本控制系统.牛人该怎么定义? 零.结论先行 倒叙总结一下: HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git ...
随机推荐
- 使用Typescript重构axios(三)——实现基础功能:处理get请求url参数
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- P3106 [USACO14OPEN]GPS的决斗(最短路)
化简:够简的了.....但是!翻译绝对有锅. 这个最短路是从n到每个点的单源最短路,也就是最短路径树. 那么,思路就很明确了.建两个图,然后跑两边SPFA,记录下最短路径. 然后,对于两点之间的边,如 ...
- 简单看看@RequestBody注解原理
又到了很无聊的时候了,于是随便看看源码假装自己很努力的样子,哈哈哈: 记得上一篇博客随便说了一下RequestBody的用法以及注意的问题,这个注解作为非常常用的注解,也是时候了解一波其中的原理了. ...
- Linux(ubuntu)下切换root用户
输入命令su root切换用户,会提示输入root密码,如果不记得或者是没设置过,那么可以输入sudo passwd root来设置密码,会让你输入两次密码确认.输入完即可使用su root命令切换r ...
- Download-学习资源下载
小白求关爱:链接为本人工作中学习所碰到的,如有不对之处,请及时联系加以改正,后续仍会追加新链接地址并及时更新旧链接地址 Jdk历史版本 https://www.oracle.com/technetwo ...
- 解决vue低版本安卓手机兼容性问题
低版本的安卓手机可能会白屏,是由新特性不支持引起的 解决代码es6新特性兼容问题 1,npm 安装 npm install babel-polyfill npm install es6-promise ...
- nyoj 3 多边形重心问题
多边形重心问题 时间限制:3000 ms | 内存限制:65535 KB 难度:5 描述 在某个多边形上,取n个点,这n个点顺序给出,按照给出顺序将相邻的点用直线连接, (第一个和最后一个连接 ...
- 使用图数据库 Nebula Graph 数据导入快速体验知识图谱 OwnThink
前言 本文由 Nebula Graph 实习生@王杰贡献. 最近 @Yener 开源了史上最大规模的中文知识图谱--OwnThink(链接:https://github.com/ownthink/Kn ...
- config system setting for elasticsearch
1. disable swapping. swap 分区会导致garbage collections 花费太多时间,elasticsearch 响应变慢. 方法1:swapoff -a 方法2:vi ...
- 扛把子组20191121-3 Final阶段贡献分配规则
此作业的要求参见http://edu.cnblogs.com/campus/nenu/2019fall/homework/10063 队名:扛把子 组长:孙晓宇 组员:宋晓丽 梁梦瑶 韩昊 刘信鹏 F ...