歡迎光臨
每天分享高質量文章

如果非得瞭解下 git 系統… – 實踐篇

(點選上方公眾號,可快速關註)


來源:野獸 ,

www.cnblogs.com/ys-ys/p/9546160.html

git的定義是一個內容定址檔案系統。內容、定址、檔案、系統,該來的總會來的…

本文旨在透過實踐來介紹.git檔案夾中的目錄及檔案功能,屬git基礎知識。但在此基礎上可解決各git使用過程中可能遇到的問題,如“.git檔案夾佔用空間大”,“git如何找回丟失的物件(提交)”,”git diff 對比依據是什麼”等,

話不多說,擼起袖子就是乾,來看看 .git 是個啥樣,這些個貨色都是幹嘛的。

# 初始化git,檢視內容

git init 產生一個.git隱藏檔案夾

cd .git

ls -F1

# 初始化時的.git長這樣

# HEAD

# config

# description

# hooks/

# info/

# objects/

# refs/

# 日常工作中的.git大概長這樣

# COMMIT_EDITMSG

# config

# description

# hooks/

# index

# info/

# logs/

# objects/

# refs/

隨著之後的多種 git操作 ,還會存在如 MERGE_HEAD 、 MERGE_MODE等和 COMMIT_EDITMSG 類似功能的檔案,branches(分支資訊)、lost-found(儲存被懸掛起/丟失的提交物件)、packed-refs(壓縮後的refs記錄)等和logs、objects類似功能的檔案夾。

以上面的.git為例做個簡單的介紹:

COMMIT_EDITMSG 最近一次的 commit edit message ;

# 編寫提交資訊的兩種姿勢

git commit

# 自動開啟檔案編輯,在檔案中輸入資訊即可

git commit -m msg

# 資訊都會被儲存到 COMMIT_EDITMSG

# 如需修改提交資訊,不可直接編輯COMMIT_EDITMSG,可執行以下命令

git commit –amend 

# 自動開啟檔案編輯,在檔案中修改資訊即可

description 描述檔案,開啟后里面提醒該為git倉庫建立個描述(Unnamed repository; edit this file ‘description’ to name the repository.)。

config 檔案包含專案特有的配置選項,如一些常用項:

  • [core] ignorecase 是否忽略檔案大小寫;

  • [remote “origin”] url 配置遠端倉庫地址;

  • [remote “origin”] fetch 遠端分支對映關係;

  • [user] name 使用者名稱

  • [user] email 郵箱

  • [alias] 命令別名配置 : cmt = commit

抑或更多其他config引數…

全域性配置檔案在 ~/.gitconfig ,Windows應該是在Users/Administrator/.gitconfig。

info/ 檔案夾用以儲存一些有關git倉庫的資訊,如exclude

# 包含一個全域性性排除(global exclude)檔案,用以放置那些不希望被記錄在 .gitignore 檔案中的忽略樣式(ignored patterns);

echo for git ignore > git-ignore

echo for git exclude > git-exclude

echo git-ignore > .gitignore

git status

     

# On branch test

# Untracked files:

# (use “git add …” to include in what will be committed)

 

# .gitignore

# git-exclude

 

# git-ignore已被忽略,還有2個untracked的檔案

 

cd .git

cd info/

vi exclude

     

# 最後一行新增 git-exclude

     

cd ../..

git status

# On branch test

# Untracked files:

# (use “git add …” to include in what will be committed)

 

# .gitignore

 

# git-exclude 已被忽略,只有.gitignore還是untracked

除exclude檔案外,還可能會有refs、grafts,attributes等檔案

hooks/ 檔案夾包含客戶端或服務端的鉤子指令碼(hook scripts),如pre-commit,post-receive等:

vi .git/hooks/pre-commit

# 儲存以下資訊  第一行指定用什麼執行

#!/bin/sh

echo “Message for pre commit”;

exit 1;

# 開執行許可權

chmod +x .git/hooks/pre-commit   # user+group+other 執行x(1)許可權  ,備註 r(4,read),w(2,write),x(1,execute),-(0,no permission),s(special)

     

touch commit-hook-test

git add .

git commit -m “pre-commit test“

# 你會看到以下資訊

# Message for pre commit

 

# 如果不exit,則繼續執行,詳情可參考.git/hooks/下的*.sample檔案

