1. Git 架构

  • Workspace:工作区(当前用户操作修改的区域)
  • Index / Stage:暂存区 (add 后的区域)
  • Repository:仓库区或本地仓库(commit 后的区域)
  • Remote:远程仓库(push 后的区域)

整体过程可以简述为:

  • 工作区–>add–>暂存区–>commit–>本地仓库区–>push–>远程仓库区
  • 远程仓库区–>fetch–>使用 refs\remotes 下对应分支文件记录远程分支末端 commit_id 和 本地仓库区 -->merge–>工作区
  • 远程仓库区–>pull–>使用 refs\remotes 下对应分支文件记录远程分支末端 commit_id and 本地仓库区 and 工作区

具体的 git 的组成部分和概念命令,请移步下述两个博客(超链接):
Git 技术干货!工作中 Git 的使用实践和常用命令合集!
Git - 使用 git 不知道内部实现机制怎么行

新建一个 Git 仓库,在 Git 仓库中可逻辑上划分三个区域——工作目录、暂存区、版本库,这三个区域是抽象的,Git 仓库中以文件来记录各个区域的内容。

Git 会追踪工作目录里文件及内容的变更。当我们在 Git 仓库的工作目录下新建三个文件,Git 会将新建的文件视作变更(图中五角星表示被 Git 检测到更改)。

我们想将 html 文件提交到仓库中,首先将变更(新建的 HTML )从工作目录添加到暂存区。然后将这个文件从暂存区提交到版本库。这时才算拍摄了一组快照,提交的更改不会丢失。

当我们在工作目录下修改了一个文件, Git 仓库会识别文件修改部分(标记 2 颗星),然后我们将文件修改添加到暂存区,最后将文件修改提交到版本库。

修改 CSS 文件,将修改添加到暂存区,再提交到仓库。

问题:如果我们在工作目录下第一次修改一个文件,并将它添加到暂存区,然后我们第二次修改文件,第二次修改没有添加到暂存区,最后我们将文件 commit 到版本库,请问仓库的文件内容是第几次修改的?请思考。

如果第一次修改 CSS 文件,添加到暂存区,再第二次修改 CSS 文件,现在执行 commit,提交到版本库只有第一次修改的内容。


当执行 commit 时,只有暂存区中的内容会被提交到版本库。

如果我们想要将第二次修改也 commit 到版本库,我们必须先将第二次修改先添加到暂存区,第一次修改的暂存与第二次修改的暂存合并,然后执行 commit,仓库中就会有第一次和第二次修改的内容。


添加到暂存区的内容会合并,一起提交到仓库。

第一次修改 CSS 文件,添加到暂存区,但没有提交仓库;第二次修改 CSS 文件,添加到暂存区,会与第一次的修改合并,这时提交到版本库的内容是两次修改的合并。

2. 实践

假设项目存在这么一个提交记录:

$ git log
commit commit_id4 (HEAD -> master)
Author: test
Date:   Thu Aug 20 16:28:45 2020 +0800第三次修改 README 文件
commit commit_id3 (HEAD -> master)
Author: test
Date:   Thu Aug 20 16:28:45 2020 +0800第二次修改 README 文件
commit commit_id2
Author: test
Date:   Thu Aug 20 16:28:19 2020 +0800第一次修改 README 文件
commit commit_id1
Author: test
Date:   Thu Aug 20 16:26:59 2020 +080初始化项目

提交顺序为:commit_id1 --> commit_id2 --> commit_id3 --> commit_id4
注意:在 git 中每次的 commit 都有一个 commit id 唯一标识当前的提交!

下面,我们先来解决小明的这个问题,使用git reset即可完美解决~

3. 问题解决

洋仔:小明,你的这个就可以用git reset 这个命令来完美的搞定,下面我们看一下如何解决

1、获取当前提交的 commit id
命令:git log
获取到当前项目分支下的所有 commit 记录;
假设上述小明提交错误的 commit id 为commit id:commit_id4这一次提交;
他的上一次提交就是commit id:commit_id3 ,我们要将修改回滚到commit_id3的时刻!

小明:我想要把我刚才 commit 的修改保留下来,我修改的代码不能给我删除掉呀!
洋仔:没问题

2、将某个 commit id 前的 commit 清除,并保留修改的代码
命令:git reset <commit_id> 当前场景下就是:git reset commit_id3
将指定 commit_id 后的所有提交,都去除,并保留修改的代码在本地的区域,也就是Workspace

小明:啊哈,这样的话我就可以把错误代码修改后再提交了; 但是我已经 push 到线上仓库的数据怎么办呢?
洋仔:别急,有办法~

