V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
git
Pro Git
Atlassian Git Tutorial
Pro Git 简体中文翻译
GitX
zeroten
V2EX  ›  git

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

  •  
  •   zeroten · 2016-08-01 10:47:10 +08:00 · 34292 次点击
    这是一个创建于 3034 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    第 1 条附言  ·  2016-08-01 11:53:05 +08:00
    要回滚的是主干分之 master , reset 可能会给团队中其他人带来困扰,且 reset 太暴力,操作不当可能丢失提交记录
    第 2 条附言  ·  2016-08-01 11:55:21 +08:00
    现在有
    8
    7
    6
    5
    4
    3
    2
    1
    若干提交, 1 是最前面的,现在想回滚到 4 , git revert git revert 4..8 命令可以把 5678 逐个 revert ,但是如果其中有 merge commit 就不能这么用。那么该怎么做?
    第 3 条附言  ·  2016-08-01 11:57:12 +08:00
    简而言之,就是不修改提交记录的回滚回去
    45 条回复    2021-01-15 11:27:22 +08:00
    jsfaint
        1
    jsfaint  
       2016-08-01 11:00:20 +08:00
    为啥不能用 git reset ?
    git reset --hard 不就是做这事的吗?
    henius
        2
    henius  
       2016-08-01 11:08:59 +08:00
    git reset --hard
    hosiet
        3
    hosiet  
       2016-08-01 11:13:22 +08:00 via Android
    为啥不能用 git reset?

    说实在的,只想检出代码又怕影响历史纪录的话, git checkout 都行,就是不知道你的需求是什么

    如果想用一个提交解决一切, git diff 导出区别再 apply 如何?
    zeroten
        4
    zeroten  
    OP
       2016-08-01 11:39:26 +08:00
    @jsfaint
    @henius
    @hosiet 不想用 reset 是为了避免其他人如果从 v101 checkout 一个分之开发去了,现在 reset 到 v100 ,那么他如果再合并代码不就又会把 v100 带回来或者产生冲突,这些不必要的麻烦。
    zeroten
        5
    zeroten  
    OP
       2016-08-01 11:39:50 +08:00
    @hosiet diff 再 apply 是怎么个方案,可以说具体点么
    Havee
        6
    Havee  
       2016-08-01 11:50:54 +08:00
    你的需求到底是什么,有点看不懂。
    如果要从 v100 从头开始来过, reset/checkout 都行,最好是 checkout 个分支,人家仍然 push 到原分支。
    zeroten
        7
    zeroten  
    OP
       2016-08-01 11:51:51 +08:00
    简而言之, reset 可能会给团队中其他人带来困扰,且 reset 太暴力,操作不当可能丢失提交记录
    zeroten
        8
    zeroten  
    OP
       2016-08-01 11:56:43 +08:00
    @Havee 见问题补充
    zeroch
        9
    zeroch  
       2016-08-01 12:05:47 +08:00
    你要知道, 如果你在 remote master branch 上进行了回滚操作,然后在 push 回到 master 上面.那么, 无论如何你已经和 diverge, *不可避免*的对别人造成影响..

    如果你在 local 的任意 branch 上操作..上面的回复说的, 不论是 reset 还是 checkout 都是可以的。甚至还有一种方法.你先 checkout 1, 然后从 1 到 4 进行 cherry-pick 。 cherry-pick 不会 hash 出新的 commit id 。
    fangdingjun
        10
    fangdingjun  
       2016-08-01 12:26:34 +08:00
    像这样, 用历史文件覆盖当前文件,并产生一个新的 commit

    git checkout -b temp
    git reset --hard c7
    git archive HEAD > /tmp/t.tar
    git checkout master
    tar xf /tmp/t.tar
    git add .
    git commit -m 'reset to c7'
    axb
        11
    axb  
       2016-08-01 12:32:04 +08:00
    xi_lin
        12
    xi_lin  
       2016-08-01 12:42:51 +08:00
    BOYPT
        13
    BOYPT  
       2016-08-01 13:26:37 +08:00
    感觉就像朋友圈里面那些不许用方程解小学数学题,好难~
    networm
        14
    networm  
       2016-08-01 15:48:45 +08:00 via iPhone   ❤️ 1
    git reset --hard 4
    git reset --soft 8
    git commit -m 'Reverted 5 6 7 8'

    其实楼主怕的是丢失提交记录,而不是使用 reset rebase 命令
    julyclyde
        15
    julyclyde  
       2016-08-01 15:52:01 +08:00
    revert 是回滚某个 commit ,不是回滚“到”某个
    你需要 checkout 或者 reset 之类的
    zzzreg
        16
    zzzreg  
       2016-08-01 16:51:34 +08:00
    切个新 branch 再 reset 怎么样?
    pagxir
        17
    pagxir  
       2016-08-01 17:03:48 +08:00 via Android
    这么简单的操作,有你们想的那么复杂吗。
    git rm -r -f *
    git checkout <rev> .
    git commit -m xxxx
    就所有文件恢复到版本 rev 的状态,同时不进行分支修剪。
    zeroten
        18
    zeroten  
    OP
       2016-08-01 17:20:44 +08:00
    @julyclyde 你看我主题中发的 git revert git revert v100..HEAD ,就有了回滚“到”的效果,只是有 merge commit 就不行了
    zeroten
        19
    zeroten  
    OP
       2016-08-01 17:22:06 +08:00
    @BOYPT reset 那些会丢失提交记录,会产生冲突等等
    zeroten
        20
    zeroten  
    OP
       2016-08-01 17:27:05 +08:00
    那些用 reset 、 rebase 的同学就不考虑到团购开发中不可能给所有人强制提交 master 的权限,而且这种操作太危险。而用 revert 或其他不修改提交记录的方式,就可以走正常的分之开发流程。
    @jsfaint @henius @hosiet @Havee @zeroch @fangdingjun @axb @xi_lin @BOYPT @networm @julyclyde @zzzreg @pagxir
    lightening
        21
    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
        22
    clino  
       2016-08-01 17:58:16 +08:00
    "且 reset 太暴力,操作不当可能丢失提交记录" 怕丢历史就先新建一个 backup 分支
    zxq1002
        23
    zxq1002  
       2016-08-01 18:06:47 +08:00 via Android
    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
        24
    zxq1002  
       2016-08-01 18:11:38 +08:00 via Android
    @zxq1002 不好意思,少了 git reset --soft <the hash id of commit: revert from 8 to 4>
    git commit -m "revert"
    同意 21 楼答案
    jsfaint
        25
    jsfaint  
       2016-08-01 18:11:44 +08:00
    @zeroten reset,rebase 用在本地的 unmerged branch 上有什么关系?
    在远端即使你使用 revert 也依然会对其他人造成困扰的。除非对应的 branch 只有你一个人在用,不过既然只有你一个人用, rebase , reset 又有什么关系呢……
    lightening
        26
    lightening  
       2016-08-01 18:11:59 +08:00   ❤️ 1
    OK 找到一个更清晰的方法:

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

    nothing to commit, working directory clean

    因为你在 commit 那步 提交的文件与你 原分支上的代码是一样的, 所以最终 cherry-pick 会显示没有什么可拿过来的,
    如果非要拿过来, 请加一个 --allow-empty 参数.
    xqin
        28
    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
        29
    zeroten  
    OP
       2016-08-01 18:53:17 +08:00
    @xqin 那有没有什么好的方法能不收购处理 merge commit 呢?比如“ 2016 年 7 月 1 日 3 点上线的那个版本”,这个版本是明确的,我应该如何能记录这个版本,并且能够方便快速的回滚的,目标是一行命令或者脚本,且不需要人工干预。
    xi_lin
        30
    xi_lin  
       2016-08-01 19:01:55 +08:00
    @zeroten 为啥在 reset 的那个里 @我?我的回复是 revert -m 让你选 parent 啊
    xi_lin
        31
    xi_lin  
       2016-08-01 19:03:23 +08:00
    @zeroten 不过正常来说 master 上的都是 fast forward 的 merge ,用楼上给的 reset soft 也不是不行
    zxq1002
        32
    zxq1002  
       2016-08-01 19:04:22 +08:00 via Android
    @xqin 我后面已经补充了,少了两步操作
    xqin
        33
    xqin  
       2016-08-01 19:05:46 +08:00
    @zeroten 你不都已经打了 Tag 了吗? 那还 revert 什么?
    你直接从那个 tag 中检出一个分支, 然后你想干嘛干嘛嘛.

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

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

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

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

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

    我就一个问题

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

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

    git checkout 4 # 回到历史
    git reset --mixed master # 把历史带到脑袋
    git checkout master # 签出脑袋
    git commit # 把历史和脑袋的 revert diff 提交
    cszhiyue
        39
    cszhiyue  
       2016-08-02 09:13:30 +08:00
    想到的方案同样和 @lightening 一致
    @xqin 这个没冲突啊。如果想 revert 10,9,5,6 的同时保留 7,8,直接把 checkout 4 改成 checkout 8 就可以了
    xx314327475
        40
    xx314327475  
       2017-03-22 11:33:41 +08:00
    @msg7086 就服你,比人不服
    Youthink
        41
    Youthink  
       2017-12-21 23:37:13 +08:00
    遇到了和楼主一样的问题。 @networm 的回答帮助了我。
    aoTao
        42
    aoTao  
       2018-01-23 10:35:36 +08:00
    遇到和楼主同样的问题 , 按照 @networm 的方式完美解决!
    njwangchuan
        43
    njwangchuan  
       2018-07-09 14:35:55 +08:00
    用了 @newtorm 的方法感觉挺好,请问有啥副作用没。
    williamwue
        44
    williamwue  
       2018-08-03 10:24:01 +08:00
    @msg7086 非常棒的方案!实测通过
    njwangchuan
        45
    njwangchuan  
       2021-01-15 11:27:22 +08:00
    @msg7086 简单,实用,解决了大问题。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3017 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 14:17 · PVG 22:17 · LAX 06:17 · JFK 09:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.