# Message for pre commit

# [test 038e6ec] pre-commit test

# 1 file changed, 1 insertion(+), 1 deletion(-)

logs/ 放置git倉庫操作記錄的檔案夾,包含HEAD檔案 和 refs檔案夾。

HAED 檔案包含對 git分支 的操作記錄,如

vi HEAD

# 99a10c283c33beed7f31c210a6c8b411d2a31085 5daf6094ea2cc60d17e947c0435096a4bdafe82d yeshou 1535082919 +0800       commit: rm files

# 5daf6094ea2cc60d17e947c0435096a4bdafe82d b8e02a5f9c2bf44342d15f5ea1e60ffd9434765a yeshou 1535087285 +0800        checkout: moving from test to master

# 先是刪除檔案後提交了次,再是由test檢出到master

refs 檔案夾包含 heads 檔案夾,remote檔案夾。heads 記錄本地相關的各 git分支 操作記錄,remote 記錄遠端倉庫相關的各 git分支 操作記錄

cd heads

ls

# master   master分支操作記錄

# test     test分支操作記錄

 

cd remote/origin

ls

# master  遠端master分支操作記錄

# test    遠端test分支操作記錄

HEAD 檔案指示當前被檢出(所在)的分支,如當前在test分支,檔案內容則為ref: refs/heads/test。

index 檔案是當前版本的檔案索引,包含生成當前樹(唯一確定的)物件的所虛資訊,可用於快速比對工作樹和其他提交樹物件的差異(各commit和HEAD之間的diff),可用於儲存單檔案的多個版本以有效的解決合併衝突。可使用git ls-files 檢視index檔案內容。如:

git commit 的一次提交從index中的資訊生成tree物件,將其儲存在物件資料庫中,並與本次新的commit做關聯,產生本次commit的tree資訊(下麵的objects介紹中會提到commit和tree)。

refs/ (references) 檔案夾儲存指向資料(分支)的提交物件指標;其中 heads 檔案夾記錄內部檔案對應名稱的分支的提交物件;tags記錄內部檔案對應名稱的標簽的提交物件;remotes記錄內部檔案對應名稱的遠端倉庫分支的提交物件;

舉個例子:

cat .git/refs/heads/master

 # ce1fed3fdbaf12e816e3028055f9feee57b33b45 當前master的提交記錄

 git checkout -b test # 檢出一個新分支

 find .git

 # 多了個 .git/refs/heads/test 檔案

 git log

 

 # commit 63a85dcbc6978f2d43996f5bebc38993c2afadaa (HEAD -> test)

 # Author: yeshou

 # Date:   Sat Aug 25 13:57:26 2018 +0800

 #

 # branch test : edit test : add line : write d

 

 # commit ce1fed3fdbaf12e816e3028055f9feee57b33b45 (master)

 # Author: yeshou

 # Date:   Sat Aug 25 12:40:35 2018 +0800

 # 

 # edit test : add line : write c

 

 cat .git/refs/heads/test

 # 63a85dcbc6978f2d43996f5bebc38993c2afadaa   當前test的最近提交記錄

 cat .git/refs/heads/master

 # ce1fed3fdbaf12e816e3028055f9feee57b33b45 依然是當前master的最近提交記錄

另兩者亦然。

objects/ 檔案夾用以儲存git倉庫中的所有資料內容。

一步步來…

先看看這個檔案夾裡是怎麼存資料內容的,再理解這些資料內容又是什麼。

# 為了清晰的看明白objects檔案夾中檔案的生成,初始化.git

rm -rf .git

git init  

cd .git 

find .git # 這時候objects檔案夾下只有pack和info 兩個空檔案夾

touch test

vi test

# 輸入 a ,儲存,退出

git add test

find .git

# objects 下多了個檔案夾,且裡面有檔案

# .git/objects/44

# .git/objects/44/2406aa9341668f9c43c2d5378a777ad69324a0

驗證下這個檔案內容是什麼,註意,這是個二進位制球,呸…是檔案,文明觀球,呸…是觀看。這裡我們用傳說中git中的手術刀( git cat-file )來解剖git檔案。

git cat-file -p 442406aa9341668f9c43c2d5378a777ad69324a0

# 輸出 a

