1. 程式人生 > >Git彙總--物件及版本庫儲存

Git彙總--物件及版本庫儲存

下述內容為團隊內部分享整理所得,實用性較強,整體性偏差!
PS:關於完整的Git內容,請參照之前發表過一系列文章,詳見:Git Pro深入淺出(一)Git Pro深入淺出(二)Git Pro深入淺出(三)
推薦兩個地址:ProGitGotGit

閱讀完內容,你會很快的解決下面問題,並瞭解其底層原理。

問題1:如何丟棄本地工作區修改的內容?

$ git checkout -- <filename>

問題2:如何丟棄本地工作區和暫存區修改的內容?

$ git checkout HEAD <filename>

問題3:誤刪了檔案且已經提交推送到遠端倉庫,如何恢復?

# 檢視遠端前一次提交的檔案樹 支援管道過濾 | grep <filename>
$ git ls-files --with-tree=origin/HEAD^
# 檢視前一次提交的指定檔案內容 > welcome.txt
$ git cat-file -p origin/HEAD^:<filename>
# 當然,也可以採用reflog形式

問題4:如何忽略某檔案?如何只讓其本地生效?

.git/info/exclude中配置

簡介

Git作者Linus Torvalds,其是一款分散式版本控制系統。

  • CVS:集中式版本控制系統。CVS採用客戶端/伺服器架構設計,版本庫位於伺服器端,實際上就是一個RCS檔案容器。每一個RCS檔案以“.v
    ”作為檔名字尾,用於儲存對應檔案的歷次更改歷史。RCS檔案中只保留一個版本的完全拷貝,其他歷次更改僅將差異儲存其中,使得儲存變得更加高效。每個檔案都擁有各自獨立的版本號。
  • SVN:集中式版本控制系統。擁有全域性版本號,每提交一次,SVN的版本號就會自動加一。利用輕量級拷貝,SVN在不同的名字空間下建立不同的目錄實現里程碑和分支的建立,輕鬆地解決了CVS中存在的里程碑、分支建立速度慢又不可見的問題。SVN還有一個突破,就是在工作區跟蹤目錄(.svn目錄)下為當前目錄中的每一個檔案都儲存一份冗餘的原始拷貝(工作區的根目錄和每一個子目錄下都有一個.svn目錄)。這樣做的好處一個是提高了網路的效率,在提交時僅傳輸變更差異,另外一個好處是部分操作不再需要網路連線,如本地修改的差異比較,以及本地更改的回退等。
  • Git:分散式版本控制系統。每個人都擁有一個完整的版本庫。分散式版本控制系統的幾乎所有操作包括檢視提交日誌、提交、建立里程碑和分支、合併分支、回退等都直接在本地完成而不需要網路連線。協同工作模型(版本庫間推送、拉回,及補丁檔案傳送等)讓開源專案的參與度有爆發式增長。

git.png

Git物件

git init 會建立一個 .git 目錄。這個目錄包含了幾乎所有 Git 儲存和操作的物件。 如若想備份或複製一個版本庫,只需把這個目錄拷貝至另一處即可。

$ ll .git
-rw-r--r--    1 ligang  staff     6B 11  1 10:13 COMMIT_EDITMSG
-rw-r--r--    1 ligang  staff   212B 10 31 10:02 FETCH_HEAD
-rw-r--r--    1 ligang  staff    23B  8 21 15:23 HEAD
-rw-r--r--    1 ligang  staff    41B 10 31 10:02 ORIG_HEAD
drwxr-xr-x    2 ligang  staff    64B  8 15  2017 branches
-rw-r--r--    1 ligang  staff   311B  8 15  2017 config
-rw-r--r--    1 ligang  staff    73B  8 15  2017 description
drwxr-xr-x   31 ligang  staff   992B  5 10 14:44 hooks
-rw-r--r--    1 ligang  staff   6.9M 11  1 10:13 index
drwxr-xr-x    4 ligang  staff   128B  5  9 16:11 info
drwxr-xr-x    4 ligang  staff   128B  5  9 16:11 logs
drwxr-xr-x  152 ligang  staff   4.8K 11  1 10:13 objects
-rw-r--r--    1 ligang  staff   166B  5  9 16:11 packed-refs
drwxr-xr-x    6 ligang  staff   192B  7  3 09:53 refs

-rw-r–r--:第一位用於標識檔案型別,d表示目錄、l表示連結檔案、-表示檔案;其他表示系統使用者許可權rw-rw-(Owner)r–(Group)r–(Other)【r可讀、w可寫、x可執行】

版本庫位於工作區根目錄下的.git目錄中,且僅此一處,在工作區的子目錄下則沒有任何其他跟蹤檔案或目錄。Git的這種設計,將版本庫放在工作區根目錄下,所有的版本控制操作(除了和其他遠端版本庫之間的互操作)都在本地即可完成。

$ git log -1 --pretty=raw
# 本次提交的唯一標識
commit b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
# 本次提交所對應的目錄樹
tree 1e0c5cb85e1d2b4ff6875a5bbaa9183389ace668
# 本地提交的父提交(上一次提交)
parent 3365948518aad171336a52674cbdf0450679b4dc
author ligang <[email protected]> 1543761152 +0800
committer ligang <[email protected]> 1543761152 +0800

    feat(git): git 彙總