3、修改代码完成后,将修改好的代码 add 到暂存区,并提交到本地仓库中
命令:git add <file_name> and git commit
当前场景下:git add . and git commit 将最新修改后的代码 commit 则提交后的提交记录假设如下:

可以看到,我们错误提交的commit_id4提交记录消失,取而代之的是我们更新代码后提交的记录commit_id5; 这样就完成了本地的代码修改和更新

$ git log
commit commit_id5 (HEAD -> master)
Author: test
Date:   Thu Aug 20 16:28:45 2020 +0800第三次修改 README 文件-更新错误后提交
commit commit_id3 (HEAD -> master)
Author: test
Date:   Thu Aug 20 16:28:45 2020 +0800第二次修改 README 文件
commit commit_id2
Author: test
Date:   Thu Aug 20 16:28:19 2020 +0800第一次修改 README 文件
commit commit_id1
Author: test
Date:   Thu Aug 20 16:26:59 2020 +080初始化项目

整体流程如下:

git log
git reset commit_id3
修改代码
git add .
git commit -m '第三次修改 README 文件-更新错误后提交'

洋仔:好了,小明,你的问题完美解决了
小明:哦吼,但是我还有一个问题: 如果我想要不保留回滚 commit 的修改,直接删除掉修改!该怎么处理呢?
洋仔:简单~ 我们整体看一下 git reset 命令

3.1 后悔药 git reset

在进行下面的讲解是,还是先假设有这么一个提交链:
commit_id1 --> commit_id2 --> commit_id3 --> commit_id4

git reset <param> commit_id2

reset 是将 HEAD 重新定位到commit_id2上,对于 commit_id3commit_id4 和本地当前的修改,对于不同的参数 param,会有不同的处理:

reset 命令有三种处理模式:

  • –soft:保留 commit 修改,将修改存储到 index 中;也就是说 git add 后的区域
  • –mixed:保留 commit 修改,将修改存储到本地工作区域中;也就是说 git add 前的区域
  • –hard:删除 commit 修改,慎用!
  1. git reset --soft
    回滚 commit_id 前的所有提交,不删除修改:
    git reset --soft commit_id
    重设 head,不动 index,所以效果是 commit_id 之后的 commit 修改全部在 index 中 将 id3 和 id4 的修改放到 index 区(暂存区),也就是 add 后文件存放的区域,本地当前的修改保留

    git reset --soft SHA 会将对应的 commit 更改回退到暂存区:

  2. git reset --mixed
    回滚 commit_id 前的所有提交,不删除修改:git reset commit_id 等同于 git reset --mixed commit_id 与下述的 git reset --hard commit_id 效果不同。
    重设 head 和 index,不重设 work tree,效果就是 commit_id 之前的修改,全部在 work tree 中,为还未 add 的状态 将 id3 和 id4 的所有修改放到本地工作区中,本地当前的修改保留。

    git reset --mixed SHA 会将对应的 commit 更改回退到工作目录:

  3. git reset --hard
    回滚 commit_id 前的所有提交,将修改全部删除:git reset --hard commit_id
    重设 head、index、work tree,也就是说将当前项目的状态恢复到 commit_id 的状态,其余的全部删除(包含 commit_id 后的提交和本地还未提交的修改) 慎用!!

    git reset --hard SHA 会将对应的 commit 直接删除,无法恢复。

    git reset --hard commit_id 该命令主要是用于代码回退,仅对已经 commit 到本地的代码有效。通过 git reflog 命令查看之前版本 id 信息。

    wohu@ubuntu-dev:~/git_demo/test_demo$ git reflog
    eda37f0 HEAD@{0}: commit: third commit
    8d2efe6 HEAD@{1}: commit: second commit
    14b5e12 HEAD@{2}: commit (initial): first commit
    wohu@ubuntu-dev:~/git_demo/test_demo$ cat README.md
    - first commit
    - second commit
    - third commit
    wohu@ubuntu-dev:~/git_demo/test_demo$
    

    想再次切回到注释为 second commit 版本下,可以再次通过 git reset --hard 8d2efe6 进行版本切换。

    wohu@ubuntu-dev:~/git_demo/test_demo$ git reset --hard 8d2efe6
    HEAD is now at 8d2efe6 second commit
    wohu@ubuntu-dev:~/git_demo/test_demo$ ls
    README.md
    wohu@ubuntu-dev:~/git_demo/test_demo$ cat README.md
    - first commit
    - second commitwohu@ubuntu-dev:~/git_demo/test_demo$
    

3.2 后悔药 git revert

小明:原来 git reset 这么强大呀! 但是我这还有个问题:

如果想要只操作修改中间的一个 commit,不对其他的 commit 产生影响; 也就是类似于我们只修改 commit_id2,而对 commit_id3 和 commit_id4 无影响,该怎么处理呢?

