书接上文,咱们对Git有了一个基本的认知,而且基础工做也作好了。接下来,笔者就用一个实例,对照执行命令先后文件的变化,探索如下命令的运行过程。若是,看官对命令底层原理不感兴趣,只是想知道命令怎么用,就直接看翻到文章最后。git
git init
git add
git status
git commit
git log
行文时,系统环境为 macOS Mojave v10.14.5,Git版本为 2.20.1 ,开发工具为 vscode 1.45.1 。
shell
git init
首先,咱们须要有一个工做目录(Working Directory)。工做目录能够是刚新建无文件的文件夹,也能够是一个已经有文件的文件夹。bash
mkdir git-kael-project
cd git-kael-project
复制代码
而后,我须要使用 git init
对工做目录进行初始化。 git init
命令以后能够带上文件夹名称,若是文件夹不存在,则在当前路径下新建文件夹,并将其初始化。若是存在就直接对文件夹初始化。app
git init
git init folderName
复制代码
接下来,让咱们看一下执行 git init
先后,工做目录的区别。工具
# 未初始化
ls -al
total 0
drwxr-xr-x 2 apple staff 64 5 29 22:31 .
drwxr-xr-x+ 45 apple staff 1440 5 29 22:31 ..
# 初始化
git init
ls -al
total 0
drwxr-xr-x 3 apple staff 96 5 29 22:32 .
drwxr-xr-x+ 45 apple staff 1440 5 29 22:31 ..
drwxr-xr-x 9 apple staff 288 5 29 22:32 .git # 多了一个 .git 文件夹,这就是咱们说的仓库!
复制代码
对比,咱们能够知道, git init
命令就是在工做目录的根目录下,生成一个 .git 文件夹,这个文件就是咱们这个项目的 Git 仓库。它包含了几乎全部 Git 存储和操做的东西。 要想备份或复制一个版本库,只需把这个目录拷贝至另外一处便可。
咱们查看一下, .git 文件夹里到底有什么开发工具
ls -al .git/
total 24
drwxr-xr-x 9 apple staff 288 5 29 21:41 .
drwxr-xr-x 3 apple staff 96 5 29 21:41 ..
-rw-r--r-- 1 apple staff 23 5 29 21:41 HEAD # HEAD指针,后文再讲
-rw-r--r-- 1 apple staff 137 5 29 21:41 config # 项目配置,使用 git config --loacl 时,配置的内容就存放在这里
-rw-r--r-- 1 apple staff 73 5 29 21:41 description # 项目描述文件,给GitWeb使用,咱们不用关心
drwxr-xr-x 13 apple staff 416 5 29 21:41 hooks # 钩子,存放的是一些 shell 脚本
drwxr-xr-x 3 apple staff 96 5 29 21:41 info # 目录下面的 exclude 文件,是用来存放不想放置在 .gitignore 文件里的忽略模式
drwxr-xr-x 4 apple staff 128 5 29 21:41 objects # 项目全部的 git 对象都被存放在这里,tree 对象、parent 对象、blob 对象
drwxr-xr-x 4 apple staff 128 5 29 21:41 refs # 存放指针的位置,branch 、remote 、tag
# -rw-r--r-- 1 apple staff xx x xx xx:xx index # 暂存区,还未生成
复制代码
你们能够先对上面的目录结构稍微记忆一下。强调一下,由于尚未执行任何操做,因此如今暂存区还没被建立,也就是 index 文件!
ui
git add
接下来,咱们新建一个 hello-git.txt 文件。编码
touch hello-git.txt
复制代码
再加入到暂存区以前,咱们先查看一下spa
# 当前状态下,是没有index文件的,也就是无暂存区
ls -al .git/
total 24
drwxr-xr-x 11 apple staff 352 5 29 22:47 .
drwxr-xr-x 5 apple staff 160 5 29 22:47 ..
-rw-r--r--@ 1 apple staff 23 5 29 21:41 HEAD
-rw-r--r-- 1 apple staff 137 5 29 21:41 config
-rw-r--r-- 1 apple staff 73 5 29 21:41 description
drwxr-xr-x 13 apple staff 416 5 29 21:41 hooks
drwxr-xr-x 3 apple staff 96 5 29 21:41 info
drwxr-xr-x 5 apple staff 160 5 29 22:37 objects
drwxr-xr-x 4 apple staff 128 5 29 21:41 refs
# 同时.git/objects下,也是没有git对象的
ls -al .git/objects/
total 0
drwxr-xr-x 5 apple staff 160 5 29 22:37 .
drwxr-xr-x 11 apple staff 352 5 29 22:47 ..
drwxr-xr-x 2 apple staff 64 5 29 21:41 info
drwxr-xr-x 2 apple staff 64 5 29 21:41 pack
复制代码
而后,咱们用 git add
命令,把 hello-git.txt 加入到暂存区。以后咱们再查看一下3d
固然,我这里查看的就是执行命令后,会有变化的位置。其它位置是没有变化的,各位看官可自行验证。
# git add 命令,后面能够带文件名(这里能够是一个或多个),也能够带符号点 . ,符号点与 --all 全等。
# git add . 与 git add --all 效果同样
git add hello-git.txt
# 查看 .git/
ls -al .git/
total 48
drwxr-xr-x 11 apple staff 352 5 29 22:47 .
drwxr-xr-x 5 apple staff 160 5 29 22:47 ..
-rw-r--r--@ 1 apple staff 6148 5 29 22:47 .DS_Store
-rw-r--r--@ 1 apple staff 23 5 29 21:41 HEAD
-rw-r--r-- 1 apple staff 137 5 29 21:41 config
-rw-r--r-- 1 apple staff 73 5 29 21:41 description
drwxr-xr-x 13 apple staff 416 5 29 21:41 hooks
-rw-r--r--@ 1 apple staff 32 5 29 22:46 index # 这个暂存区终于出来!
drwxr-xr-x 3 apple staff 96 5 29 21:41 info
drwxr-xr-x 5 apple staff 160 5 29 22:37 objects
drwxr-xr-x 4 apple staff 128 5 29 21:41 refs
# 查看 .git/objects/
ls -al .git/objects/
total 0
drwxr-xr-x 5 apple staff 160 5 29 22:37 .
drwxr-xr-x 11 apple staff 352 5 29 22:47 ..
drwxr-xr-x 3 apple staff 96 5 29 22:37 e6
drwxr-xr-x 2 apple staff 64 5 29 21:41 info
drwxr-xr-x 2 apple staff 64 5 29 21:41 pack
# 继续查看 .git/objects/e6/
ls -al .git/objects/e6/
total 8
drwxr-xr-x 3 apple staff 96 5 29 22:37 .
drwxr-xr-x 5 apple staff 160 5 29 22:37 ..
-r--r--r-- 1 apple staff 15 5 29 22:37 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
复制代码
到这里,咱们能够清楚的看到,当咱们执行了 git add hello-git.txt
以后,仓库发生的变化。
git ls-files --stage
查看暂存区的内容
对于 git add
这个命令,其实它是 git hash-object
和 git update-index
这两个底层命令组合实现的。也就是说,若是你愿意,你是可使用这两个底层命令来 装逼 操做的。 git add
只是为了简化操做而实现的上层命令。这两个底层命令后续有机会再讲!
git status
好了,在看官平时工做当中,可能会出现,离开工位一段时间,回来时就不记得以前在工做目录作了什么(嗯,应该只有笔者才会这样zz)。这个时候,你能够努力回想使用 git status
查看工做目录的状态。
git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello-git.txt
# 打印的信息告诉咱们,在 master 分支上,有一个尚未提交的新文件: hello-git.txt。
# 括号里还告诉咱们,可使用 git rm --cached filename 来把文件从暂存区移走。
复制代码
若是你想,你也可使用 git ls-files --stage
来 装逼 操做。
git ls-files --stage
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 hello-git.txt
复制代码
git commit
把修改加入到暂存区以后,使用 git commit
命令,生成整个项目的‘全貌快照’,也就是 commitObject 。项目版本历史就是由一个个 commitObject 组成的,Git 能够将项目还原到任意一个 commitObject 。生成一个 commitObject 又叫 完成一次提交。
# 在使用 git add 时,咱们能够有选择的把文件加放到暂存区
# 可是在使用 git commit 时,必定是把暂存区里包含的 blob 对象所有一块儿,生成一个 commitObject。
# git commit --amend 后面会在应用场景里讲,能够先理解为修改前一个 commitObject。
# 虽然表面上来看是这样。可是实际上是不对的。commitObject 在大部分状况下,是没法修改的。
git commit -m 'create hello-git.txt'
[master (root-commit) 9eb3d5f] create hello-git.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello-git.txt
# 提示信息里,有 分支、commitObject 的校验和、commitMessage、修改的内容统计。
复制代码
老规则,咱们来对比看一下, 输入 git commit
命令先后, .git 里的文件变化。
# 下面信息是在 git commit 命令以前查看的。
ls -al .git/
total 32
drwxr-xr-x 10 apple staff 320 5 30 17:27 .
drwxr-xr-x 5 apple staff 160 5 30 17:27 ..
-rw-r--r-- 1 apple staff 23 5 30 17:26 HEAD
-rw-r--r-- 1 apple staff 137 5 30 17:26 config
-rw-r--r-- 1 apple staff 73 5 30 17:26 description
drwxr-xr-x 13 apple staff 416 5 30 17:26 hooks
-rw-r--r-- 1 apple staff 112 5 30 17:26 index
drwxr-xr-x 3 apple staff 96 5 30 17:26 info
drwxr-xr-x 5 apple staff 160 5 30 17:26 objects
drwxr-xr-x 4 apple staff 128 5 30 17:26 refs
# 下面信息是在 git commit 命令以后查看的。
ls -al .git/
total 56
drwxr-xr-x 13 apple staff 416 5 30 17:22 .
drwxr-xr-x 4 apple staff 128 5 30 17:22 ..
-rw-r--r-- 1 apple staff 21 5 30 17:10 COMMIT_EDITMSG
-rw-r--r--@ 1 apple staff 23 5 29 21:41 HEAD
-rw-r--r-- 1 apple staff 137 5 29 21:41 config
-rw-r--r-- 1 apple staff 73 5 29 21:41 description
drwxr-xr-x 13 apple staff 416 5 29 21:41 hooks
-rw-r--r-- 1 apple staff 145 5 30 17:10 index
drwxr-xr-x 3 apple staff 96 5 29 21:41 info
drwxr-xr-x 5 apple staff 160 5 30 17:22 logs
drwxr-xr-x 8 apple staff 256 5 30 17:10 objects
drwxr-xr-x 4 apple staff 128 5 29 21:41 refs
# 查看logs
ls -al .git/logs/
total 24
drwxr-xr-x 5 apple staff 160 5 30 17:22 .
drwxr-xr-x 13 apple staff 416 5 30 17:22 ..
-rw-r--r--@ 1 apple staff 165 5 30 17:10 HEAD
drwxr-xr-x 4 apple staff 128 5 30 17:22 refs
ls -al .git/logs/refs/heads/
total 8
drwxr-xr-x 3 apple staff 96 5 30 17:10 .
drwxr-xr-x 4 apple staff 128 5 30 17:22 ..
-rw-r--r--@ 1 apple staff 165 5 30 17:10 master
# 查看objects
ls -al .git/objects/
total 16
drwxr-xr-x 8 apple staff 256 5 30 17:10 .
drwxr-xr-x 13 apple staff 416 5 30 17:22 ..
drwxr-xr-x 3 apple staff 96 5 30 17:10 62
drwxr-xr-x 3 apple staff 96 5 30 17:10 9e
drwxr-xr-x 3 apple staff 96 5 29 22:37 e6
drwxr-xr-x 2 apple staff 64 5 29 21:41 info
drwxr-xr-x 2 apple staff 64 5 29 21:41 pack
ls -al .git/objects/9e/
total 8
drwxr-xr-x 3 apple staff 96 5 30 17:10 .
drwxr-xr-x 8 apple staff 256 5 30 17:10 ..
-r--r--r-- 1 apple staff 131 5 30 17:10 b3d5fe6014cf11a120a26b3f8b3af2f20964bd
ls -al .git/objects/62/
total 8
drwxr-xr-x 3 apple staff 96 5 30 17:10 .
drwxr-xr-x 8 apple staff 256 5 30 17:10 ..
-r--r--r-- 1 apple staff 58 5 30 17:10 01f623b63c5736c92895712c6038835be6a7f2
复制代码
对比能够发现, git commit
以后,多了如下文件
经过对比,笔者理解的 git commit
命令执行以后,Git 作了以下事情:
-m
参数以后的 commitMessage 写入到这个文件。git log
命令获得的内容。
这里,笔者再详细的描述一下 commitObject。为了更好的演示,咱们在当前的提交基础上,继续做一些操做。
mkdir lib
touch lib/lib.js
echo 'Git 真好用!' > lib/git.txt
git add .
git commit -m 'create lib/git.txt'
[master 75f75b1] create lib/git.txt
2 files changed, 1 insertion(+)
create mode 100644 lib/git.txt
create mode 100644 lib/lib.js
复制代码
咱们新建了一个 lib/ 文件夹,而后在这个文件夹里,新建了一个 lib.js 文件,和一个 git.txt 文件,并在git.txt 文件里写入了 'Git 真好用!'。接着就是 git add .
把刚的这些修改添加到暂存区,最后使用 git commit -m 'create lib/git.txt'
生成一个提交。
咱们如今获得了一个 75f75b1
的 commitObject。咱们来查看一下这个对象的信息。
# 查看 75f75b1 的类型和详情
git cat-file -t 75f75b1
commit # 这是一个 commit 类型的对象
git cat-file -p 75f75b1
tree d1d138a1890c432ef996b8713b69453a8baa339e # 75f75b1 对象的 tree 对象,对应的项目目录
parent 9eb3d5fe6014cf11a120a26b3f8b3af2f20964bd # 75f75b1 对象的 parent 对象,对应着父提交对象
author kael <kael_yp@foxmail.com> 1590841110 +0800 # 75f75b1 对象的做者
committer kael <kael_yp@foxmail.com> 1590841110 +0800 # 75f75b1 对象的提交者
create lib/git.txt # 75f75b1 对象的提交说明 commitMessage
复制代码
咱们继续查看 d1d138a
这个 tree 对象。
# 查看 d1d138a 的类型和详情
git cat-file -t d1d138a
tree # 这是一个 tree 类型的对象
git cat-file -p 1d138a
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello-git.txt # blob 类型的对象,hello-git.txt文件
040000 tree 8bc5165a0972ca341226552000d29888be651bc9 lib # tree 类型的对象,lib 文件夹
# 查看 hello-git.txt 文件
git cat-file -p e69de2
# 为空,由于这个文件就是一个空文件。我忘记加内容了。。。哈哈
# 查看 lib 文件夹
git cat-file -p 8bc516
100644 blob cdb8667b7f629f26cae0ea24993bfd2c6411e33c git.txt # blob 类型的对象,git.txt 文件
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 lib.js # blob 类型的对象,lib.js 文件
# 查看 git.txt 文件
git cat-file -p cdb8667
Git 真好用! # 这就是咱们以前写入的内容。这就是 Git 哲学里的,经过文件快照记录版本,而不是使用 diff 。
复制代码
咱们继续查看 9eb3d5f
这个 parent 对象。
# 查看 9eb3d5f 的类型和详情
git cat-file -t 9eb3d5f
commit # 这是一个 commit 类型的对象
# 这是一个 parent 类型的对象
git cat-file -p 9eb3d5f
tree 6201f623b63c5736c92895712c6038835be6a7f2 # 9eb3d5f 对象的 tree 对象,对应的项目目录
author kael <kael_yp@foxmail.com> 1590829805 +0800 # 9eb3d5f 对象的做者
committer kael <kael_yp@foxmail.com> 1590829805 +0800 # 9eb3d5f 对象的提交者
create hello-git.txt # 9eb3d5f 对象的提交说明 commitMessage
复制代码
再看看 6201f62
这个 tree 对象。
git cat-file -p 6201f62
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello-git.txt
git cat-file -p e69de29
# hello-git.txt 内容为空
复制代码
咱们简单描述上面查看到的信息。 75f75b1
这个对象里有 tree 和 parent。tree 里有 blob 和 tree。而后就是 parent 里有 tree,没有 parent。
若是你不想使用git commit
这个命令,那就使用 git write-tree
和 git commit-tree
来 装逼 实现。 git write-tree
命令用来将当前的目录结构,生成一个 treeObject。 git commit-tree
命令用于将目录树对象写入版本历史。有机会,后续再讲这两个命令。
git log
git log
这个命令是最经常使用命令之一,能够查看指定的 指针 的提交历史。
git log
# 第一行是 类型 校验和 指针集,指针集里可能会有 HEAD、branch、tag,使用逗号分开
commit 9eb3d5fe6014cf11a120a26b3f8b3af2f20964bd (HEAD -> master)
# 第二行是 做者信息
Author: kael <kael_yp@foxmail.com>
# 第三行是 时间
Date: Sat May 30 17:10:05 2020 +0800
# 往下就是 commitMessage
create hello-git.txt
复制代码
使用 git log
命令是不会对 .git 仓库操做的,也就是你不会有 何时什么方式使用了 git log
命令 的记录,由于记录这个操做,没有任何意义。
git log
是一个超级厉害的命令。看一我的 Git 用得好很差,看Ta使用 git log
的姿式怎么样就能看出来。下面我就列举一些经常使用的 git log
使用场景和其它参数。如下选项能够组合使用
# 正常查看历史记录
git log
# 查看简洁的历史记录
git log --oneline
# 查看带每一次提交时修改的历史记录
git log -p # -p 是 --patch 的简写
# 查看必定数量的历史记录
git log -4 # 这里就是显示倒数的4条
# 查看有简略统计信息的历史记录
git log --stat
# 控制历史记录的显示格式。
git log --pretty=format:"%h - %an, %s" # %h 简短校验和;%an 做者;%s 提交说明;
git log --pretty=online # 差很少等于 git log --oneline
# 查看带结合路径的历史记录,大概就是会给 commitObject 间边上线条
git log --graph
# 查看指定时限内的历史记录
git log --since=1.weeks # 一周内的历史记录
git log --since=2020-05-01 # 查看2020年05月01号以后的提交,--since 能够写做 --after
# 查看指定时间以前的提交
git log --until=2020-05-01 # 查看2020年05月01号以前的提交,--until 能够写做 --before
# 查看包含了指定字符的message所在的历史记录
git log --grep=create # 查看包含了 ‘create’ 的提交说明的历史记录
# 这个就牛逼了,查看包含了指定字符修改的历史记录
git log -Svar # 查看修改里包含了 ‘var’ 的历史记录
# 查看指定做者的历史记录
git log --author=kael # 查看 kael 写的历史记录
# 查看指定提交者的历史记录
git log --committer=kael # 查看 kael 提交的历史记录
复制代码
这个提交历史记录实际是经过 commitObject 的 parent 属性连起来的,直到找到一个没有 parent 属性的 commitObject。能够跟链表对应理解。
补充一下,.git/HEAD 文件的相关知识点。这个文件在 git init
时,里面的内容是 ref: refs/heads/master
,是否是很眼熟?没错,这是一个路径【 .git/refs/heads/master 】。当咱们提交时,HEAD 会带着移动的分支指针。若是是第一次提交,Git 会用这个文件的内容生成对应的 分支。这也就是为何,咱们的仓库通常都会有一个 master 分支。若是你在 git init
以后(固然,你其实能够在任意时候对这个文件进行修改),把这个文件的内容改为 refs/heads/test
,那在提交时,默认就会生成 test 分支。笔者强烈建议不要改!会被同事打的! 后续会讲的 git checkout
签出命令,改的就是这个文件的内容。
补充一下,.git/logs/里文件的相关知识点。这个文件夹下的文件,它的内容是长这样的:
cat .git/logs/HEAD
# 首先是40个0,原本是这个记录的 parent 的校验和,可是由于这条记录是第一个,没有父对象,因此就用0占位
# 而后是正常的40位的校验和,这是这个记录自身的校验和
# 而后再是提交者,邮箱,时间
# 最后就是操做类型和 message
0000000000000000000000000000000000000000 9eb3d5fe6014cf11a120a26b3f8b3af2f20964bd kael <kael_yp@foxmail.com> 1590829805 +0800 commit (initial): create hello-git.txt
复制代码
当咱们使用 git reflog
命令时,就是把对应的指针的 logs 文件的内容稍微处理一下显示出来。
最后,笔者就 大概的说一下 Git 基本使用流程 祝你们六一快乐。
git ini
初始化。git add
把修改的内容添加到暂存区。git commit
把暂存区的内容生成快照提交。git status
查看当前的状态。git log
查看历史记录。
世界上只有两种人,一种懂二进制,一种不懂二进制。