【摘】Git-從零單排 02期

前言


書接上文,咱們對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 以後,倉庫發生的變化。

  1. 針對有修改的文件 hello-git.txt 的內容,生成一個 blob 對象,這個對象能完美還原 hello-git.txt 文件當下的內容。
  2. 針對 blob 對象,外加一個頭部信息(header)一塊兒作 SHA-1 校驗運算,得的校驗和(一個長度爲40的字符串)。並使用校驗和的前兩位作文件夾名,在這個文件夾裏,存入使用後38位作文件名的 blob 對象。
  3. 生成 .git/index 文件,.git/index 文件的文本編碼是 ISO88591 ,而且把 blob 對象的校驗和、文件名寫入到 .git/index 裏。咱們能夠經過 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 以後,多瞭如下文件

  • COMMIT_EDITMSG
  • logs/HEAD
  • logs/refs/heads/master
  • refs/heads/master
  • objects/9e/b3d5fe6014cf11a120a26b3f8b3af2f20964bd
  • objects/62/01f623b63c5736c92895712c6038835be6a7f2


經過對比,筆者理解的 git commit 命令執行以後,Git 作了以下事情:

  1. 生成 .git/COMMIT_EDITMSG 文件,把  -m 參數以後的 commitMessage 寫入到這個文件。
  2. 根據項目目錄,計算獲得一個 treeObject,並把這個對象寫入 .git/objects/ 裏,對應的就是多出了 objects/62/01f623 。
  3. 根據暫存區的內容、git 配置內容、執行時間、commitMessage、treeObject等,生成一個 commitObject。並把這個對象寫入到 .git/objects/ 裏。對應的就是多出了 objects/9e/b3d5fe 。
  4. 修改 .git/HEAD 文件裏的分支指針所指向的 commitObject,也就是把 9e__b3d5fe6014cf11a120a26b3f8b3af2f20964bd_ 寫入到 .git/_refs/heads/master 文件裏。若是沒有 _.git/_refs/heads/master 文件,就生成一個,因此這裏會新增 _.git/_refs/heads/masterr 文件。
  5. 把當前的這個操做寫入到對應的指針的 logs 文件裏,這裏相關的指針只有兩個,一個是 HEAD,另外一個是 master。若是指針的 logs 文件不存在,就新建對應的文件。因此就新增了,.git/logs/HEAD 和 .git/logs/refs/heads/master。這個文件裏的內容就是本地 reflog 記錄,並非後文講的 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。

  • tree 屬性是目錄結構,裏面可能會有 tree 和 blob
  • parent 是父提交對象,這個屬性可能有,或沒有。其實還有可能會有第二父提交對象。
  • blob 的內容記錄的文件內容


若是你不想使用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。能夠跟鏈表對應理解。

未命名.001.jpeg

補充一下,.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 基本使用流程 祝你們六一快樂

  1. 在項目目錄下使用 git ini 初始化。
  2. 在項目內編輯文件,而後使用 git add 把修改的內容添加到暫存區。
  3. 而後使用 git commit 把暫存區的內容生成快照提交。
  4. 可使用 git status 查看當前的狀態。
  5. 可使用 git log 查看歷史記錄。


世界上只有兩種人,一種懂二進制,一種不懂二進制

相關文章
相關標籤/搜索