洋仔:(这么多问题,幸亏我懂,要不这次就丢大了。。) 简单! git revert 命令!

适用场景: 在项目开发中,突然发现在前几次的提交中,有一次提交中包含一个 bug; 当然我们可以进行一个新的修改,然后再提交一次; 但是,不优雅哈哈; 我们可以直接重做有 bug 的 commit~

为什么不直接去再添加一个 commit 呢?
git revert 是用于“反做”某一个版本,以达到撤销该版本的修改的目的。
比如,我们 commit 了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有 bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西;

在 revert 命令中常用的就两个:

  • git revert -e <commit_id>:重做指定 commit 的提交信息
  • git revert -n <commit_id>:重做执行 commit 的代码修改
  1. git revert -e
    重做 commit_id 的提交信息,生成为一个新的 new_commit_id
    git revert -e commit_id

  2. git revert -n
    重做 commit_id 的提交git revert -n commit_id将 commit_id 中修改,放到 index 区,我们可以对他重新做修改并重新提交

git revert <commitHash>  回退指定提交记录git revert --no-commit <commitHash>
回退指定提交记录 --no-commit 不会自动提交成一条 `commit` 后续手动操作git revert --no-commit <commitHash1> <commitHash2>
回退多个提交记录git revert --no-commit <commitHash1>..<commitHashN>
回退一定范围的提交记录,前开后闭(不包含 1,包含 N)

3.3 revert vs reset

  • git revert 是用一次新的 commit 来回滚之前的 commit,此次提交之前的 commit 都会被保留不动;
  • git reset 是回到某次提交,提交及之前的 commit 都会被保留,但是此 commit id 之后的修改都会被删除或放回工作区等待下一次提交;

小明:还有这种操作,可以直接单独操作提交过程中的某一个 commit! 太棒了!

4. 后悔药git checkout

小明:还有最后一个问题:
如果我在一次开发中,发现某个文件修改错误了,想要将文件恢复到刚 pull 代码时的状态怎么办呢?
洋仔:简单! 看 git checkout 解决这个问题!

我们知道使用git checkout可以

  • git checkout <branch_name>切换分支

  • git checkout -b <branch_bame>创建分支等操作

它还有回滚指定文件的修改的功能,命令:git checkout -- <file_name>
上述语句的作用,就是将 file_name 的本地工作区的修改全部撤销,有两种情况:

  • 如果 file_name 在 commit 后没有 add 过这个文件,则撤销到版本库中的状态

  • 如果 file_name 在 commit 后 add 过这个文件,则撤销到暂存区的状态,也就是 add 后的状态

总之,就是让指定的文件回滚到最近的一次git add 或者 git commit时的状态!

git checkout --file 该命令是对未提交到缓存区的代码进行撤销。创建空文件 Readme.md。通过 git status 查看文件在工作区的状态。

wohu@ubuntu-dev:~/git_demo/test_demo$ echo "hello world" >> Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified:   Readme.mdno changes added to commit (use "git add" and/or "git commit -a")
wohu@ubuntu-dev:~/git_demo/test_demo$

然后执行 git checkout -- Readme.md。我们又回复到了修改前的版本。

wohu@ubuntu-dev:~/git_demo/test_demo$ cat Readme.md
hello world
wohu@ubuntu-dev:~/git_demo/test_demo$ git checkout -- Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$ cat Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$

我们再次对 Readme 内容进行编辑,并将其放入到缓存区中。

wohu@ubuntu-dev:~/git_demo/test_demo$ echo "hello world" >> Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$ git add Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:(use "git reset HEAD <file>..." to unstage)modified:   Readme.mdwohu@ubuntu-dev:~/git_demo/test_demo$ git checkout -- Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:(use "git reset HEAD <file>..." to unstage)modified:   Readme.mdwohu@ubuntu-dev:~/git_demo/test_demo$

此时执行 git checkout 没有啥效果,如果想恢复就要用到我们接下来介绍的命令。

git reset HEAD -- file 该命令是将放入暂存区的代码进行撤销,放入到工作区中。

wohu@ubuntu-dev:~/git_demo/test_demo$ git reset HEAD -- Readme.md
Unstaged changes after reset:
M   Readme.md
wohu@ubuntu-dev:~/git_demo/test_demo$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified:   Readme.mdno changes added to commit (use "git add" and/or "git commit -a")
wohu@ubuntu-dev:~/git_demo/test_demo$

这样我们的 Readme.md 代码又再次回到了工作区。再次进行 git checkout 将代码内容进行恢复。

参考:
https://gitbook.cn/books/5f4db870237b0e7d7f238fa0/index.html
https://gitbook.cn/books/5ebca21b9b907c12334b6287/index.html

