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 文件,添加到暂存区,会与第一次的修改合并,这时提交到版本库的内容是两次修改的合并。
假设项目存在这么一个提交记录:
$ 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
即可完美解决~
洋仔:小明,你的这个就可以用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
当前场景下就是:git reset commit_id3
将指定 commit_id 后的所有提交,都去除,并保留修改的代码在本地的区域,也就是Workspace
中
小明:啊哈,这样的话我就可以把错误代码修改后再提交了; 但是我已经 push 到线上仓库的数据怎么办呢?
洋仔:别急,有办法~
3、修改代码完成后,将修改好的代码 add 到暂存区,并提交到本地仓库中
命令:git add
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 命令
在进行下面的讲解是,还是先假设有这么一个提交链:
commit_id1 --> commit_id2 --> commit_id3 --> commit_id4
git reset commit_id2
reset
是将 HEAD
重新定位到commit_id2
上,对于 commit_id3
和 commit_id4
和本地当前的修改,对于不同的参数 param,会有不同的处理:
reset 命令有三种处理模式:
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 更改回退到暂存区:
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 更改回退到工作目录:
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 commit
wohu@ubuntu-dev:~/git_demo/test_demo$
小明:原来 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 的提交信息git revert -n
:重做执行 commit 的代码修改git revert -e
重做 commit_id 的提交信息,生成为一个新的 new_commit_id
git revert -e commit_id
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)
小明:还有这种操作,可以直接单独操作提交过程中的某一个 commit! 太棒了!
小明:还有最后一个问题:
如果我在一次开发中,发现某个文件修改错误了,想要将文件恢复到刚 pull 代码时的状态怎么办呢?
洋仔:简单! 看 git checkout 解决这个问题!
我们知道使用git checkout
可以
git checkout
切换分支
git checkout -b
创建分支等操作
它还有回滚指定文件的修改的功能,命令:git checkout --
上述语句的作用,就是将 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 ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: Readme.md
no 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 ..." to unstage)
modified: Readme.md
wohu@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 ..." to unstage)
modified: Readme.md
wohu@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 ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: Readme.md
no 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