研究Git物件ID的一個重量級武器就是git cat-file命令。

檢視一下這三個ID的型別:

$ git cat-file -t b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
commit
$ git cat-file -t 1e0c5cb85e1d2b4ff6875a5bbaa9183389ace668
tree
$ git cat-file -t 3365948518aad171336a52674cbdf0450679b4dc
commit

檢視物件的內容:

$ git cat-file -p <commitID>

Git 有一個底層命令git rev-parse 可以用於顯示引用對應的提交ID

$ git rev-parse master
b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
$ git rev-parse refs/heads/master
b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
$ git rev-parse HEAD
b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259

可以看出它們都指向同一個物件!

git-objects.png

  • 顯示版本庫.git 目錄所在的位置
$ git rev-parse --git-dir
/Users/ligang/Documents/github/practice/.git
  • 顯示工作區根目錄
$ git rev-parse --show-toplevel
/Users/ligang/Documents/github/practice

git rev-parse 是Git的一個底層命令,其功能非常豐富(或者說雜亂),很多Git指令碼或工具都會用到這條命令。

  • 顯示分支 $ git rev-parse --symbolic --branches
  • 顯示tags $ git rev-parse --symbolic --tags
  • 顯示HEAD對應的SHA1雜湊值 $ git rev-parse HEAD

版本庫儲存

本地(工作區、暫存區、HEAD)

工作區、版本庫、暫存區原理圖.png

說明
工作區
Git暫存區(stage,或稱為index)
HEAD(當前分支,注意非遠端
  • HEAD實際是指向master分支的一個“遊標”,HEAD全部可以使用master替換

  • objects為Git的物件庫,位於 .git/objects 目錄下;

  • 工作區 <==> 暫存區

    命令 說明
    git add ./<filename> 將工作區變更提交到暫存區
    git checkout ./<filename>
    git checkout -- <filename>
    暫存區內容覆蓋工作區
    git rm --cached <file> 直接從暫存區刪除檔案,工作區則不做出改變
  • 暫存區 <==> HEAD

    命令 說明
    git commit -s -m "" 將暫存區提交到master分支
    (即master指向的目錄樹就是原暫存區的目錄樹)
    git reset HEAD ./<filename> 使用master指向的目錄樹替換快取區
    git checkout HEAD ./<filename> 用HEAD指向的master分支內容替換暫存區及工作區的檔案

重點來了! 如何還原本地工作區某檔案

$ git reset HEAD <filename>
$ git checkout <filename>

# 替換命令
$ git checkout HEAD <filename>

本地(stash)

git stash 儲存當前工作進度,會分別對暫存區和工作區的狀態進行儲存。

$ git stash [save [–patch] [-k|[no-]keep-index] [-q|–quiet] [<message>]]
  • save "message..." 儲存工作進度時使用指定說明
  • --patch 會顯示工作區和HEAD的差異,通過對差異檔案的編輯決定在進度中最終要儲存的工作區的內容
  • -k或者--keep-index引數,在儲存進度後不會將暫存區重置。預設會將暫存區和工作區強制重置!

注意: 本地沒有被版本控制系統跟蹤的檔案並不能儲存進度,即新建立檔案需要 git add

恢復工作進度,可以通過下述命令:

$ git stash apply [–index] [<stash>]
$ git stash drop [<stash>]
# 等價於上述兩條命令
$ git stash pop [–index] [<stash>]

遠端(remote)

$ cd .git/refs/remotes
$ ll
drwxr-xr-x   5 ligang  staff   160B 11 28 10:42 origin
drwxr-xr-x  11 ligang  staff   352B 11 27 00:11 upstream
$ ll origin 
-rw-r--r--  1 ligang  staff    32B 11  8 09:43 HEAD
-rw-r--r--  1 ligang  staff    41B 11 28 10:42 feature-v2.1
-rw-r--r--  1 ligang  staff    41B 11 28 10:42 master
$ ll upstream
-rw-r--r--  1 ligang  staff    41B 11 27 00:11 develop
-rw-r--r--  1 ligang  staff    41B 11 27 00:11 master
-rw-r--r--  1 ligang  staff    41B 11 27 00:11 themes
$ cat origin/develop
eeaa2013d901bda74eaa9fe102abe1e474b7a5d6
$ git ls-tree eeaa2013d901bda74eaa9fe102abe1e474b7a5d6

Git 這樣的設計是非常巧妙的,在向遠端版本庫執行獲取操作時,不是把遠端版本庫的分支原封不動地複製到本地版本庫的分支中,而是複製到其名稱空間中。如在克隆一個版本庫時,會將遠端分支都複製到目錄 .git/refs/remotes/origin/ 下。這樣向不同的遠端版本庫執行獲取操作,因為遠端分支相互隔離,所以就避免了相互的覆蓋。

  • 新增新遠端版本庫
$ git remote add remote-name [email protected]:project-namespace/project-name.git
$ git remote -v