目录
1. 创建Git本地仓库
要提前说的是,仓库是进行版本控制的一个文件目录。我们要想对文件进行版本控制,就必须先创建一个仓库出来。
git本地仓库是什么东西呢?我们为什么要创建git本地仓库呢?
git是版本控制管理系统,也是一个版本控制器,如果我们想要对我们的电脑或者服务器上的文件进行记录,比如每次修改的内容以及版本迭代的详细内容,我们就可以使用git来追踪管理,我们要想管理的文件或者文档是存在我们电脑上的任意位置吗?如果存到电脑上的任意位置,git是不能进行追踪管理的,我们想要使用git来管理一些文件的时候,我们必须要把这些文件放在git仓库中,只有在仓库下的文件才能被git所追踪管理。
创建一个Git本地仓库的命令为git init,注意命令要在文件目录下执行。
aurora@wanghao:~$ mkdir gitcode
aurora@wanghao:~$ cd gitcode/
aurora@wanghao:~/gitcode$ ls
aurora@wanghao:~/gitcode$ git init
Initialized empty Git repository in /home/aurora/gitcode/.git/
aurora@wanghao:~/gitcode$ la
.git
aurora@wanghao:~/gitcode$ tree .git/
.git/
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ └── update.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
9 directories, 16 files
我们发现,当前目录下多了一个.git的隐藏文件,.git目录是Git来跟踪管理仓库的,不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
2. 配置Git
当安装Git后首先要做的事情就是设置你的用户名称和e-mail地址,这个是非常重要的,如果你不配置这两个,将来我们在做本地仓库操作的时候,就可能会出现一系列的问题,可能会报错,比如我们想向这个本地仓库去提交一些内容的话,如果没有配置name和e-mail的话就会出错,所以为了避免后面出现这种问题,我们建议把仓库创建好之后就把name和e-mail配置一下。
配置命令为:
git config user.name "Your Name"
git config user.email "email@example.com"
# 把Your Name改成你的昵称。
# 把email@example.com改成邮箱的格式,只要格式正确即可。
git config user.name "wh"
git config user.email "520@qq.com"
# git config -l查看我们的配置信息
aurora@wanghao:~/gitcode$ git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
user.name=wh
user.email=520@qq.com
# 删除配置
git config --unset user.name
git config --unset user.email
aurora@wanghao:~/gitcode$ git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
--global选项
git config --global user.name "wh"
git config --global user.email "520@qq.com"
我们在一台服务器上,不止可以创建一个本地仓库,在一台服务器下,可以创建多个本地仓库,加上--global的目的表示我们配置的这个配置项是会在当前机器的所有git仓库下都生效的,加这个选项就是讲配置项生效于所有的当前机器的git仓库中。
如果希望在不同仓库中使用不同的name或e-mail,可以不要--global选项,但要注意的是,执行命令时必须要在仓库中。
查看配置命令为:
aurora@wanghao:~/gitcode$ git config -l
user.email=520@qq.com
user.name=wh
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
删除对应的配置命令为:
git config --global --unset user.name
git config --global --unset user.email
aurora@wanghao:~/gitcode$ git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
3. 认识工作区、暂存区、版本库
aurora@wanghao:~/gitcode$ mkdir ReadMe
aurora@wanghao:~/gitcode$ la
.git ReadMe
目前情况下,git能否管理ReadMe文件???
不能,ReadMe所在的gitcode目录,不是本地仓库,正真的本地仓库是隐藏的.git文件,它才能被称为git的仓库,仓库又能被称为版本库(仓库),当然,也不能直接在.git目录下创建文件或者文件夹,这样是不被允许的,也不能这么做。
工作区:是在电脑上你要写代码或文件的目录。
暂存区:英文叫stage或index。一半存放在.git目录下的index文件(.git/index)中,我们把暂存区有时候也叫做索引(index)。
版本库:又名仓库,英文名repository。工作区有一个隐藏目录.git,它不算工作区,而是Git的版本库。这个版本库里面的所有文件都可以被Git,每个文件的修改、删除、Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以"还原"。
.git目录不属于工作区
下面这个图展示了工作区,暂存区和版本库之间的关系:
图中左侧为工作区,右侧为版本库。Git的版本库里存了很对东西,其中最重要的就是暂存区。
在创建Git版本库时,Git会为我们自动创建一个唯一的master分支,以及指向master的一个指针叫做HEAD。
当对工作区修改、删除、新增的文件执行git add命令时,暂存区目录树的文件索引会被更新。
当执行提交操作git commit时,master分支会做出相应的更新,可以简单理解为暂存区的目录树才会被正真写到版本库中。
由上述描述我们便能得知:通过新建或粘贴进目录的文件,并不能称之为向仓库中新增文件,而只是在工作区新增了文件。必须要通过使用git add和git commit命令才能将文件添加到仓库中进行管理。
git能做到版本控制,那在这个图中并没有体现出版本控制是如何做到的?
在我们的版本库中,其实还有一个模块,这个部分就叫对象库,objects,这个objects就是一个对象库,这个对象库中肯定是存储了一堆的git对象,当我们在add操作的时候,我们新增工作区的修改的时候,会将修改的内容写入到一个git对象中,这个git对象就会被维护到git的对象库里面。
对象库中存的是一个个的git对象,每个对象add一次,就会存修改的内容,在这个对象库中其实是存放了很多对象的,这些对象里面存的都是工作区的修改,这就表示它维护了我们所有文件的版本。
暂存区中可以看到,也是一个树状结构,它里面其实存的就不是一个个对象了,它存的是这一个个修改对象的索引,所以暂存区也是比较轻量级的。
commit操作时将暂存区的这棵树写道master分支下,可以看到,这个分支下也是一个树状结构,也存的是一个目录树,这个目录下存的也不是一个个对象,和暂存区一样,它存的是一个个对象的索引。
我们只要能拿到HEAD,HEAD是一个指针,我们能拿到HEAD,就能拿到master这棵树,拿到这棵树就能拿到某一个文件具体的修改的内容,拿到具体修改的内容之后就可以管控一个文件了。
index没有打印出来是因为这是一个刚刚新建的仓库的.git,它并不存在暂存区,因为我们没有对这个仓库进行任何的Add操作,所以不存在index。
4. 添加文件 -- 场景一
第一步就是将我们工作区的修改添加进暂存区中,第二步就是将暂存区的内容提交到本地仓库中,完成这两步才能让git追踪管理我们的文件。add和commit对应的就是git里面的两个命令,分别是git add命令和git commit命令。
在包含.git的目录下新建一个ReadMe文件,我们可以使用git add命令可以将文件添加到暂存区。
添加一个或多个文件到暂存区:git add [file1] [file2] ...
添加指定目录到暂存区,包括子目录:git add [dir]
添加当前目录下的所有文件改动到暂存区:git add .
再使用git commit命令将暂存区内容添加到本地仓库中:
提交暂存区全部内容到本地仓库中:git commit -m "message"
提交暂存区的指定文件到仓库区:git commit [file1] [file2] ... -m "message"
注意git commit后面的-m选项,要跟上描述本次提交的message,有用户自己完成,这部分内容绝对不能省略,并要好好描述,是用来记录你的提交细节,是给我们人看的。
例如:
aurora@wanghao:~/gitcode$ touch ReadMe
aurora@wanghao:~/gitcode$ vim ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
# aurora@wanghao:~/gitcode$ git add . #可以这样写,.代表全部
aurora@wanghao:~/gitcode$ git add ReadMe
aurora@wanghao:~/gitcode$ git commit -m "add first file"
[master (root-commit) f63c9cc] add first file
1 file changed, 1 insertion(+)
create mode 100644 ReadMe
git commit命令执行成功后会告诉我们,1个文件被改动(就是我们新添加的ReadMe文件),插入了两行内容(ReadMe有两行内容)。
我们还可以多次add不同的文件,而只commit一次便可以提交所有文件,是因为需要提交的文件是通通被add到了暂存区中,然后一次性commit暂存区的所有修改。如:
# add可以多次的进行add操作,一次将我们暂存区的内容提交到本地仓库中
aurora@wanghao:~/gitcode$ touch file1 file2 file3
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 ReadMe
aurora@wanghao:~/gitcode$ git add file1
aurora@wanghao:~/gitcode$ git add file2 file3
aurora@wanghao:~/gitcode$ git commit -m "add 3 file"
[master 63670a0] add 3 file
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file1
create mode 100644 file2
create mode 100644 file3
截至目前为止,我们已经能够将代码直接提交至本地仓库了。我们可以使用git log命令,来查看历史提交记录。
aurora@wanghao:~/gitcode$ git log
commit 63670a092869957c63a25e80c873c529344efb02 (HEAD -> master)
Author: 17309339946 <1358954942@qq.com>
Date: Fri May 30 18:55:01 2025 +0800
add 3 file
commit f63c9cc1531dc8d3753b35d0299a1c0709da1c6a
Author: 17309339946 <1358954942@qq.com>
Date: Fri May 30 18:49:10 2025 +0800
add first file
# 63670a092869957c63a25e80c873c529344efb02 -> commit Id 用哈希算出来的一个数字,不是123递增的
该命令显示从最近到最远的提交日志,并且可以看到我们commit时的日志消费。
如果嫌输出信息太多,看的眼花缭乱,我们可以加上--pretty=oneline参数:
#打印一行漂亮可观的日志
aurora@wanghao:~/gitcode$ git log --pretty=oneline
63670a092869957c63a25e80c873c529344efb02 (HEAD -> master) add 3 file
f63c9cc1531dc8d3753b35d0299a1c0709da1c6a add first file
需要说明的是,我们看到的一大串类似f63c9c....709da1c6a的是每次提交的commit id(版本号),Git的commit id不是1,2,3....递增的数字,而是一个SHAI计算出来的一个非常大的数字,用十六进程表示。
5. 查看.git文件
先来看看我们的.git的目录结构:
aurora@wanghao:~/gitcode$ tree .git/
.git/
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ └── update.sample
├── index #暂存区,add之后的内容都会添加到这个里面
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 0e
│ │ └── 6b1780b73cd9220ec5073dc64b42f7ad4bd945
│ ├── 15
│ │ └── a37e9ef171cca4a5d985fccd1fcf9414b2c7cf
│ ├── 63
│ │ └── 670a092869957c63a25e80c873c529344efb02
│ ├── 8d
│ │ └── 0e41234f24b6da002d962a26c2495ea16a425f
│ ├── e6
│ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│ ├── f6
│ │ └── 3c9cc1531dc8d3753b35d0299a1c0709da1c6a
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
18 directories, 27 files
# HEAD指针
aurora@wanghao:~/gitcode$ cat .git/HEAD
ref: refs/heads/master
# master里面的内容 master里面存放的就是最新的提交的commitID
aurora@wanghao:~/gitcode$ cat .git/refs/heads/master
63670a092869957c63a25e80c873c529344efb02
connitID的前两位是文件夹的名称,后面跟的就是该文件夹下面的内容。
那如何查看git对象里面的内容呢?这次我们就不能使用cat了。
aurora@wanghao:~/gitcode$ git cat-file -p 63670a092869957c63a25e80c873c529344efb02
tree 15a37e9ef171cca4a5d985fccd1fcf9414b2c7cf
parent f63c9cc1531dc8d3753b35d0299a1c0709da1c6a
author 17309339946 <1358954942@qq.com> 1748602501 +0800
committer 17309339946 <1358954942@qq.com> 1748602501 +0800
add 3 file
最下面的add 3 file也就是我们最新的一次提交的描述信息,是我们自己写的,还有是谁提交的,还有parent,是上次提交的ID,tree后面的也是个对象,我们不知道是啥,我们可以打印出来看一下。
aurora@wanghao:~/gitcode$ git cat-file -p 15a37e9ef171cca4a5d985fccd1fcf9414b2c7cf
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f ReadMe
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file1
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file2
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file3
打印出来有4行提示,首先ReadMe,是我们自己的文件还有file1、file2、file3都是我们之前提交的文件,每个文件都对应了一个commit ID,也就是对应了一个索引,我们就可以使用cat-file来查看这个对象的内容。
aurora@wanghao:~/gitcode$ git cat-file -p
8d0e41234f24b6da002d962a26c2495ea16a425f
hello git
我们可以看到,打印了一行内容,这一行内容刚好就是我们修改。新增ReadMe文件的时候新增的那一行内容,这是我们对ReadMe文件的一次修改,被git记录下来了,所以,我们每次提交的一些内容,都会被git记录下来,就是通过对象,记录下来的修改。
修改的工作区的内容会写入对象库的一个新的git对象中,这个对象被维护在了objects库里面。
总结一下,在本地的git仓库中,有几个文件或者目录很特殊
index:暂存区,git add后会更新该内容。
HEAD:默认指向master分支的一个指针。
refs/heads/master:文件例保存当前master分支的最新connit ID。
objects:包含了创建的各种版本库对象及内容,可以简单理解为放了git维护的所有修改。
最好能将常见的git操作与.git目录当中的结构内容变化对应起来,这样有利于我们理解git细节流程。
6. 添加文件 -- 场景二
我们现在已经清楚了如何向仓库中添加问题,并且对于工作区、暂存区、版本库也有了一定的认识。那么我们再展示一种添加文件的场景,能加深对工作区、暂存区、版本库的理解。
aurora@wanghao:~/gitcode$ touch file4
aurora@wanghao:~/gitcode$ git add file4
aurora@wanghao:~/gitcode$ touch file5
aurora@wanghao:~/gitcode$ git commit -m "add file"
[master 5881fd2] add file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file4
提交后发现打印了 1 file changed, 0 insertions(+), 0 deletions(-),意思是只有一个文件改变了,这时我们提出了疑问,不是新增了两个文件吗?
再来回忆一下,git add是将文件添加到暂存区,git commit是将暂存区的内容添加到本地仓库中。由于我们并没有使用git add file5,file5就不在暂存区中维护,所以我们commit的时候其实只是把已经在暂存区的file4提交了,而泄漏了工作区的file5。如何提交file5呢?很简单,再次add,commit即可。
aurora@wanghao:~/gitcode$ git add file5
aurora@wanghao:~/gitcode$ git commit -m "add file5"
[master a60a63b] add file5
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file5
7. 修改文件
在工作区中新增、删除、修改文件都算是对工作区的修改,Git比其他版本控制系统设计得很优秀,因为Git跟踪并管理得是修改,而非文件。
什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。
让我们将ReadMe文件进行一次修改:
aurora@wanghao:~/gitcode$ vim ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
此时,仓库中得ReadMe和我们工作区得ReadMe是不同得,如何查看当前仓库得状态呢?git status命令用户查看在你上次提交之后是否有对文件进行再次修改。
aurora@wanghao:~/gitcode$ 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: ReadMe
no changes added to commit (use "git add" and/or "git commit -a")
git status只能查哪个文件变化了,哪个文件修改了,修改了哪一行是查不到的。
目前,我们只知道文件被修改了,如果能知道具体哪些地方被修改了,就更好了。
aurora@wanghao:~/gitcode$ git diff ReadMe
diff --git a/ReadMe b/ReadMe
index 8d0e412..05fe86c 100644
--- a/ReadMe
+++ b/ReadMe
@@ -1 +1,2 @@
hello git
+hello world
#这个地方打印出来的就是unix通用的diff格式,
# 第一行的a表示改动前,b是改动后,都是ReadMe,第三和第四行的+++、---也表示改动前和改动后,第五行表示改动前的第一行内容+改动后从第一行起的两行内容。前面有个+号表示新增的内容。
Git diff [file] 命令用来显示暂存区和工作区文件的差异,显示的格式正是Unix通用的diff格式。也可以使用git diff HEAD -- [file]命令来查看版本库和工作区文件的区别。
知道了对ReadMe做了什么修改后,再把它提交到本地仓库就放心多了。
aurora@wanghao:~/gitcode$ git add ReadMe
aurora@wanghao:~/gitcode$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: ReadMe
git add之后,就没有看到上面no changes added to commit (use "git add" and/or "git commit -a")的消息了,接下来让我们继续git commit即可。
aurora@wanghao:~/gitcode$ git commit -m "modify ReadMe"
[master c7019d3] modify ReadMe
1 file changed, 1 insertion(+)
aurora@wanghao:~/gitcode$ git status
On branch master
nothing to commit, working tree clean
8. 版本回退
之前我们也提到过,Git能够管理文件的历史版本,这也是版本控制器重要的能力。如果有一天你发现之前的工作的工作做的出现了很大的问题,需要再某个也顶的历史版本重新开始,这个时候,就需要版本回退的功能了。
执行git reset命令用户回退版本,可以指定退回某一次提交的版本,"回退"本质是要将版本库的内容进行回退,工作区或暂存区是否回退由命令行参数决定:
git reset 命令语法格式为:git reset [--soft | --mixed | --hard] [HEAD]
--mixed 为默认选项,使用时可以不用带该参数。该参数将暂存区的内容退回为指定提交版本内容,工作区文件保持不变。
--soft 参数对于工作区和暂存区的内容都不变,只是将版本库回退到某个指定版本。
--hard 参数将暂存区与工作区都退回到指定版本。切记工作区有未提交的代码时不要用这个命令,因为工作区会回滚,你没有提交的代码就再也找不回了,所以使用该参数前一定要慎重。
HEAD说明:
可直接写成commit id,表示指定退回的版本。
HEAD表示当前版本
HEAD^上一个版本
HEAD^^上上一个版本
以此类推...
可以使用~数字表示:
HEAD~0 表示当前版本
HEAD~1 上一个版本
HEAD^2 上上一个版本
以此类推
为了方便测试回退功能,我们的ReadMe一共是有两个版本的。
查看历史提交记录:
aurora@wanghao:~/gitcode$ git log --pretty=oneline
c7019d3eefb55f0272bfb62e7d5dfd696f46f7cd (HEAD -> master) modify ReadMe
a60a63b1c4a5fecc6618d9a5fcaa28462c5fb253 add file5
5881fd20fba8e9b8ae464df6aa182da045384210 add file
63670a092869957c63a25e80c873c529344efb02 add 3 file
f63c9cc1531dc8d3753b35d0299a1c0709da1c6a add first file
最新的一次是修改ReadMe,新增了hello world,我们想要回退到只剩个hello git,我们可以使用git reset。
aurora@wanghao:~/gitcode$ git log --pretty=oneline
c7019d3eefb55f0272bfb62e7d5dfd696f46f7cd (HEAD -> master) modify ReadMe
a60a63b1c4a5fecc6618d9a5fcaa28462c5fb253 add file5
5881fd20fba8e9b8ae464df6aa182da045384210 add file
63670a092869957c63a25e80c873c529344efb02 add 3 file
f63c9cc1531dc8d3753b35d0299a1c0709da1c6a add first file
aurora@wanghao:~/gitcode$ git reset --hard f63c9cc1531dc8d3753b35d0299a1c0709da1c6a
HEAD is now at f63c9cc add first file
aurora@wanghao:~/gitcode$ la
.git ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
aurora@wanghao:~/gitcode$ git log --pretty=oneline
f63c9cc1531dc8d3753b35d0299a1c0709da1c6a (HEAD -> master) add first file
我们惊奇的发现,此时ReadMe文件的内容,已经回退到version1了!我们再次使用git log查看一下提交日志,发现HEAD指向了version1。
我们当前的目录下只剩下ReadMe了,我们之前的file1到file5都不见了,原因是我们使用了hard将我们的工作区也回退了,我们的file1到file5都是经过add first file后面才提交的,一旦回退到这次,后面的提交都会被回退了。所以file1到file5也不见了。我们的ReadMe中的内容只有一个hello git,hello world已经不见了,所以这样也证明了我们的回退确实是成功的,我们打印log也只剩了add first file,后面的log都不见了。
到这里一般回退功能就演示完了,但现在如果我后悔了,想再回到回退前,我们可以继续使用git reset命令,回退到会退前的版本,但是我们必须拿到回退前版本的commitID去指定回退的版本。
aurora@wanghao:~/gitcode$ git reset --hard c7019d3eefb55f0272bfb62e7d5dfd696f46f7cd
HEAD is now at c7019d3 modify ReadMe
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 file4 file5 ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
aurora@wanghao:~/gitcode$ git log --pretty=oneline
c7019d3eefb55f0272bfb62e7d5dfd696f46f7cd (HEAD -> master) modify ReadMe
a60a63b1c4a5fecc6618d9a5fcaa28462c5fb253 add file5
5881fd20fba8e9b8ae464df6aa182da045384210 add file
63670a092869957c63a25e80c873c529344efb02 add 3 file
f63c9cc1531dc8d3753b35d0299a1c0709da1c6a add first file
我们可以看到file1~file5都回来了,并且ReadMe里面的hello world也回来了,我们发现log也回来了,master最新的还是最新一次提交的。
但是我们看到了git log并不能打印出会退前的版本的commidID,运气号的话我们可以从终端上找找之前的记录,运气不好的话commitID已经被我们搞丢了。
Git还提供了一个git reflog命令能补救一下,该命令用来记录本地的每一次命令。
aurora@wanghao:~/gitcode$ git reflog
f63c9cc (HEAD -> master) HEAD@{0}: reset: moving to f63c9cc1531dc8d3753b35d0299a1c0709da1c6a
c7019d3 HEAD@{1}: reset: moving to c7019d3eefb55f0272bfb62e7d5dfd696f46f7cd
f63c9cc (HEAD -> master) HEAD@{2}: reset: moving to f63c9cc1531dc8d3753b35d0299a1c0709da1c6a
c7019d3 HEAD@{3}: commit: modify ReadMe
a60a63b HEAD@{4}: commit: add file5
5881fd2 HEAD@{5}: commit: add file
63670a0 HEAD@{6}: commit: add 3 file
f63c9cc (HEAD -> master) HEAD@{7}: commit (initial): add first file
reflog命令用来记录本地的每一次提交命令,还记录了我们每一次的回退指令。在modify ReadMe之前有一个c7019d3,它其实就是我们这一次的commit ID,它其实就是commit ID的一部分,我们可以用一部分的commit ID来进行回退。
aurora@wanghao:~/gitcode$ git reset --hard c7019d3
HEAD is now at c7019d3 modify ReadMe
aurora@wanghao:~/gitcode$ git log --pretty=oneline
c7019d3eefb55f0272bfb62e7d5dfd696f46f7cd (HEAD -> master) modify ReadMe
a60a63b1c4a5fecc6618d9a5fcaa28462c5fb253 add file5
5881fd20fba8e9b8ae464df6aa182da045384210 add file
63670a092869957c63a25e80c873c529344efb02 add 3 file
f63c9cc1531dc8d3753b35d0299a1c0709da1c6a add first file
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 file4 file5 ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
我们确实发现回到modify ReadMe这一次了,我们打印了一下ReadMe里面的内容,确实,还有hello world,表示回退成功。
在开发的过程中,我们会进行很多的git操作,迟早会把我们的commit ID冲掉,我们会找不到的,一旦找不到commit ID的时候,就没有后悔药可以吃了。
值得说的是,Git的版本回退速度非常快,因为Git在内部有个指向当前分支(此处是master)的HEAD指针,refs/heads/master文件里保存当前master分支的最新commit id。当我们在回退版本的时候,Git仅仅是给refs/heads/master中存储一个特定的version。
最新的提交时git以及world,上一次提交其实就是git,我们可以看到Git里面其实是有一个HEAD指针,HEAD指针指向master,master里面其实存放的是commit ID,commit ID其实是Git对象,Git对象里面有git以及world,我们进行版本回退会变成下面的样子。
会直接把指针指向的内容改掉,不指向第二次提交了,而是指向第一次提交。其实就是把master的内容改一下,只是改commit ID,所以,只改commit ID,它的速度是非常快的,我们想吃后悔药的时候,从git到git+world的时候只需要修改一下master里面的commit ID即可完成回退。
9. 撤销修改
如果我们在我们的工作区进行了很长时间的代码开发了,越写越写不下去,觉得自己写的实在是垃圾,想恢复到上一个版本。
撤销修改有不同的一些场景,不同的场景对应的一些git的操作是不一样的,撤销修改有以下三种场景。
第一种场景就是我们写的代码只存在于工作区中,暂存区和版本库中是没有这断code的,第二个场景就是既在工作区中进行了代码的开发,又将工作区中的内容进行了一次add操作,所以暂存区中也是存在这段代码的,但是版本库中我们没有进行commit操作,所以版本库中是不存在的,第三个场景就是工作区中也有,进行了一次add操作,还进行了一次commit操作,导致工作区,暂存区和版本库对应的code都是有的。
对于这三个场景,想要撤退,会达到什么效果?
对于第一个场景,只有工作区中存在我们的代码,我们撤销的话肯定只撤销工作区的code,暂存区和版本库不懂,对于第二个场景,我们撤销的是工作区和暂存区中的内容,对于第三个场景,我们撤销的是工作区、暂存区和版本库中所有的code。
9.1 情况一:对于工作区的代码,还没有add
你当然可以直接删掉你目前工作区新增的代码,但是不推荐,如果我们工作效率不高,才写了一行代码就发现不行了,要是你写了三天,一直没有提交,该怎么删掉呢》你自己都忘了自己新增了哪些,有的人会想,我们可以git diff xxx一下,看看差别再删,那你肯定又要花费时间删代码了,并且很大概率还会出bug。
Git其实还为我们提供了更好的方式,我们可以使用git chaekout -- [file]命令让工作区的文件回到最近一次add或者commit时的状态,要注意git checkout -- [file]命令中的--很重要,切记不要省略,一旦省略,该命令就变成其他的意思了。
aurora@wanghao:~/gitcode$ vim ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
xxx code
aurora@wanghao:~/gitcode$ git checkout -- ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
9.2 情况二:已经add,但没有commit
add后还是保存到了暂存区,怎么撤销?
aurora@wanghao:~/gitcode$ vim ReadMe
aurora@wanghao:~/gitcode$ git add ReadMe
aurora@wanghao:~/gitcode$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
xxx code
让我们回忆一下学过的git reset回退命令,该命令如果使用--mixed参数,可以将暂存区的内容退回为指定的版本内容,但工作区文件保持不变。那我们就可以回退下暂存区的内容了!!!使用--mixed就回到了场景一,当然,我们还可以使用--hard,暂存区和工作区就保持和版本库中一样了,也正是我们想要的效果。
# --mixed 是默认参数,使用时可以省略
aurora@wanghao:~/gitcode$ git reset HEAD ReadMe
Unstaged changes after reset:
M ReadMe
用git status查看一下,发现现在暂存区是感觉的,工作区有修改。
aurora@wanghao:~/gitcode$ 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: ReadMe
no changes added to commit (use "git add" and/or "git commit -a")
还记得如何丢弃工作区的修改吗?
aurora@wanghao:~/gitcode$ git checkout -- ReadMe
aurora@wanghao:~/gitcode$ git status
On branch master
nothing to commit, working tree clean
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
打印一下,发现我们新增的xxx code就不见了。
9.3 情况三:已经add,并且也commit了
当版本库中存在xxx code的版本的代码,我们称之为最新版本的代码,它的上一个版本的代码其实是不包含xxx code的代码,其实就可以使用git reset命令将版本库的内容回退到上一个版本的代码,同时我们可以使用--hard选项将工作区和暂存区的代码都回退到上一个版本的内容,也就是不包含xxx code的这一份代码中。
从工作区将我们的代码添加成暂存区,是需要add操作的,将暂存区的内容提交到版本库,是需要commit操作的。将版本库中的内容到远程仓库是需要push操作的。
不要担心,我们可以git reset --hard HEAD^回退到上一个版本!不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程,一旦你推送到远程版本库,你就真的惨了.....
将来再公司中运行的代码,比如说功能,产品,它跑的代码不是工作区中写的代码,也不是暂存区中的代码,还不是版本库中的代码,它其实跑的就是我们远程仓库中的代码,我们撤销功能其实防止我们写的一下错误,写的不好的代码推送到远程仓库中。撤销的目的其实就是不影响远程仓库中的代码。
# 修改内容
aurora@wanghao:~/gitcode$ vim ReadMe
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
xxx code
# 提交
aurora@wanghao:~/gitcode$ git add ReadMe
aurora@wanghao:~/gitcode$ git commit -m "modify ReadMe: xxx code"
[master 58fc749] modify ReadMe: xxx code
1 file changed, 1 insertion(+)
# 回退
aurora@wanghao:~/gitcode$ git reset --hard HEAD^
HEAD is now at c7019d3 modify ReadMe
aurora@wanghao:~/gitcode$ git status
On branch master
nothing to commit, working tree clean
aurora@wanghao:~/gitcode$ cat ReadMe
hello git
hello world
10. 删除文件
在Git中,删除也是一个修改操作,如果要删除file5文件,应该怎么做呢?
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 file4 file5 ReadMe
aurora@wanghao:~/gitcode$ rm file5
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 file4 ReadMe
这样我们只是把工作区的删掉了,但是对于我们的本地仓库,file5并没有被删掉,我们可以使用add命令和commit命令来将我们的修改提交到本地仓库中,因为对工作区的删除也算是修改。
但是这样直接删除是没有用的,反而徒增烦恼,git status命令会立刻告诉你哪些文件被删除了。
aurora@wanghao:~/gitcode$ git add file5
aurora@wanghao:~/gitcode$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: file5
# 暂存区的file5已经被删掉了。
最后我们来进行commit。
aurora@wanghao:~/gitcode$ git commit -m "delete file5"
[master f510000] delete file5
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file5
这样就完成了一个文件在工作区、暂存区以及版本库中的删除操作。
aurora@wanghao:~/gitcode$ git status
On branch master
nothing to commit, working tree clean
用git status查看,当前是干净的工作区。
第一步我们使用rm删除了工作区的内容,第二步我们用add操作将工作区的变动提交进暂存区,第三步我们使用了commit命令进行了提交,所以使用了三步。
git提供的rm命令可以将三步转换为两步。
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 file4 ReadMe
aurora@wanghao:~/gitcode$ git rm file4
rm 'file4'
aurora@wanghao:~/gitcode$ ls
file1 file2 file3 ReadMe
aurora@wanghao:~/gitcode$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: file4
aurora@wanghao:~/gitcode$ git commit -m "delete file4"
[master 1059760] delete file4
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file4
aurora@wanghao:~/gitcode$ git status
On branch master
nothing to commit, working tree clean
git rm命令其实帮我们干了两件事情,第一件事情就是删除工作区中的文件,第二件事情就是对应的暂存区也帮我们删除了,我们只需要做最后一件事情,git commit操作就可以了。