git 如何用 revert 回滚代码到某个 commit?

2016-08-01 10:47:10 +08:00
 zeroten

比如有 commit asda21313123,我给它打了 tag v100 ,如果使用git revert git revert v100..HEAD的方式,如果中间有 merge commit 就不行。

要求不能用 reset 、 rebase 这样重写提交记录的方法。

34319 次点击
所在节点    git
45 条回复
lightening
2016-08-01 17:51:42 +08:00
赞同 @networm

你 reset 后并不 force push ,不会修改 history 的。

他所做的本质上就是手动提交一个新 commit ,其内容是 revert 你的那些 commits 。

我稍微修改一下,帮助理解,本质上和 @newworm 是一样的:

git checkout 4
# 把你 checkout 的代码状态更新到 4 的状态,此时你是 detached HEAD 状态,没有在任何 branch 上

git reset --soft <your_branch_name>
# 把你的 branch 状态回复到原先的 branch 。因为是 soft reset , checkout 的代码还是 4 的状态

git add .
git commit -m "Revert 5 6 7 8"
# 此时再提交一个新的 commit 。由于你的代码是 4 的状态,但是你此时 history 处于 8 的位置,再移交一个 commit 就是 9 了。其内容正好是变回 4 的状态,也就是说实际上 commit 9 正好 revert 了 5 6 7 8.
clino
2016-08-01 17:58:16 +08:00
"且 reset 太暴力,操作不当可能丢失提交记录" 怕丢历史就先新建一个 backup 分支
zxq1002
2016-08-01 18:06:47 +08:00
git branch tmp
git checkout tmp
git reset --soft 4
git commit -m "revert from 8 to 4"
git checkout <your branch>
git cherry-pick <the hash id of commit: revert from 8 to 4> or git merge tmp
git branch -d tmp
zxq1002
2016-08-01 18:11:38 +08:00
@zxq1002 不好意思,少了 git reset --soft <the hash id of commit: revert from 8 to 4>
git commit -m "revert"
同意 21 楼答案
jsfaint
2016-08-01 18:11:44 +08:00
@zeroten reset,rebase 用在本地的 unmerged branch 上有什么关系?
在远端即使你使用 revert 也依然会对其他人造成困扰的。除非对应的 branch 只有你一个人在用,不过既然只有你一个人用, rebase , reset 又有什么关系呢……
lightening
2016-08-01 18:11:59 +08:00
OK 找到一个更清晰的方法:

git checkout 4 -- ./ # 直接取出 4 的代码,不改变 branch
git commit -m "Revert 5 6 7 8" # 然后提交 commit 9
xqin
2016-08-01 18:20:07 +08:00
@zxq1002 按你的做法, 最终 cherry-pick 的时候, 会得到一个提示:

nothing to commit, working directory clean

因为你在 commit 那步 提交的文件与你 原分支上的代码是一样的, 所以最终 cherry-pick 会显示没有什么可拿过来的,
如果非要拿过来, 请加一个 --allow-empty 参数.
xqin
2016-08-01 18:40:14 +08:00
@lightening 你的方法存在问题吧?
楼主已经说了 他要 revert 的内容中有 merge , 你直接从某个 commit 点提取出来的代码, 并不是 revert 后的结果.
举个最简单的例子.

==10
==9
8===6
7===5
==4
==3
==2
==1

> 由于 v2 在评论中对格式展示的不好, 所以用 = 号来表示空格, 请自行替换后, 感受一下 git log 目前的情况.

9 是由 6/8 合并而来, 现在要还原的 commit 为 10,9,6,5

按你的做法是 checkout 4 -- ./ 然后 commit

这么做的后果是 8/7 的 commit 没了.

楼主的情况应该就是这种, 所以 git 要求他, 选择 parent.
zeroten
2016-08-01 18:53:17 +08:00
@xqin 那有没有什么好的方法能不收购处理 merge commit 呢?比如“ 2016 年 7 月 1 日 3 点上线的那个版本”,这个版本是明确的,我应该如何能记录这个版本,并且能够方便快速的回滚的,目标是一行命令或者脚本,且不需要人工干预。
xi_lin
2016-08-01 19:01:55 +08:00
@zeroten 为啥在 reset 的那个里 @我?我的回复是 revert -m 让你选 parent 啊
xi_lin
2016-08-01 19:03:23 +08:00
@zeroten 不过正常来说 master 上的都是 fast forward 的 merge ,用楼上给的 reset soft 也不是不行
zxq1002
2016-08-01 19:04:22 +08:00
@xqin 我后面已经补充了,少了两步操作
xqin
2016-08-01 19:05:46 +08:00
@zeroten 你不都已经打了 Tag 了吗? 那还 revert 什么?
你直接从那个 tag 中检出一个分支, 然后你想干嘛干嘛嘛.

另外代码回滚 是你发布工具的事情 与 git 无关, 如果你代码中有 bug,只管在 git 中修复即可.

线上的代码如果要回滚的话, 一般情况下 发布系统(至少我们发布系统会)都会有保留之前站点前几个版本的内容, 比如每次使用的时候使用不同的文件夹, 如果要回滚, 直接将站点切回之前的文件夹即可. 根本不需要 git 参与嘛.

比如当前的代码在 A 目录, 然后发布一次使用 B 目录, 再发一次使用 C 目录,

如果 C 目前的代码有问题,直接将站点切换回 B 目录即可.
lightening
2016-08-01 19:23:32 +08:00
@xqin 因为楼主一直说是要“回滚到”某个以前的版本,我觉得他其实就是想把所有的 merge 都不要了。

楼主说“ 2016 年 7 月 1 日 3 点上线的那个版本”这个需求,应该就是所有的 commit 都不要的意思吧。
caiya21
2016-08-01 20:26:21 +08:00
楼主可以考虑 用下 source tree (逃
HackerOO7
2016-08-01 21:09:57 +08:00
git branch newbranch commitid
zhx1991
2016-08-01 21:47:35 +08:00
太长了

我就一个问题

中间的提交还要不要
msg7086
2016-08-02 03:39:48 +08:00
重写分支太危险 -> 你需要一个好用的工具。我做历史重写从来没丢过东西。

至于 reset ,谁告诉你 reset 一定要重写历史的?
如果你要让线上的版本滚回到精确的某个提交的状态,那么先 checkout 历史提交,然后做 reset mixed 到 HEAD ,再做一次提交就行了,这个提交就包含了两者之间所有更改的 revert 。

git checkout 4 # 回到历史
git reset --mixed master # 把历史带到脑袋
git checkout master # 签出脑袋
git commit # 把历史和脑袋的 revert diff 提交
cszhiyue
2016-08-02 09:13:30 +08:00
想到的方案同样和 @lightening 一致
@xqin 这个没冲突啊。如果想 revert 10,9,5,6 的同时保留 7,8,直接把 checkout 4 改成 checkout 8 就可以了
xx314327475
2017-03-22 11:33:41 +08:00
@msg7086 就服你,比人不服

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/296286

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX