當你敲完 git commit 命令後,究竟發生了什麼?

做者:Maxence Poutord
來源:dev.to
翻譯:1024譯站git

現在大多數項目都使用 Git 做爲版本控制系統,這意味着大多數項目都有一個.git文件夾。可是,你有沒有嘗試過打開它?shell

我試過一次……而後在一分鐘內就關掉了!bash

長期以來,我都是把 Git 當作「黑盒」來用的。 微信

git explained

「這是GIT。它經過一個漂亮的分佈式圖論模型跟蹤項目中的協做工做。」
「哇,好酷!怎麼用的呢?」
「母雞。只需記住這些 shell 命令並輸入它們來同步。若是出現錯誤,那就把代碼保存到其餘地方,刪掉項目,再下載一份新的。」app

直到一年前。我對這種知其然、不知其因此然的作法感到厭倦。最後我找到了機會,開始學習它。 我看了《Pro Git》這本書,並進行了大量實驗。我發現它並不像看起來那麼複雜!分佈式

所以,若是你:學習

  • 跟上面那張圖中的人同樣
  • 想了解.git 文件夾裏有什麼
  • 曾經 "在 git 里弄丟過代碼"……

這篇文章就是爲你寫的!fetch

第一步:Git 數據模型

爲了便於理解,我會從一個基礎項目開始(有2個文件: index.js and README.md)。ui

  1. git init
  2. echo "console.log('hi')" >> index.js
  3. echo "# Cool project" >> README.md
  4. git add . && git commit -m "First commit"

如今咱們來看看.git 文件夾裏的內容:spa

$ tree .git/objects

.git/objects
├── 2d
│   └── 1a41ebd2d32cb426b6d32e61e063f330aa1af8
├── 66
│   └── 87898e2a0fe2da282efab6b5f7f38b7f788f56
├── c2
│   └── 43f24fb294ebc954b0a7ee6020589245f78315
├── c3
│   └── 0500babf488d06033e8d039cbf64be3edbd089
├── info
└── pack

6 個目錄,4 個文件

複製代碼

Git 建立了4個文件。爲了不一個文件夾包含太多文件,git 會自動截取前兩個字符做爲文件夾名。要檢索一個 git 對象,必須將文件夾名+文件名拼接起來。

因爲這些文件不具有可讀性,你能夠用命令git cat-file <sha-1> -p 查看裏面的內容(或者用 -t 參數查看類型)。順便提一下,只能用前 8 個字符。

這些文件是這樣互相連接的:

git data model

Git 對象模型有 4 種不一樣類型:

  • commit:包含提交人、日期、消息還有目錄樹
  • tree:引用其餘 tree 和(或)blob
  • blob:存儲文件數據
  • tag:存儲某個提交的引用(本文未涉及)

請注意,blob 不存儲文件名(和位置)。這就是爲何當你更改文件位置時 git 會丟失歷史記錄的緣由之一。

🤔若是你在本機運行,可能會獲得不一樣的 hash 值(做者和日期不同)!

第二步:第二次提交

如今咱們要更新 index.js ,給文件再添加一行:

  1. echo "console.log('world')" >> index.js
  2. git add . && git commit -m "Second commit"

因而又多了 3 個 對象:

$ tree .git/objects

.git/objects
├── 11
│   └── 75e42a41f75f4b25bab53db36d581f72387aa9
├── 2d
│   └── 1a41ebd2d32cb426b6d32e61e063f330aa1af8
├── 66
│   └── 87898e2a0fe2da282efab6b5f7f38b7f788f56
├── c2
│   └── 43f24fb294ebc954b0a7ee6020589245f78315
├── c3
│   └── 0500babf488d06033e8d039cbf64be3edbd089
├── ee
│   └── c2cd5e0b771793e03bbd5f8614c567af964a4e
├── fc
│   └── 512af17ca7ec04be6958047648b32629e4b5a5
├── info
└── pack

9 個目錄,7 個文件

複製代碼

如今咱們獲得這樣的結果:

git data model with 2 commits

這裏有意思了:Git 並無存儲文件之間的差別!幸好有packfiles (位於 .git/objects/pack),Git 在硬盤上保留了一個合理的位置。

第三步:亂改一通

在最後一步,咱們將添加一個提交。而後,咱們將回到過去以「刪除此提交」。

  1. echo "console.log('')" >> index.js
  2. git add . && git commit -m "Third commit"

你可能猜到了,git 建立了3個新文件。結構跟第二步的相似。

.git/objects
├── 00
│   └── ee8c50f8d74eaf1d3a4160e9d9c9eb1c683132
├── 09
│   └── f760de83890e3c363a38e6dc0700b76e782bc1
├── cf
│   └── 81d6f570911938726cff95b62acbf198fd3510
└── ...

12 個目錄, 10 個文件

複製代碼

如今,咱們僞裝想回退一個提交(git reset HEAD~1 --hard)。

git reset --hard

如今你可能會認爲,你搞砸了一切,提交記錄再也找不到了。是否是?

多是。讓咱們看看還有多少個 git 對象……

$ tree .git/objects

.git/objects
└── ...

12 個目錄, 10 個文件

複製代碼

看到沒,咱們還有10個文件!沒有被刪!你猜怎麼着?若是我用命令git cat-file cf81d6f570911938726cff95b62acbf198fd3510 -p,我還能查看第三次提交的 index.js 文件內容。

"在 git 裏你不可能丟失代碼。"

——魯迅

比這更嚴重的是,我天天使用git push --forcegit rebasegit reset --hard,但我歷來沒有丟失任何東西。可是,咱們是人類,人類是容易犯錯的。

別擔憂,若是你想回滾,不用丟棄全部文件。接下來就是見證奇蹟的時刻!

reflog:魔術棒✨

若是你嘗試使用git log檢索歷史記錄,則不會看到「Third commit」 這個提交。可是,若是加了參數-g(表明 --walk-reflogs),就會看到第三個提交。

爲了讓結果更好看,你能夠用 git log -g --abbrev-commit --pretty=oneline

這個超有用的命令有個別名:git reflog ❤️

$ git reflog

eec2cd5 (HEAD -> master) HEAD@{0}: reset: moving to HEAD~1
00ee8c5 HEAD@{1}: commit: Third commit
eec2cd5 (HEAD -> master) HEAD@{2}: commit: Second commit
c30500b HEAD@{3}: commit (initial): First commit

複製代碼

(注意:在 .git/logs/HEAD裏能夠看到相似的結果)

如今,你有了第三個提交的指紋: 00ee8c5。你能夠用 git reset 00ee8c5 --hard 來撤銷以前的重置。

注意事項:

在某些狀況下,git reflog 對你沒有幫助:

  • 當你拉取別人的代碼
  • 當你刪除倉庫並從新克隆
  • 當你查找超過90天前的改動(被git gc清理掉了 )。我不知道大家的狀況,反正我連一個月前作了啥都不記得了。因此記錄保存3個月應該足夠了。

另外,若是你像 ctrl + s同樣使用 git commit,就可能很容易犯迷糊。很抱歉,除了建議你閱讀個人那篇有關 conventional commits 的文章外,我也無能爲力。我認爲這是使用 git 最乾淨的方法。

總結

  • 有4種不一樣類型的 git 對象:committreeblobtag
  • blob 並不存儲文件名(這就是爲何移動文件後會丟失歷史)
  • Git 不存儲文件差別
  • 提交後的代碼不會丟失。git reflog 能幫到你。

今天就到這裏,下課!

獲取更多技術趨勢和資源,歡迎關注微信公衆號:1024譯站

微信公衆號:1024譯站
相關文章
相關標籤/搜索