Git常用命令reset和revert
1、reset
用于回退版本,可以指定退回某一次提交的版本。
checkout 可以撤销工作区的文件,reset 可以撤销工作区/暂存区的文件。
reset 和 checkout 可以作用于 commit 或者文件,revert 只能作用于 commit。
命令格式:
$ git reset [--mixed | --soft | --hard | --merge | --keep] [commit | HEAD]
-
mixed:reset HEAD and index
-
soft:reset only HEAD
-
hard:reset HEAD, index and working tree
-
merge:reset HEAD, index and working tree
-
keep:reset HEAD but keep local changes
将当前的分支重设到指定的 commit 或者 HEAD,如果不显示指定 commit,默认是 HEAD,即最新的一次提交。
并且根据 mode 有可能更新索引和工作目录,mode 的取值可以是 hard、soft、mixed、merged、keep 。默认
为 mixed。
下面将演示每一种的使用方法。
# 使用如下的提交
# master分支
echo a > a.txt
echo b > b.txt
git add a.txt
git add b.txt
git commit -m "add a.txt | add b.txt"
echo c > c.txt
echo d > d.txt
git add c.txt
git add d.txt
git commit -m "add c.txt | add d.txt"
echo e > e.txt
echo f > f.txt
git add e.txt
git add f.txt
git commit -m "add e.txt | add f.txt"
$ git log --oneline
1475123 (HEAD -> master) add e.txt | add f.txt
b41f99c add c.txt | add d.txt
a1397f6 add a.txt | add b.txt
1.1 mixed
# reset HEAD and index
# 将指定commit撤回之后所有内容全部放进工作区中,也就是没有执行git add之前的状态
$ git reset --mixed [commit | HEAD]
# 将提交 b41f99c 恢复,最后的一次提交会被撤销
$ git reset --mixed b41f99c
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
e.txt
f.txt
nothing added to commit but untracked files present (use "git add" to track)
1.2 soft
# reset only HEAD
# 将指定commit撤回之后所有内容全部放进暂存区,也就是执行git add之后的状态
$ git reset --soft [commit | HEAD]
# 将提交 b41f99c 恢复,最后的一次提交会被撤销
$ git reset --soft b41f99c
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: e.txt
new file: f.txt
1.3 hard
# reset HEAD, index and working tree
# 将指定commit撤回并清空工作目录及暂存区所有修改,也就是修改和添加文件之前的状态
$ git reset --hard [commit | HEAD]
# 将提交 b41f99c 恢复,最后的一次提交会被撤销
$ git reset --hard b41f99c
HEAD is now at b41f99c add c.txt | add d.txt
$ git status
On branch master
nothing to commit, working tree clean
$ ls
a.txt b.txt c.txt d.txt
1.4 merge
# reset HEAD, index and working tree
# 暂存区会被删除
# 工作区不会
$ git reset --merge [commit | HEAD]
该选项的作用,把当前分支和指定的 commit 进行合并,规则如下:
-
重置暂存区,任何已经添加到暂存区的改动都将被抛弃
-
如果 commit 和 HEAD 之间有文件存在不同(这个不同指的是文件被删除或者新增),那么将会把该文件重置成
commit 中的状态(新增或删除)。
-
如果 commit 和 HEAD 之间有文件存在不同(这个不同是指文件内容的不同),且此时工作区也存在未提交的改
动,那么本次的 reset 将会被终止。
-
如果一个文件在 commit 和 HEAD 中完全相同,但是它的工作区存与暂存区存在着不同(也就是改动未提交到
暂存区),那么该文件在工作区的改动在重置之后就会被保留。
cat > a.txt << EOF
a1
a2
a3
EOF
git add a.txt
git commit -m "add a.txt"
cat > b.txt << EOF
b1
b2
EOF
git add b.txt
git commit -m "add b.txt"
cat > a.txt << EOF
a1
a2
a3
a4
EOF
cat > c.txt << EOF
c1
c2
EOF
git add c.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: c.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: a.txt
$ git log --oneline
65fc0d0 (HEAD -> master) add b.txt
2b89c2f add a.txt
# 将提交 2b89c2f 恢复,最后的合并会被撤销
$ git reset --merge 2b89c2f
$ ls
a.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: a.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ cat a.txt
a1
a2
a3
a4
1.5 keep
# reset HEAD but keep local changes
# 暂存不会被删除
# 工作区也不会
$ git reset --keep [commit | HEAD]
该选参的作用和 --merge 相似,唯一的区别就是暂存区中被重置的会被保留在工作区中。
cat > a.txt << EOF
a1
a2
a3
EOF
git add a.txt
git commit -m "add a.txt"
cat > b.txt << EOF
b1
b2
EOF
git add b.txt
git commit -m "add b.txt"
cat > a.txt << EOF
a2
a3
EOF
cat > c.txt << EOF
c1
c2
EOF
git add c.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: c.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: a.txt
$ git log --oneline
f6429e8 (HEAD -> master) add b.txt
22a85b3 add a.txt
# 将提交 22a85b3 恢复,最后的合并会被撤销
$ git reset --keep 22a85b3
$ ls
a.txt c.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: a.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ cat a.txt
a2
a3
$ cat c.txt
c1
c2
1.6 其它用法
下面将不演示使用 [–mixed | --soft | --hard | --merge | --keep] 参数,默认使用后 --mixed。
1.6.1 reset指定文件
$ git log --oneline
1475123 (HEAD -> master) add e.txt | add f.txt
b41f99c add c.txt | add d.txt
a1397f6 add a.txt | add b.txt
# 撤回指定文件到工作区,也就是没有执行git add之前的状态
$ git reset [commit | HEAD] file
# 例子
$ git reset b41f99c f.txt
$ ls
a.txt b.txt c.txt d.txt e.txt f.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: f.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
f.txt
$ git reset
# 等价于
$ git reset HEAD
# 上面的操作也可以使用
# 例子
$ git reset HEAD^ f.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: f.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
f.txt
$ ls
a.txt b.txt c.txt d.txt e.txt f.txt
# 回退三个版本
$ git reset --hard HEAD^^^
# 可以使用HEAD~数字,表示版本的次数
$ git reset --hard HEAD~3
1.6.2 从服务器上获取最新的版本历史
# 假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它
git fetch origin
git reset --hard origin/master
1.7 总结
git reset 有很多种用法,它可以被用来移除提交快照,尽管它通常被用来撤销暂存区和工作区的修改。不管是
哪种情况,它应该只被用于本地修改,你永远不应该重设和其他开发者共享的快照。
当你用 reset 回滚到了某个版本后,那么在下一次 git 提交时,之前该版本后面的版本会被作为垃圾删掉。
2、revert
撤销指定的提交内容。
revert 是回滚某个 commit ,不是回滚到某个。git revert是用于反做某一个版本,以达到撤销该版本的修改的目
的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤
销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本
四里会保留版本三的东西,但撤销了版本二的东西。
# 使用如下的提交
# master分支
echo a > a.txt
echo b > b.txt
git add a.txt
git add b.txt
git commit -m "add a.txt | add b.txt"
echo c > c.txt
echo d > d.txt
git add c.txt
git add d.txt
git commit -m "add c.txt | add d.txt"
echo e > e.txt
echo f > f.txt
git add e.txt
git add f.txt
git commit -m "add e.txt | add f.txt"
$ git log --oneline
1475123 (HEAD -> master) add e.txt | add f.txt
b41f99c add c.txt | add d.txt
a1397f6 add a.txt | add b.txt
# 用来撤销指定commit,销后会生成一个新的commit
$ git revert [commit]
# 例子
$ git revert b41f99c
Removing d.txt
Removing c.txt
[master 3671b77] Revert "add c.txt | add d.txt"
2 files changed, 2 deletions(-)
delete mode 100644 c.txt
delete mode 100644 d.txt
$ git log --oneline
3671b77 (HEAD -> master) Revert "add c.txt | add d.txt"
1475123 add e.txt | add f.txt
b41f99c add c.txt | add d.txt
a1397f6 add a.txt | add b.txt
$ ls
a.txt b.txt e.txt f.txt
$ git status
On branch master
nothing to commit, working tree clean
# 生成一个撤销指定提交版本的新提交,执行时不打开默认编辑器,直接使用Git自动生成的提交信息
# 这个和上面的命令没有什么区别,唯一的区别是不会弹出框修改commit信息
$ git revert <commit_id> --no-edit
# 例子
$ git revert b41f99c --no-edit
Removing d.txt
Removing c.txt
[master 727c8f3] Revert "add c.txt | add d.txt"
Date: Fri May 26 10:35:08 2023 +0800
2 files changed, 2 deletions(-)
delete mode 100644 c.txt
delete mode 100644 d.txt
$ git log --oneline
727c8f3 (HEAD -> master) Revert "add c.txt | add d.txt"
1475123 add e.txt | add f.txt
b41f99c add c.txt | add d.txt
a1397f6 add a.txt | add b.txt
# 反做,使用git revert -n 版本号命令,如下我们反做版本号为8b89621的版本
# 这里可能会出现冲突,那么需要手动修改冲突的文件,而且要git add文件名来提交
# 假如上面Revert之后的提交727c8f3我们不想要了,可以执行如下操作
$ git revert -n 727c8f3
$ git log --oneline
727c8f3 (HEAD -> master) Revert "add c.txt | add d.txt"
1475123 add e.txt | add f.txt
b41f99c add c.txt | add d.txt
a1397f6 add a.txt | add b.txt
# 会发现回到了最初的状态,文件都回来了,只是多了commit
$ ls
a.txt b.txt c.txt d.txt e.txt f.txt
# 生成一个撤销最近的一次提交的新提交
$ git revert HEAD
# 生成一个撤销最近一次提交的上一次提交的新提交
$ git revert HEAD^
# 生成一个撤销最近一次提交的上两次提交的新提交
$ git revert HEAD^^
# 生成一个撤销最近一次提交的上n次提交的新提交
$ git revert HEAD~num
git revert 命令用来撤销某个已经提交的快照(和 reset 重置到某个指定版本不一样)。它是在提交记录最后面加上一
个撤销了更改的新提交,而不是从项目历史中移除这个提交,这避免了 Git 丢失项目历史。
撤销(revert)应该用在你想要在项目历史中移除某个提交的时候。比如说,你在追踪一个 bug,然后你发现它是
由一个提交造成的,这时候撤销就很有用。
撤销(revert)被设计为撤销公共提交的安全方式,重设(reset)被设计为重设本地更改。
因为两个命令的目的不同,它们的实现也不一样:重设完全地移除了一堆更改,而撤销保留了原来的更改,用一个
新的提交来实现撤销。千万不要用 git reset 回退已经被推送到公共仓库上的提交,它只适用于回退本地修改(从未
提交到公共仓库中)。如果你需要修复一个公共提交,最好使用 git revert。
发布一个提交之后,你必须假设其他开发者会依赖于它。移除一个其他团队成员在上面继续开发的提交在协作时会
引发严重的问题。当他们试着和你的仓库同步时,他们会发现项目历史的一部分突然消失了。一旦你在重设之后又
增加了新的提交,Git 会认为你的本地历史已经和 origin/master 分叉了,同步你的仓库时的合并提交(merge
commit)会使你的同事困惑。