9de29bb2d1d6434b8b29ae775ad8c2e48c5391 是個二進位制檔案,是git物件中的blob物件,它記錄了當前版本的該檔案的資料內容,並以SHA-1計算產生一個40個字元的校驗和。 根據官方描述:這是一個 SHA-1 雜湊值——一個將待儲存的資料外加一個頭部資訊(essay-header)一起做 SHA-1 校驗運算而得的校驗和。

繼續操作,修改test

vi test

# 第二行輸入 b ,儲存,退出

git add test

find .git

# objects 下又多了個檔案夾,且裡面也有檔案

# .git/objects/bf

# .git/objects/bf/daa0f1c3415c09d3080063911d155fd7259d18

這次的二進位制檔案的資料內容是 a (手動換行) b 。

繼續走下去:

git commit -m “add test”

# wtf,怎麼突然生出2個檔案夾,來來來,看看內容

# .git/objects/3e

# .git/objects/3e/5f95cd5c4f0ff429522b0fdfeda9369f92d89c

# .git/objects/fd

# .git/objects/fd/1332e4e95f8a64682c1516e175abb66b6f6325

git cat-file -t fd1332e4e95f8a64682c1516e175abb66b6f6325

# commit

git cat-file -p fd1332e4e95f8a64682c1516e175abb66b6f6325

# tree物件、作者、提交者、提交日期、提交資訊、父物件(有的話帶一個或多個父物件)

# tree 3e5f95cd5c4f0ff429522b0fdfeda9369f92d89c 

# author yeshou 1535168447 +0800

# committer yeshou 1535168447 +0800

# add dir-test    

git cat-file -t 3e5f95cd5c4f0ff429522b0fdfeda9369f92d89c

# tree

git cat-file -p 3e5f95cd5c4f0ff429522b0fdfeda9369f92d89c

# 100644 blob bfdaa0f1c3415c09d3080063911d155fd7259d18    test

git cat-file -p bfdaa0f1c3415c09d3080063911d155fd7259d18

# a (換行) b

3e5f95cd5c4f0ff429522b0fdfeda9369f92d89c 也是個二進位制檔案,是git物件中的tree物件,記錄著blob識別符號、路徑名和在一個tree下的所有檔案的元資料。

fd1332e4e95f8a64682c1516e175abb66b6f6325 又是個二進位制檔案,是git物件中的commit物件,它記錄了當前版本的一次提交資料內容,包含tree物件、作者、提交者、提交日期、提交資訊、父物件(有的話帶一個或多個父物件)。

然後,pack檔案夾是幹嘛的?

隨著objects檔案夾下的檔案夾和檔案不斷生成(也就是N多次的commit之後),objects檔案夾明顯會”長大”,這時開發者可以用 git gc 來對之前的操作的物件做整理壓縮。

pack 檔案夾內有2個檔案 pack-(SHA-1).pack 和 pack-(SHA-1).idx 前者是以壓縮形式儲存之前記錄物件的檔案,後者用以儲存訪問索引的檔案。

舉個例子:

git gc 

# Counting objects: 12, done.

# Delta compression using up to 4 threads.

# Compressing objects: 100% (4/4), done.

# Writing objects: 100% (12/12), done.

# Total 12 (delta 0), reused 0 (delta 0)

find .git

# 發現少了很多objects裡的檔案夾和檔案,多了以下2個檔案

# .git/objects/pack/pack-2021ec3cb18c796fdfca8ef616fb6a20b1449ab1.pack

# .git/objects/pack/pack-2021ec3cb18c796fdfca8ef616fb6a20b1449ab1.idx

git verify-pack -v .git/objects/pack/pack-2021ec3cb18c796fdfca8ef616fb6a20b1449ab1.idx

# 列出之前存在objects裡的所有運算元據內容

# 655a12c9b83a029bb46fa852ea15e6affd1587d8 commit 167 117 510

# …

# 616dfdb2643c725fa1027ecef76d49d482d9e26d tree   32 43 670

# …

# bfdaa0f1c3415c09d3080063911d155fd7259d18 blob   5 14 853

# 也可以透過後面加 | grep keyword 來搜尋所需的內容,如下列出所有commit記錄

git verify-pack -v .git/objects/pack/pack-2021ec3cb18c796fdfca8ef616fb6a20b1449ab1.idx | grep commit

