参考:
https://blog.csdn.net/yxlshk/article/details/79944535
https://www.cnblogs.com/0616--ataozhijia/p/3709917.html
https://blog.csdn.net/rebeccachong/article/details/39379703
https://blog.csdn.net/weixin_44781409/article/details/107560533
https://www.jianshu.com/p/c2ec5f06cf1a
git reset、revert、rebase
Git恢复之前版本的两种方法reset、revert(图文详解)
一、问题描述
在利用github实现多人合作程序开发的过程中,我们有时会出现错误提交的情况,此时我们希望能撤销提交操作,让程序回到提交前的样子,本文总结了两种解决方法:回退(reset)、反做(revert)。
二、背景知识
git的版本管理,及HEAD的理解
使用git的每次提交,Git都会自动把它们串成一条时间线,这条时间线就是一个分支。如果没有新建分支,那么只有一条时间线,即只有一个分支,在Git里,这个分支叫主分支,即master分支。有一个HEAD指针指向当前分支(只有一个分支的情况下会指向master,而master是指向最新提交)。每个版本都会有自己的版本信息,如特有的版本号、版本名等。如下图,假设只有一个分支:
三、解决方法
方法一:git reset
原理: git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本,如下图所示,假设我们要回退到版本一:
适用场景: 如果想恢复到之前某个提交的版本,且那个版本之后提交的版本我们都不要了,就可以用这种方法。
具体操作:
1. 查看版本号:
可以使用命令“git log”查看:
也可以在github网站上查看:
2. 使用“git reset --hard 目标版本号”命令将版本回退:
再用“git log”查看版本信息,此时本地的HEAD已经指向之前的版本:
3. 使用“git push -f”提交更改:
此时如果用“git push”会报错,因为我们本地库HEAD指向的版本比远程库的要旧:
所以我们要用“git push -f”强制推上去,就可以了:
在github图形化界面上看,远程库的HEAD也已经指向目标版本:
回退成功!
方法二:git revert
原理: git revert是用于“反做”某一个版本,以达到撤销该版本的修改的目的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。如下图所示:
适用场景: 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。
具体操作:
举个例子,现在库里面有三个文件:READ.md、text.txt、text2.txt。
1. 查看版本号:
可以通过命令行查看(输入git log):
如图,最近的两个版本分别叫:“add text.txt”(即新增了文件text.txt)、“add text2.txt”(新增了文件text2.txt)。这个时候我们不需要text.txt这个文件了,那就是说不想要“add text.txt”那个版本的操作,那可以通过反做“add text.txt”这个版本来实现。
也可以通过github网站图形化界面查看版本号:
2.使用“git revert -n 版本号”反做,并使用“git commit -m 版本名”提交:
(1)反做,使用“git revert -n 版本号”命令。如下命令,我们反做版本号为8b89621的版本:
git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861
注意: 这里可能会出现冲突,那么需要手动修改冲突的文件。而且要git add 文件名。
(2)提交,使用“git commit -m 版本名”,如:
git commit -m "revert add text.txt"
此时可以用“git log”查看本地的版本信息,可见多生成了一个新的版本,该版本反做了“add text.txt”版本,但是保留了“add text2.txt”版本:
3.使用“git push”推上远程库:
git push
查看github上显示的远程库版本信息:
此时查看仓库的文件,剩下两个:READ.md、text2.txt
反做成功!
git revert 用法
git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销作为一次最新的提交
* git revert HEAD 撤销前一次 commit
* git revert HEAD^ 撤销前前一次 commit
* git revert commit (比如:fa042ce57ebbe5bb9c8db709f719cec2c58ee7ff)撤销指定的版本,撤销也会作为一次提交进行保存。
git revert是提交一个新的版本,将需要revert的版本的内容再反向修改回去,版本会递增,不影响之前提交的内容
--------------------------------------------------------------------------------------------------------------------------------------------
git revert 和 git reset的区别
1. git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。
2. 在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别。因为git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现,但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。
3. git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。
git reset 、rebase和 revert的区别
git revert 放弃某次提交
Git恢复之前版本的三种方法reset、revert、rebase 及其他操作
一. 问题描述
在利用github实现多人合作程序开发的过程中,我们有时会出现错误提交的情况,此时我们希望能撤销提交操作,让程序回到提交前的样子,本文总结了三种解决方法:回退(reset)、反做(revert)、回扣(rebase)。
二. 解决方案
方法一:git reset
原理: git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本
具体实现:
-
查看版本号 使用命令“git log”查看:
-
使用“git reset --hard 目标版本号”命令将版本回退:
-
再用“git log”查看版本信息,此时本地的HEAD已经指向之前的版本:
-
使用“git push -f”提交到远程更改
注意: 此时如果用“git push”会报错,因为我们本地库HEAD指向的版本比远程库的要旧
方法二:git revert
原理: git revert是用于“反做”某一个版本,以达到撤销该版本的修改的目的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。
适用场景: 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。
具体实现:
1.查看 使用命令“git log”查看commit提交记录
2.使用“git revert 版本号”反做,并使用“git commit -m 版本名”提交
3.使用“git push”推上远程库
方法三:git rebase -i (commit-id) 或 git rebase -i HEAD~(回退几步)
1.查看 使用命令“git log”查看commit提交记录
2.git rebase -i (commit-id) 选择回退版本之前的一次commit版本
3.编辑文件命令行输入 a,切换到编辑模式 将要删除的commit之前的 pick 单词改为drop
4.按下Esc(退出)键 命令行输入 :wq 保存文件退出
5.使用“git push -f”提交到远程更改
6.在使用 git rebase -i (commit-id) 过程中若撤销回退可以使用 git rebase --abort 撤回
若在 git rebase后出现(xxx|REBASE-i)这种情况
原因:用git推送数据到仓库的时候会出现推送失败
解决方案: 使用git rebase --abort 代码回退 回到git rebase之前的状态
其他操作
一. 当代码库远程迁移后,修改本地代码关联到远程地址
1.迁移前先查看当前代码库的远程地址
git remote -v 查看远程地址
2.修改本地代码关联到的远程地址
git remote set-url origin [远程地址]
3.查看当前代码库的远程地址是否修改成功
git remote -v 查看远程地址
4.将本地的master分支推送到origin主机
①指定origin为默认主机 ,后面就可以不加任何参数使用git push
git push -u origin master
② 提交本地test分支 作为 远程的master分支 ,不指定默认主机
git push origin test:master
Git Reset 三种模式
有时候,我们用Git的时候有可能commit提交代码后,发现这一次commit的内容是有错误的,那么有两种处理方法:
1、修改错误内容,再次commit一次 2、使用git reset 命令撤销这一次错误的commit
第一种方法比较直接,但会多次一次commit记录。
而我个人更倾向第二种方法,错误的commit没必要保留下来。
那么今天来说一下git reset。它的一句话概括
git-reset - Reset current HEAD to the specified state
意思就是可以让HEAD这个指针指向其他的地方。例如我们有一次commit不是不是很满意,需要回到上一次的Commit里面。那么这个时候就需要通过reset,把HEAD指针指向上一次的commit的点。
它有三种模式,soft,mixed,hard,具体的使用方法下面这张图,展示的很全面了。
这三个模式理解了,对于使用这个命令很有帮助。在理解这三个模式之前,需要略微知道一点Git的基本流程。正如上图,Git会有三个区域:
- Working Tree 当前的工作区域
- Index/Stage 暂存区域,和git stash命令暂存的地方不一样。使用git add xx,就可以将xx添加近Stage里面
- Repository 提交的历史,即使用git commit提交后的结果
以下简单敘述一下把文件存入Repository流程:
-
刚开始 working tree 、 index 与 repository(HEAD)里面的內容都是一致的
-
当git管理的文件夹里面的内容出现改变后,此時 working tree 的內容就会跟 index 及 repository(HEAD)的不一致,而Git知道是哪些文件(Tracked File)被改动过,直接将文件状态设置为 modified (Unstaged files)。
-
当我們执行 git add 后,会将这些改变的文件內容加入 index 中 (Staged files),所以此时working tree跟index的內容是一致的,但他们与repository(HEAD)內容不一致。
-
接着执行 git commit 後,將Git索引中所有改变的文件內容提交至 Repository 中,建立出新的 commit 节点(HEAD)后, working tree 、 index 與与repository(HEAD)区域的内容 又会保持一致。
实战演示
reset --hard:重置stage区和工作目录:
reset --hard 会在重置 HEAD 和branch的同时,重置stage区和工作目录里的内容。当你在 reset 后面加了 --hard参数时,你的stage区和工作目录里的内容会被完全重置为和HEAD的新位置相同的内容。换句话说,就是你的没有commit的修改会被全部擦掉。
例如你在上次 commit 之后又对文件做了一些改动:把修改后的ganmes.txt文件add到stage区,修改后的shopping list.txt保留在工作目录
git status
然后,你执行了reset并附上了--hard参数:
git reset --hard HEAD^
你的 HEAD 和当前 branch 切到上一条commit 的同时,你工作目录里的新改动和已经add到stage区的新改动也一起全都消失了:
git status
可以看到,在 reset --hard 后,所有的改动都被擦掉了。
reset --soft:保留工作目录,并把重置 HEAD 所带来的新的差异放进暂存区
reset --soft 会在重置 HEAD 和 branch 时,保留工作目录和暂存区中的内容,并把重置 HEAD 所带来的新的差异放进暂存区。
什么是「重置 HEAD 所带来的新的差异」?就是这里:
由于 HEAD 从 4 移动到了 3,而且在 reset 的过程中工作目录和暂存区的内容没有被清理掉,所以 4 中的改动在 reset 后就也成了工作目录新增的「工作目录和 HEAD 的差异」。这就是上面一段中所说的「重置 HEAD 所带来的差异」。
此模式下会保留 working tree工作目录的內容,不会改变到目前所有的git管理的文件夹的內容;也会
保留 index暂存区的內容,让 index 暂存区与 working tree 工作目录的內容是一致的。就只有 repository 中的內容的变更需要与 reset 目标节点一致,因此原始节点与reset节点之间的差异变更集合会存在与index暂存区中(Staged files),所以我们可以直接执行 git commit 將 index暂存区中的內容提交至 repository 中。当我们想合并「当前节点」与「reset目标节点」之间不具太大意义的 commit 记录(可能是阶段性地频繁提交)時,可以考虑使用 Soft Reset 来让 commit 演进线图较为清晰点。
所以在同样的情况下,还是老样子:把修改后的ganmes.txt文件add到stage区,修改后的shopping list.txt保留在工作目录
git status
假设此时当前 commit 的改动内容是新增了 laughters.txt 文件:
git show --stat
如果这时你执行:
git reset --soft HEAD^
那么除了 HEAD 和它所指向的 branch1 被移动到 HEAD^ 之外,原先 HEAD 处 commit 的改动(也就是那个 laughters.txt 文件)也会被放进暂存区:
git status
这就是--soft 和 --hard 的区别:--hard 会清空工作目录和暂存区的改动,*而 --soft则会保留工作目录的内容,并把因为保留工作目录内容所带来的新的文件差异放进暂存区。
reset 不加参数(mixed):保留工作目录,并清空暂存区
reset 如果不加参数,那么默认使用 --mixed 参数。它的行为是:保留工作目录,并且清空暂存区。也就是说,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录。简而言之,就是「把所有差异都混合(mixed)放在工作目录中」。
还以同样的情况为例:
git status
修改了 的games.txt 和 shopping list.txt,并把 games.txt 放进了暂存区。
git show --stat
最新的 commit 中新增了 laughters.txt 文件。
这时如果你执行无参数的reset或者带--mixed参数:
git reset HEAD^
git reset --mixed HEAD^
工作目录的内容和 --soft 一样会被保留,但和 --soft 的区别在于,它会把暂存区清空,并把原节点和reset节点的差异的文件放在工作目录,总而言之就是,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录
git status
总结
reset 的本质:移动 HEAD 以及它所指向的 branch
实质上,reset 这个指令虽然可以用来撤销 commit ,但它的实质行为并不是撤销,而是移动 HEAD ,并且「捎带」上 HEAD 所指向的 branch(如果有的话)。也就是说,reset 这个指令的行为其实和它的字面意思 "reset"(重置)十分相符:它是用来重置 HEAD 以及它所指向的 branch 的位置的。
而 reset --hard HEAD^ 之所以起到了撤销 commit 的效果,是因为它把 HEAD 和它所指向的 branch 一起移动到了当前 commit 的父 commit 上,从而起到了「撤销」的效果:
Git 的历史只能往回看,不能向未来看,所以把 HEAD 和 branch 往回移动,就能起到撤回 commit 的效果。
所以同理,reset --hard 不仅可以撤销提交,还可以用来把 HEAD 和 branch 移动到其他的任何地方。
reset三种模式区别和使用场景
区别:
-
--hard:重置位置的同时,直接将 working Tree工作目录、 index 暂存区及 repository 都重置成目标Reset节点的內容,所以效果看起来等同于清空暂存区和工作区。
-
--soft:重置位置的同时,保留working Tree工作目录和index暂存区的内容,只让repository中的内容和 reset 目标节点保持一致,因此原节点和reset节点之间的【差异变更集】会放入index暂存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和Reset节点之间的所有差异都会放到暂存区中。
-
--mixed(默认):重置位置的同时,只保留Working Tree工作目录的內容,但会将 Index暂存区 和 Repository 中的內容更改和reset目标节点一致,因此原节点和Reset节点之间的【差异变更集】会放入Working Tree工作目录中。所以效果看起来就是原节点和Reset节点之间的所有差异都会放到工作目录中。
使用场景:
-
--hard:(1) 要放弃目前本地的所有改变時,即去掉所有add到暂存区的文件和工作区的文件,可以执行 git reset -hard HEAD 来强制恢复git管理的文件夹的內容及状态;(2) 真的想抛弃目标节点后的所有commit(可能觉得目标节点到原节点之间的commit提交都是错了,之前所有的commit有问题)。
-
--soft:原节点和reset节点之间的【差异变更集】会放入index暂存区中(Staged files),所以假如我们之前工作目录没有改过任何文件,也没add到暂存区,那么使用reset --soft后,我们可以直接执行 git commit 將 index暂存区中的內容提交至 repository 中。为什么要这样呢?这样做的使用场景是:假如我们想合并「当前节点」与「reset目标节点」之间不具太大意义的 commit 记录(可能是阶段性地频繁提交,就是开发一个功能的时候,改或者增加一个文件的时候就commit,这样做导致一个完整的功能可能会好多个commit点,这时假如你需要把这些commit整合成一个commit的时候)時,可以考虑使用reset --soft来让 commit 演进线图较为清晰。总而言之,可以使用--soft合并commit节点。
-
--mixed(默认):(1)使用完reset --mixed后,我們可以直接执行 git add 将這些改变果的文件內容加入 index 暂存区中,再执行 git commit 将 Index暂存区 中的內容提交至Repository中,这样一样可以达到合并commit节点的效果(与上面--soft合并commit节点差不多,只是多了git add添加到暂存区的操作);(2)移除所有Index暂存区中准备要提交的文件(Staged files),我们可以执行 git reset HEAD 来 Unstage 所有已列入 Index暂存区 的待提交的文件。(有时候发现add错文件到暂存区,就可以使用命令)。(3)commit提交某些错误代码,或者没有必要的文件也被commit上去,不想再修改错误再commit(因为会留下一个错误commit点),可以回退到正确的commit点上,然后所有原节点和reset节点之间差异会返回工作目录,假如有个没必要的文件的话就可以直接删除了,再commit上去就OK了。
假如手贱,又想回退撤销的版本呢?
请看另外一篇文章:TODO
参考文章:
https://dotblogs.com.tw/wasichris/2016/04/29/225157
https://www.domon.cn/2018/09/06/Git-reset-used-in-coding/
https://juejin.im/book/5a124b29f265da431d3c472e/section/5a14529bf265da43310d7351(掘金小册)
作者:carway
链接:https://www.jianshu.com/p/c2ec5f06cf1a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
reset
git reset [--soft | --mixed | --hard
上面常见三种类型
--mixed
会保留源码,只是将git commit和index 信息回退到了某个版本.
git reset 默认是 --mixed 模式
git reset --mixed 等价于 git reset
--soft
保留源码,只回退到commit 信息到某个版本.不涉及index的回退,如果还需要提交,直接commit即可.
--hard
源码也会回退到某个版本,commit和index 都回回退到某个版本.(注意,这种方式是改变本地代码仓库源码)