git的工作区、暂存区和版本库的概念
关于这三个区域的概念其实网上以及讲的很多了,但是发现有些概念不同的人理解的不一样。
我自己的理解是如图所示
工作区的内容就是工作目录中实际显示出来的文件,而暂存区的内容是工作区的内容的快照,暂存区对工作区的内容进行跟踪,而版本库的内容是对暂存区的一种保存,版本库对工作区进行跟踪。通过git add命令就能够将此时工作区的内容推送到暂存区,git commit命令则是将暂存区的内容推送到版本库。

如图所示,通过git status命令可以看到绿色字体以及红色字体的两类不同的文件,绿色字体的”modifiled: file1”对应着上图中file1暂存区和版本库的差异(绿色箭头),而红色字体包含多种情况,上图中file1工作区和版本库的差异(红色箭头)对应着红色字体的”modified: file1”,而file2是在工作区创建了文件,却没有add和commit,于是暂存区和版本库就没有对该文件的追踪,对应着图中”Untracked files: file2”,file3是在add并commit之后删除了工作区的file3文件,对应着图中“deleted: file3”。总之,工作区与暂存区的差异总是红色字体表示,而暂存区与版本库的差异总是绿色字体,具体是属于modified,deleted,还是untracked files类型,则是由产生差异的原因决定。例如modified就是由于文件更改而产生的差异。
我始终认为,工作区的内容就是工作目录中看到的内容,暂存区的内容就是工作区推送过来的全部内容。但是网上有些博客的理解似乎是将暂存区和版本库的差异(git status出现的绿字内容)当成了暂存区的内容。尤其是廖雪峰的网站对于git的暂存区的讲解,说commit之后暂存区就空了,我是非常不认可的,因为如果接受了这样的设定,后面理解git checkout -- file,git restore,git reset等命令就会很不直观,在很多教程中就会出现类似“将reset带来的差异放入暂存区”之类的话,其实本质就是将版本库的文件整个回退到暂存区,却由于将暂存区错误理解成暂存区与版本库之间的差异,而将原本简单的概念理解复杂了。
git版本回退的几个命令
在接受了之前对三个区域的理解之后,git版本回退的命令就非常好理解了。有三个命令可以用于版本回退,
- git checkout -- file
- git reset HEAD -- file
- git restore -- file
其中git restore是git新版本用来代替git checkout在版本回退方面的作用的,而checkout另一个作用切换分支则是被git switch替代了。参考这里
其实本质上就是将文件从版本库覆盖到暂存区,或者从暂存区覆盖到工作区,下图展示了具体从哪个区覆盖到哪个区。

关于git的切换branch时工作区的变化
有时候在工作区有改动但还没有add的情况下需要切换到其他branch,切换branch后若该文件在repository中的数据没有变化,那么该文件就不会在工作区得到刷新,工作区原先的修改就会得以保留。如果切换branch之后该文件在repository中的数据变化了,工作区文件就得跟着刷新,就有可能覆盖掉原来工作区文件,所以这时候一般git会提示你不能切换,要先commit或者stash。
关于远端更改然后pull到本地后遇到冲突的情况
首先要理解git pull这个命令相当于先git fetch将远端的最新内容拉倒本地,然后通过merge合并到本地的分支。所以说其实pull遇到的冲突本质上是merge引起的冲突。有时候在工作区有改动但是还没有add的情况下需要pull拉下远端最新的内容,遇到的情况其实跟上面切换branch的情况类似,如果pull之后若该文件在repository中的数据没有变化,那么该文件就不会在工作区得到刷新,工作区原先的修改就会得以保留,否则就会跟着刷新,然后冲掉工作区的修改,这时候git会提示你工作区文件可能会被overwriten。
解决方法
一般上面两种问题都可以通过git stash解决,但是也会存在一种情况,你本地工作区修改的文件,在你切换branch或者pull之后也被修改了,并且修改了同样的地方,那么git stash pop的时候也会产生冲突,其实这种冲突跟repository的冲突差不多,如图所示。 ##

git merge
主要分为三种情况,分别如图所示

1.新分支与主分支都有新的commit时git merge回主分支
merge之后会生成新节点,且需要commit message
2.只有新分支更新了commit时采用git merge –no-ff命令
merge之后会生成新节点,且需要commit message
3.只有新分支更新了commit时采用git merge命令
merge之后不会生成新节点,相当于直接把新分支的commit直接拉到主分支上来了
未完待续。。。


