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

总结一下 Git 不同情况下如何回滚

  •  4
     
  •   billyangg · 2019-03-02 00:14:09 +08:00 · 5402 次点击
    这是一个创建于 2085 天前的主题,其中的信息可能已经有所发展或是发生改变。

    撤销

    在未进行git push前的所有操作,都是在“本地仓库”中执行的。我们暂且将“本地仓库”的代码还原操作叫做“撤销”

    情况一:文件被修改了,但未执行git add操作(working tree 内撤销) (modify file) <==> git checkout <filename>/.

    git checkout fileName
    git checkout .
    

    情况二:同时对多个文件执行了git add操作,但本次只想提交其中一部分文件 git add <==> git reset HEAD <filename>

    $ git add *
    $ git status
    # 取消暂存
    $ git reset HEAD <filename>
    

    情况三:文件执行了git add操作,但想撤销对其的修改( index 内回滚) (modify file and add) <==> git reset HEAD <filename> && git checkout <filename>

    # 取消暂存
    git reset HEAD fileName
    # 撤销修改
    git checkout fileName
    

    情况四:修改的文件已被git commit,但想再次修改不再产生新的 Commit git commit <==> git commit --amend -m 'msg'

    # 修改最后一次提交 
    $ git add sample.txt
    $ git commit --amend -m"说明"
    

    情况五:已在本地进行了多次git commit操作,现在想撤销到其中某次 Commit (git multiple commit) <==> git git reset [--hard|soft|mixed|merge|keep] [commit|HEAD]

    一般使用 --soft

    git reset [--hard|soft|mixed|merge|keep] [commit|HEAD]
    

    回滚

    上述场景二,已进行git push,即已推送到“远程仓库”中。我们将已被提交到“远程仓库”的代码还原操作叫做“回滚”!注意:对远程仓库做回滚操作是有风险的,需提前做好备份和通知其他团队成员!

    情况一:切换到 tag 或 branch

    如果你每次更新线上,都会打 tag,那恭喜你,你可以很快的处理上述场景二的情况 git tag <==> git checkout <tag>

    git checkout <tag>
    

    如果你回到当前 HEAD 指向 (git current HEAD) <==> git checkout <branch_name>

    git checkout <branch_name>
    

    情况二:撤销指定文件到指定版本 (git file in history) <==> git checkout <commitID> <filename>

    # 查看指定文件的历史版本
    git log <filename>
    # 回滚到指定 commitID
    git checkout <commitID> <filename>
    

    情况三:删除最后一次远程提交 git revert HEAD || git reset --hard HEAD^

    方式一:使用 revert 会有新的 commit 记录

    git revert HEAD
    git push origin master
    

    方式二:使用 reset 不会产生新的 commit 记录

    git reset --hard HEAD^
    git push origin master -f
    

    二者区别:

    • revert是放弃指定提交的修改,但是会生成一次新的提交,需要填写提交注释,以前的历史记录都在;
    • reset是指将 HEAD 指针指到指定提交,历史记录中不会出现放弃的提交记录。

    情况四:回滚某次提交 (git commit in history) <==> git revert <commitID>

    # 找到要回滚的 commitID
    git log
    git revert commitID
    

    同样的,revert 会出现一次新的 commit 提交记录,这里也可以使用 reset

    删除

    删除某次提交 (git commit in history) <==> git rebase -i <commitID>

    git log --oneline -n5
    
    git rebase -i <commit id>^
    

    **注意:**需要注意最后的_^_号,意思是 commit id 的前一次提交

    git rebase -i "5b3ba7a"^
    

    在编辑框中删除相关 commit,如pick 5b3ba7a test2,然后保存退出(如果遇到_冲突_需要先解决_冲突_)!

    git push origin master -f
    

    通过上述操作,如果你想对历史多个 commit 进行处理或者,可以选择git rebase -i,只需删除对应的记录就好。rebase 还可对 commit 消息进行编辑,以及合并多个 commit。

    转自: https://blog.csdn.net/ligang2585116/article/details/71094887

    我的订阅号:JS 菌;希望能给个关注支持我谢谢~

    27 条回复    2019-03-03 21:50:42 +08:00
    loading
        1
    loading  
       2019-03-02 05:23:09 +08:00 via Android
    这么多收藏,我顶一个。
    kimifdw
        2
    kimifdw  
       2019-03-02 08:03:19 +08:00
    厉害了
    luckyqiang
        3
    luckyqiang  
       2019-03-02 08:59:52 +08:00 via iPhone
    马克
    guog
        4
    guog  
       2019-03-02 09:20:24 +08:00 via Android
    应该是 git checkout -- filename 吧
    zllmath
        5
    zllmath  
       2019-03-02 10:42:42 +08:00
    kaneg
        6
    kaneg  
       2019-03-02 10:58:36 +08:00 via iPhone
    git push -f 应该是 force push 吧,有一定的危险性,很多仓库不一定开启
    billyangg
        7
    billyangg  
    OP
       2019-03-02 11:58:20 +08:00
    billyangg
        8
    billyangg  
    OP
       2019-03-02 12:01:41 +08:00
    @kaneg 在网上查了一下 是不是应该用 --force-with-lease ?

    > "解决的是本地仓库不够新时,依然覆盖了远端新仓库的问题"
    LuckCode
        9
    LuckCode  
       2019-03-02 12:46:05 +08:00
    赞一个,最近刚好遇到部分场景
    kaneg
        10
    kaneg  
       2019-03-02 12:57:06 +08:00 via iPhone
    @billyangg 关于 force push 的技术问题可以参考这个 https://juejin.im/post/5985b9c56fb9a03c376de762

    我个人的观点是对版本管理系统而言,发生的所有操作都应该在历史记录中,如同时间不可倒流一样,可以补救但不应该回滚。
    除非是系统管理员迫于某种特殊原因,否则其他普通用户不要碰这么底层的功能。
    rockyou12
        11
    rockyou12  
       2019-03-02 13:04:11 +08:00
    真远程仓库上提交错了,老老实实从以前的版本 ctrl+c,ctrl+v 然后 commit 条新的记录算了。-f 这种搞错了太吓人了
    msg7086
        12
    msg7086  
       2019-03-02 18:10:50 +08:00
    @kaneg 我个人的观点是,对版本管理系统而言,只有有用的操作才应该写在历史记录中。
    比如写某个模块,程序员尝试了 20 种不同的方法,最后把正确的方法提交进了版本库。如果每一个操作都记录进去,那么就等于是时间线上出现了三四十次提交,审核代码时,这之前的那么多次尝试你是审呢(浪费时间)还是不审呢(会有漏网之鱼)。还我的话,宁愿把代码整理干净,既方便审核,又方便回滚和追溯。
    以及我认为,Force push 应该是每一个程序员都应该掌握的功能。连 Interactive rebase 和 Force push 都做不到的程序员,我不认为是完整掌握了 Git。
    picone
        13
    picone  
       2019-03-02 23:04:47 +08:00
    @rockyou12 #11 这样很容易搞错,出事了也很可怕,建议熟悉一下 revert。。
    rockyou12
        14
    rockyou12  
       2019-03-02 23:29:39 +08:00
    @picone 都 push 还 revert 个鬼哦……
    rockyou12
        15
    rockyou12  
       2019-03-02 23:30:36 +08:00
    @msg7086 你这个完全可以靠开发的时候每个人一个分支,功能完成 rebase 一下。完全不需要-f
    picone
        16
    picone  
       2019-03-02 23:54:21 +08:00
    @rockyou12 #14 push 了本地 revert 提交新的回滚 commit 再 push 即可
    deeplee
        17
    deeplee  
       2019-03-03 00:31:03 +08:00
    腻害,收藏了
    rockyou12
        18
    rockyou12  
       2019-03-03 00:32:03 +08:00
    @picone 好像是我没仔细读 lz 文章……
    msg7086
        19
    msg7086  
       2019-03-03 08:04:06 +08:00
    @rockyou12 开发不一定能做到每人一个分支。
    而且每人一个分支 Rebase 一样要 Force push,只不过 Push 的不是 Master/Dev 而是 Feature 分支罢了。
    nicevar
        20
    nicevar  
       2019-03-03 09:18:39 +08:00
    总结的不错,给个赞
    rockyou12
        21
    rockyou12  
       2019-03-03 11:00:55 +08:00
    @msg7086 做不到就是项目管理问题啊,有啥好说的。而且你开 feature 分支,肯定是从 master 或者 dev 上来的,这两个分支不允许直接提交。然后你 feature 上开发一堆 commit,然后 rebase 成一个 commit,merge 到 master 或 dev 上需要做什么 force push ?你 feature 分支开发完本来删了就是了,你还 rebase 了 push 上去干嘛。
    msg7086
        22
    msg7086  
       2019-03-03 11:15:20 +08:00
    @rockyou12 我们就是这么管理项目的。Rebase 了 Push 上去是为了做 Merge Request Peer Review。不做 Peer Review 直接 Squash 成一个 Commit 然后就往主干上塞?我可谢谢您了。
    而且我们要求组员都要频繁提交频繁 Push,完成一处修改就 Commit+Push,以避免本地副本意外丢失。
    不知道这些被很多人推荐的项目管理实践怎么到你眼中都变成没必要的东西了。

    对我们来说,绑着程序员不让他们自由地利用工具来完成项目才是个项目管理问题,有啥好说的。
    rockyou12
        23
    rockyou12  
       2019-03-03 12:16:45 +08:00
    @msg7086 我确实不理解我说的和你流程上哪点冲突了,你是主动 merge 也好还是 pull request 也好和你代码 review 没啥冲突。

    而且说什么自由利用工具,你们项目都知道做 review 就不是为了让你们不乱写代码,别乱用功能吗? git 功能强,但是功能设计是很有问题的,就像 reset、revert,merge、rebase,单看命名鬼知道区别在哪。

    再摸着自己良心想想才工作时候的自己把,你能把 git 吃这么透?不深入掌握就没 git 使用权了?你团队个个都是神能 100%不出错? java 这么流行不就是写着感觉吃屎,但很难想 c++一样能恶心到别人。

    所以不准用-f 这个屎,只要领导懂技术,还真就肯定要让开发人员吃,不爽也没法。
    msg7086
        24
    msg7086  
       2019-03-03 12:57:01 +08:00
    @rockyou12 Git 这么好用的工具,出错了又怎么样? push -f 又不会丢数据,覆盖了有用提交直接从 reflog 里挖出来 push -f 推回去就行了,为什么要个个是神 100%不出错?出错了才能吸取经验才能进步,就是因为怕犯错才天天这也不敢用那也不敢用,Git 用了 10 年也就和刚上手一两年的人一个水平。

    而且相反的,不允许-f 来纠正错误,让程序员提心吊胆生怕不小心推错东西一失足成千古恨,才是所谓的「你团队个个都是神能 100%不出错」。

    至于你说的 reset revert merge rebase 瞎瘠薄命名的问题,我只能说,找个好点的客户端吧。不要让工具去限制人的发挥。

    命令行 Git 功能参数太乱 -> 复杂 Merge/Rebase 容易丢代码 -> 不允许 push -f -> 程序员怕推错提交而自裁提交数量 -> 不再频繁提交与推送

    不知道别人怎么样,反正要是哪天不让我用 push -f 了,怕是我连维护手头的那堆开源软件都不知道怎么操作了。
    msg7086
        25
    msg7086  
       2019-03-03 13:03:16 +08:00
    Git 包含两部分,一个是底层的文件结构,也就是 .git 目录和里面的数据。
    另一个是上面跑的命令行程序。
    Git 强大就强大在他的灵活性上,也就是前者强大;而后者功能虽然强,但是结构非常乱,适合被脚本、软件调用,而不是直接面对用户。

    他的对手 Hg 则正好相反,Hg 命令行功能很清晰,但是底层 .hg 功能太弱,基本概念照搬 SVN,分支不方便,Rebase 不方便,适合那种一锤定音一次提交就再不用改的人。Git 则是允许用户犯错,错你随便犯,反正都能修回来;代码尽管随手提交,反正后期都能 Interactive Rebase 回来。

    我怕的就是把 Git 当高级版 SVN 用的。
    rockyou12
        26
    rockyou12  
       2019-03-03 20:28:57 +08:00
    @msg7086 最后回一次后面不回了。你说的这些全部都是从个人来考虑,完全不考虑团队。-f 错了,损失的不是 push 错的人的时间,而是整个团队、甚至运维等其他人的时间。

    我 tm 当然知道 git 数据肯定不会丢,你服务器炸了每个人手上都还有个副本呢。但你光看到技术上的东西,管理上的完全给无视了。
    haoz1w0w
        27
    haoz1w0w  
       2019-03-03 21:50:42 +08:00
    马克
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2706 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 12:21 · PVG 20:21 · LAX 04:21 · JFK 07:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.