git gc ( garbage collect )命令將會收集所有鬆散物件並將它們存入 pack,合併這些 pack 進一個大的 pack,然後將不被任何 commit 取用並且已存在一段時間 (數月) 的物件刪除,除此之外還會將所有取用 (references) 併入一個單獨檔案(上面有提到隨著各種操作,.git下還會產生更多檔案夾,.git中的packed-refs檔案夾就是這時候產生的)。該命令可能透過修改配置中的 gc.auto 和 gc.autopacklimit 來調整操作閾值。註意:git gc 呼叫的也是 git prune ,如有需求也可關註這個命令。

至於”info檔案夾是幹嘛的?”這個問題還未知…  官網的描述也沒看懂,也沒查到或者在專案中實際出現這個檔案夾有存在什麼檔案,要麼等遇到再說?

至此git物件中的三個物件已經知道是咋回事了,還剩個tags物件,簡單介紹下。

tags物件通常也是一個commit物件,指的是一個指定了開發者可讀名稱的一個特殊物件,如有需要也可透過 git cat-file 來解析探索。

其間關係大致如下:

|- commit       aaaaa…

    |- tree       abbbb…

        |- blob       acccc… (可能是這次修改的)

        |- blob       adddd…(也可能是上次修改的)

        |- tree        aeeee…  

            |- blob        affff…

 

|- commit        bbbbb…

關係圖的話,這個是git官網的…  和上面的結構是一樣的。

基於objects的介紹再回過頭來看看”內容、定址、檔案、系統”便比較清晰了:以git物件作為內容,透過唯一的校驗和定址,檔案形式儲存的一個版本控制系統。

瞭解完這些,主要還是希望能夠運用到實際生產中來解決問題。如 “專案中.git檔案為什麼這麼大?怎麼處理?”

可能的處理方案:

1. 執行 git gc ,如果壓縮後能達到預期效果,則不做過多處理

2.針對歷史記錄中對某些大檔案的取用,則刪除對應取用的物件,操作如下

git gc

git count-objects -v

git verify-pack -v .git/objects/pack/pack-(SHA-1).idx | sort -k 3 -n | tail -5

# 前面用過了git verify-pack,可知第三列資訊表示的檔案大小,這裡用tail取前5個較大檔案的記錄

git rev-list –objects –all | grep (SHA-1)

# 使用 git rev-list –objects -all 來檢視指定 (SHA-1) 物件資訊

# ce1fed3fdbaf12e816e3028055f9feee57b33b45 xxx.mp4  比如是個影片檔案

git log –pretty=oneline –branches — xxx.mp4

# 找出哪些 commit 修改/操作了這個檔案

# 94cbe08e… add xxx.mp4

git filter-branch –index-filter ‘git rm –cached –ignore-unmatch xxx.mp4’ — 94cbe08e^..

# 刪除檔案取用,rewrite 資訊…

rm -rf .git/refs/original

rm -rf .git/logs/

git gc

# 刪除 .git/refs/original 和 .git/logs/ ,處理其中對xxx.mp4檔案仍存在的取用,之後repack倉庫

git count-objects -v

# 檢查下操作後檔案大小,或者直接在.git目錄下執行 du -h -d 1 檢視1級目錄/檔案的大小

3.若還是難處理,或者不好處理,或者不想刪除大檔案的取用,則備份一份.git,然後初始化git倉庫,操作如下

# 除去備份操作,備份操作使用者自定義

rm -rf .git

git init

git remote add origin xxx.git 

# 重新指向新的遠端倉庫地址,也可根據上文所說修改config檔案來指定

參考

  • 關於git hooks,參考Customizing-Git-Git-Hooks

    https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks

  • 關於更詳細的.git檔案夾,參考 Gitrepository-layout-objectsinfo

    https://git-scm.com/docs/gitrepository-layout#gitrepository-layout-objectsinfo

【關於投稿】


如果大家有原創好文投稿,請直接給公號傳送留言。


① 留言格式:
【投稿】+《 文章標題》+ 文章連結

② 示例:
【投稿】《不要自稱是程式員,我十多年的 IT 職場總結》:http://blog.jobbole.com/94148/

③ 最後請附上您的個人簡介哈~



看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