git提交修改记录的工作原理

git里存在三个区域,分别是工作区、暂存区和本地库。git的工作原理如下图所示:
在这里插入图片描述
以下简单敘述一下上图中把文件提交到git本地库(Repository)流程:刚开始工作区、缓存区与git本地库(Repository)里面的內容都是一致的。

  1. 当git管理的文件夹里面的内容出现改变后,此时工作区的內容就会发生变化,而缓存区与git本地库(Repository)里面的內容此时是不发生改变的,而Git知道是哪些文件(Tracked File)被改动过,直接将文件状态设置为 modified (Unstaged files)。
  2. 当我們执行 git add 后,会将这些改变的文件內容加入缓存区 (Staged files),所以此时工作区和缓存区的內容是一致的,但它们与指向it本地库(Repository)的HEAD內容不一致。
  3. 接着执行 git commit 后,将git索引中所有改变的文件內容提交至 git本地库(Repository)中,建立出新的 commit 节点(HEAD)后,工作区、暂存区和本地库(Repository)HEAD区域的内容会保持一致。

git reset

git reset用于撤销未被提交到远端的改动(commit)。已经push到远程仓库的commit不允许reset,因为git reset是会丢弃掉一些commit的。如果commit已经被push到远程仓库上了,也就意味着其他开发人员就可能基于这个commit形成了新的commit,这时你去reset,就会造成其他开发人员的提交历史莫名其妙的丢失,后果严重。

reset 的本质是移动 HEAD 以及它所指向的 branch

实质上,reset 这个指令虽然可以用来撤销 commit ,但它的实质行为并不是撤销,而是移动 HEAD ,并且「捎带」上了 HEAD 所指向的 branch(如果有的话)。也就是说,reset 这个指令的行为其实和它的字面意思 “reset”(重置)十分相符:它是用来重置 HEAD 以及它所指向的 branch 的位置的。
而 git reset --hard HEAD^ 之所以起到了撤销 commit 的效果,是因为它把 HEAD 和它所指向的 branch 一起移动到了当前 commit 的父 commit 上,从而起到了「撤销」的效果:
在这里插入图片描述
所以同理,git reset --hard 不仅可以撤销提交,还可以用来把 HEAD 和 branch 移动到其他的任何地方。
在这里插入图片描述

不带文件参数的reset

git reset配合不同的参数,对工作区、暂存区和本地库(Repository)这三个区域会产生不同的影响。

reset三种模式区别:

git reset --hard HEAD:重置位置的同时,直接将工作区、缓存区及本地库(Repository)都重置成目标节点的內容,所以效果看起来等同于清空暂存区和工作区。

git reset --soft HEAD :重置位置的同时,保留工作区工作目录和缓存区的内容,只让本地库(Repository)中的内容和 reset 目标节点保持一致,因此原节点和reset节点之间的【差异变更集】会放入缓存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和Reset节点之间的所有差异都会放到暂存区中。

git reset --mixed HEAD:–mixed默认参数,重置位置的同时,只保留工作区內容,会将缓存区和本地库(Repository)中的內容更改和reset目标节点一致,因此原节点和Reset节点之间的【差异变更集】会放入工作目录中。所以效果看起来就是原节点和Reset节点之间的所有差异都放到工作目录中。

reset三种模式的使用场景:

--hard

  1. 要放弃目前本地的所有改变时,即去掉所有add到缓存区的文件和工作区的文件,可以执行 git reset --hard HEAD 来强制恢复git管理的文件夹的內容及状态;
  2. 在不影响其他人的前提下,真的想抛弃目标节点后的所有commit(可能觉得目标节点到原节点之间的commit提交都是错了,之前所有的commit有问题)。

--soft

  1. 使用–soft合并commit节点,这样做的使用场景是:假如我们之前工作目录没有改过任何文件,也没add到暂存区,想合并「当前节点」与「reset目标节点」之间不具太大意义的 commit 记录,可能是阶段性地频繁提交,就是开发一个功能的时候,改或者增加一个文件的时候就commit,这样做导致一个完整的功能可能会好多个commit点,这时假如你需要把这些commit整合成一个commit的时候时,可以考虑使用reset --soft来让 commit 演进线图较为清晰。

--mixed(默认)

  1. 使用完reset --mixed后,我们可以直接执行 git add 将这些改变过的文件內容加入缓存区中,再执行 git commit 将缓存区中的內容提交至Repository中,这样一样可以达到合并commit节点的效果(与上面–soft合并commit节点差不多,只是多了git add添加到暂存区的操作);
  2. 移除所有缓存区中准备要提交的文件(Staged files),我们可以执行 git reset HEAD 来 Unstage 所有已列入缓存区的待提交的文件。(有时候发现add错文件到暂存区,就可以使用命令)。
  3. commit提交某些错误代码,或者没有必要的文件也被commit上去,不想再修改错误再commit(因为会留下一个错误commit点),可以回退到正确的commit点上,然后所有原节点和reset节点之间差异会返回工作目录,假如有个没必要的文件的话就可以直接删除了,再commit上去就OK了。

带文件参数的reset

git reset file.txt 相当于 git reset --mixed HEAD file.txt

  1. 本地库的 HEAD 不会动。
  2. 将指向 HEAD 的那个文件放到工作区域中。
  3. 需要注意的是带文件参数的 git reset没有 --hard、–soft 这两个参数。只有默认选项 --mixed 参数。

git revert

有时候,我们用Git的时候有可能commit提交代码后,发现这一次commit的内容是有错误的,那么有两种处理方法:

  1. 修改错误内容,再次commit一次 ;
  2. 使用git reset 命令撤销这一次错误的commit;
  3. 使用git revert 命令通过创建一次新的 commit 来撤销一次 commit 所做出的修改。

git revert 命令通过创建一次新的 commit 来撤销一次 commit 所做出的修改。这种撤销的方式是安全的,因为它并不修改 commit history, 比如这个命令git revert HEAD~2将会查出倒数第二次(即当前commit的往前一次)提交的修改,并创建一个新的提交,用于撤销当前提交的上一次 commit。
在这里插入图片描述

git checkout

前面讲到checkout会修改HEAD的指向,变更缓存区域里的内容,修改工作区域里的内容,这样看上去很像git reset --hard,但和reset --hard相比有三个重要的差别:

  1. reset会把工作区里的所有内容都更新掉,checkout不会去修改你在工作区里修改过的文件;
  2. reset把branch和HEAD同时移动,checkout则只把HEAD移动到另一个分支。
  3. 不同于reset,checkout 不会废弃任何分支或提交。

第二个区别可能有点难以理解,举例来说:假设你有两个分支master和develop,这两个分支指向不一样的commit,我们现在在develop分支上(HEAD指向的地方)。
如果我们git reset master,那么develop就会指向master所指向的那个commit。
如果我们git checkout master,那么develop不会动,只有HEAD会移动。HEAD会指向master。
在这里插入图片描述
我们可以 checkout 到任何一次 commit,通过提供 commit Id 作为参数。比如下面的命令:
git checkout HEAD~2git checkout commitid
再次添加新的提交时,你应该在修改前总是创建一个新的分支。

当带文件参数时,当执行git checkout [branch] file.txt时,更新了工作区域里file文件的内容。

参考博文出处

https://www.jianshu.com/p/c2ec5f06cf1a
https://segmentfault.com/a/1190000006185954
https://segmentfault.com/a/1190000003102737?utm_source=sf-related

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