Git 错误提交后该如何回滚操作相关推荐

  1. git错误提交怎么整?强行回滚最为致命

    首先将本地分支重置到你错误提交前的分支 git reset --hard 要重置到的commitId 其次强制提交本地的代码到远程 git push origin master --force 如果发 ...

  2. Git:撤销回滚操作

    Git撤销&回滚操作(git reset 和 get revert) git的工作流 工作区:即自己当前分支所修改的代码,git add xx 之前的!不包括 git add xx 和 git ...

  3. 在idea中纯界面进行git回滚操作

    在idea中git回滚操作 红色:工作区 绿色:缓存区 白色:已提交(本地版本库或者远程版本库) 通常我们都是在缓存区中写代码 第一步:reset到回滚的版本 找到需要回滚的版本,将代码重置到需要回滚 ...

  4. git 常规操作以及回滚操作

    2019独角兽企业重金招聘Python工程师标准>>> 在这里记录一些平时比较常用的git的命令行操作,以及一些回滚的操作,以提高我们平时的开发效率. 首先我的 git 项目仓库在 ...

  5. [20131014] 出现错误,显示事务没有回滚

    [20131014] 出现错误,显示事务没有回滚 测试代码: SET XACT_ABORT OFF goBEGIN TRANBEGIN TRYDELETE dbo.T SELECT * FROM [不 ...

  6. 1.2 git回滚操作

    1.远程仓库中有的文件,但是本地要给删除掉,如何同步 git ls-files //查看暂存区是否有该文件,以readme.md为例 git rm -r --cached readme.md //删除 ...

  7. Team Foundation Server的回滚操作

    VSTF Rollback 操作 最近遇到要把有些项目需要做回滚操作,发现TFS的UI上没有回滚的操作. 经过百度,查到一个CSDN上的博主发了一种方法,经过验证,那种方法是错误的: 他通过先获取指定 ...

  8. sqlsever回滚操作_SqlServer 事务回滚(1)

    SQL事务 一.事务概念 事务是一种机制.是一种操作序列,它包含了一组数据库操作命令,这组命令要么全部执行,要么全部不执行.因此事务是一个不可分割的工作逻辑单元.在数据库系统上执行并发操作时事务是作为 ...

  9. javaweb中mysql数据库的回滚操作代码

    2019独角兽企业重金招聘Python工程师标准>>> 在mysql中创建用户账户数据库(注意,count不能为负数,要设置无符号型) 添加数据 下面我们得到connection对象 ...

最新文章

  1. 老司机做VR视频,需要什么样的全景相机?
  2. 黑客们的故事(连载三) 因为好奇
  3. 常见的网站布局方式---左固定右自适应、左右固定中自适应等
  4. 容器和 批量删除 镜像_更高更快更稳,看阿里巴巴如何修炼容器服务「内外功」...
  5. ros开发增加clion常用模板及初始化配置(三)
  6. 使用kubeadm搭建的k8s集群修改node节点主机名
  7. 学习Python的第七天
  8. oracle联合分组查询,Oracle分组查询
  9. 你要知道的N个Android适配问题
  10. Ubuntu18.04安装分析
  11. MacOS图标文件 .icns 一键生成脚本
  12. 麦克风产品 麦克风元器件 市场调研
  13. FPGA资源类型简介
  14. 大学计算机python期末考试_大学计算机程序设计(Python)_章节测验,期末考试,慕课答案查询公众号...
  15. 树莓派(raspberry pi)日记1之个人网站的构建(localhost内网穿透实现公网可以访问)
  16. nkd 编译ffmpeg错误: clang is unable to create an executable file. C compiler test failed.
  17. CDN和CDN加速有什么关联
  18. html布局方式有哪些,HTML几种布局方式-HTML教程第十六讲
  19. 苹果手机几月份最便宜_1亿像素最便宜手机, 不是小米10, 网友: 这款手机拍照不错...
  20. 利用OSG实现模拟飞机尾迹-粒子系统

热门文章

  1. 2021-2027年中国透光立体玻璃行业市场深度分析及前瞻研究报告
  2. 2022-2028年中国干洗行业市场全景调研与投资前景预测报告
  3. Docker入门六部曲——服务
  4. 如何在本地访问远程服务器的jupyter notebook?
  5. 初窥CSRF攻击方式以及Flask-WTF
  6. MySQL解压版安装
  7. Apache POI:解决数据库和Excel之间相互转换的烦恼~
  8. Camera噪声问题
  9. 转置卷积Transposed Convolution
  10. 2021年大数据Spark(五十一):Structured Streaming 物联网设备数据分析