初中高級的 git 和 gerrit 技巧【大型項目實戰總結 && CR 經驗】

前半部分屬於基礎,後半部分屬於進階。從初級到中級再到我都 hold 不住的高級。全文共 12000 餘字,超幹超乾的那種。 然而,寫完一半的時候,我忽然虎軀一震,我是否是在造輪子?隨後我悄悄的搜了一下 git 。 嗯?這麼多 git 文章,我滴天呢,我陷入了沉思,我皺着眉頭點了幾篇文章,有號稱封山之做的2萬字真理,也有完整詳細的 git 系列教程。好像有點輪子啊,可是我繼續看了下他們的內容後,有種茅廁頓悟般的驚喜,由於我發現個人文章仍是很獨樹一幟的。最後得出一個結論,我沒有造輪子,這是一篇高可用高擴展高性能的 gitgerrit 文章。用實戰去推進思考,kill 掉大衆化的 git 知識,從經常使用的角度去擴展深層的知識,進而抽象出咱們能夠理解掌握的 git 奧祕。不拘泥於 API ,不畏懼其餘輪子,不要慫,就是幹。php

本文是站在別人的 commit 上去 mergepatch 我本身獨具特點的理解,從而生成一個更好的 commit ,而後留給你們往後更好的 mergepatch ,技術在一次次 patch 中不斷進步。html

開門見山

在實際項目開發中,能靈活的使用 gitgerrit 是一個很重要的事情,一方面能夠提升團隊開發效率,另外一方面能夠把控項目的代碼質量。不過對於 gerrit , 可能一些同窗沒有用過,還有 git 的一些你可能沒有掌握的技巧,今天就一塊兒分享給你們,分享的同時,本身也有不少即時收穫。前端

PS: 爲何我會說我本身也會有不少收穫呢,由於是這樣的:當我選擇寫一篇博客的時候,我會本身先去深刻的理解我寫的這篇博客的相關知識點,在深刻理解這個過程當中,我會去閱讀各類資料,而後去分析,最後總結出屬於我本身特點的學習心得,這對我來講,就是一種即時收穫和高級進階。vue

爲何會出現 git

這裏咱們用 git , 咱們就應該去了解一下 git 出現的背景,具體故事不說了,自行維基。這裏我簡單說一下 git 的出現,在技術層面上的背景。react

git 是一個開源的 分佈式版本控制系統 ,源碼也在 github 上開源了,能夠自行搜索。提到分佈式版本控制系統,那應該聯想到 集中式版本控制系統 ,具備表明性的好比 SVNSVN 的全稱是 Subversionlinux

那這二者究竟有什麼區別呢?用兩張圖對比一下:webpack

SVN: git


GIT: 程序員

從圖中基本能夠分析出二者的主要區別。好比:github

  • git 能夠離線開發,svn 不能離線
  • git 處理 merge 的優點碾壓 svn

固然其餘的區別還有不少,好比 git 的內容更完整,使用了 SHA-1 算法,git 能夠更加靈活的操做 branch 。 等等,這裏就不造輪子了,參考下面這篇博客:

SVN與Git比較的優缺點差別

看到這裏,咱們可能比較偏向於, SVNGit 差一些的觀點,但其實這是兩種不一樣的版本控制系統,不能簡單的認爲誰好誰壞,這裏有一篇爲 SVN 洗白的博客,挺有趣的,你們能夠看看:

90%人都不知道:SVN 和 Git 的一些誤解和真相

要是高度總結一下,那就是: SVN 更適用於項目管理, Git 更適用於代碼管理。

爲何會出現 gerrit

咱們看維基介紹:

言簡意賅:從維基上可知,Gerrit 是一種開源的代碼審查軟件,專門用來 CR 的。

版本控制系統

版本控制系統的三板斧

這裏說一下版本控制系統的三板斧:

第一板斧: 存儲內容

第二板斧: 跟蹤內容更改

第三板斧: 與協做者分發內容和歷史記錄

理解了上面的三板斧,你就理解了版本控制系統的精髓,這裏先不作解釋,繼續閱讀,而後本身體會。

版本控制系統發展史

如今和未來的前端,必定是和 git 打交道最多的行業之一,上面提到了版本控制系統,那爲了擴展版本控制的知識,咱們有必要去了解一下版本控制系統的發展歷史,歷史大體是這樣的:

從 手動 copy diffpatch 引入了互斥寫入機制的 RCS再到 第一次引入了 branch 概念的 CVS再到 實現原子操做級別合併的 SVN再到 如今的新皇登基 git 。每個時代,都有本身的那一份驕傲,這裏推薦一篇很是很是好的博客:

博主大佬:Vamei

博文:版本管理三國志 (CVS, Subversion, git)

這篇文章簡直把版本控制系統的整個歷史解釋的堪稱完美,從最開始的我的手工 copy 壓縮打包,到後面的經過 diffpatch ( 也就是咱們常說的 補丁 ),而後經過郵件進行傳達 patch 。而後繼續說了 rcs cvs svn git 在說到 git 時,解釋之精妙,使人佩服。

這裏我修改一下文中最後一段:

和三國志不一樣, VCS 的三國尚未決出最終勝負,或許 SVN 會繼續在一些重要項目上發揮做用,可是 git 最終會一統江山,至少會一統前端江湖。

git 和 gerrit 命名的由來

有時候,咱們可能對爲何叫 gitgerrit 不怎麼在乎。可是不少命名都是有本身的故事的,好比 vue , react 爲何這樣命名。能夠去查閱資料瞭解一下,這有助於咱們更形象化的理解它們。

git 的命名

好比說,git 一詞的由來,能夠從維基百科上的一段話能夠看出:

Quoting Linus: "I'm an egotistical bastard, and I name all my projects after myself. First 'Linux', now 'Git'".('git' is British slang for "pig headed, think they are always correct, argumentative").

翻譯一下就是:我是一個自負的混蛋,我把本身的全部項目命名爲本身。首先是 "Linux" ,如今是 "Git" 。( git 在英國俚語中是豬頭,認爲他們老是正確的,有爭議的 )。

是否是發現其實命名也是有本身的故事的。

再舉個例子,好比 MySQL 中的 My 並非 個人 的意識。MySQL 的命名由來是這樣的,維基上有介紹:

Its name is a combination of "My", the name of co-founder Michael Widenius's daughter,[7] and "SQL", the abbreviation for Structured Query Language.

gerrit 的命名

因爲已經說了 git 的命名由來了,這裏我就言簡意賅點,gerrit 的命名來自於荷蘭設計師赫裏特·裏特費爾德( Gerrit Rietveld ) 的名字首個單詞。

爲何要用 git

這也是一個值得思考的問題,咱們爲何要用 git

直覺上,咱們天然而然的用了,發現也很好用。那咱們能夠問一下本身,git 爲何很好用,若是咱們看了上面提到的博客,可能咱們已經有了答案,其實不少很棒的東西的誕生,都是在誕生的某個維度背景下,解決了大部分同類沒有解決掉的痛點。

因此如今咱們用了 git ,咱們也以爲很好用,可是事實上咱們好像並不清楚 git 的出現,解決了什麼樣的痛點,咱們只知道好用。我說這句話,就是想說明一下,去了解一個東西,最好去了解這個東西誕生時所處的時代背景或者技術背景。哎,好像我沒有回答爲何要用 git ? 不慌,問題不大,其實答案已經在前面提到了。

談談 git-flow 流程

網上有不少 git-flow 開發流程的博客,這裏不進行講解了,可是我想講的就是:

總結出一個符合本項目的 git 開發模式,纔是真正意義上的 git-flow

如何去制定一個好的 git-flow

目前的代碼託管平臺主要有:githubgitlabCoding 、 碼雲 。 這是我知道的主流的代碼託管平臺( 排除 bitbucket ,由於國內用的很少)。因爲最近 github 容許我的開發也能夠創建私有倉庫,那也就說明這四個代碼託管平臺均可以避免費創建私有倉庫了,這算是一個重要時刻吧。

參與了幾個項目後,我在想一個事情,就是什麼樣子的開發模式 ( 只針對開發 ) 纔是一個好的開發模式,最後我得出一個關鍵的因素,那就是:

一個好的開發模式,能夠提升團隊的開發效率,同時提升團隊的代碼質量。 ( 這不是廢話嗎,手動滑稽 )

咱們上面提到的,無論是 svn 仍是 git , 都是爲了優化現有的開發模式。那麼,如何去按照本項目的特色去制定屬於本項目 stylegit-flow 呢?下面我會分享一些我本身的見解。

項目背景

目前參與一個前端開發者達到幾十人的一個大型項目,使用的是 git 版本控制。本人負責給項目加上 gerrit 和 幫助其餘開發者平穩過渡到 gerrit 開發模式中,說通俗點就是:

有啥 gitgerrit 操做問題,我負責解決。

熊和魚掌不可兼得

根據個人經驗,若是要提升團隊的代碼質量,那必定會下降團隊的開發效率,也就是在平均時間內,工做產出會下降。

爲何這樣說呢?由於這是合理的,我用 V8 來舉個例子:

V8 引擎來講,V8JS 代碼的優化,並無一網打盡似的所有采用 JIT編譯器 進行優化,而是針對性對一部分代碼使用 JIT 優化,對另外一部分代碼直接生成本地代碼。

緣由很簡單:

優化的越好,就意味着須要的分析和生成代碼的時間就越長。C++ 這種編譯型,等待的時間長一點能夠接受,可是對於 JavaScript來講,哪怕是 200ms,那對於用戶體驗來講,都是一個考驗。

我舉這個例子是爲了從軟件編程領域去說明一個道理:

就是不能一味的追求質量,而是要把質量和效率結合在一塊兒,去達到一種最優解。

我我的認爲,網上標準的 git-flow 模式 對於那些開源的項目可能比較適合,或者公司內部很重要的項目合適,其實 git 誕生背景,主要就是爲了讓開源的代碼版本控制變得更強大。github 的出現,讓 git 變得很是流行。咱們看一下網上那一套標準的 git-flow 模式,以下圖:

是否是看的眼花繚亂,都有點懼怕,我不就是作個版本控制嗎,有必要這麼複雜嗎?

首先,徹底沒有必要這麼複雜,各位小夥伴不要被這種博客嚇到了,嚇到都不敢用 git 。雖然上圖的 git-flow 模式能夠說是使用 git 進行版本控制的 best practice 。可是我認爲這並不適合大部分的業務項目。

敢問,在大公司內,或者小公司內,使用上述的標準版 git-flow 模式進行開發的前端項目佔比是多少? 我想佔比幾乎沒有,或者說能有 10% ,我以爲都是奇蹟。若是項目開發時間緊,迭代快,那幾乎不可能按照這種模式來,那怎麼來呢?

我以爲應該是:在分析項目的時間,和迭代速度後,作出一個既能夠控制代碼質量和版本管理,又可讓開發過程變的不那麼繁瑣,從而保證必定的開發效率。這纔是一個比較好的 git-flow

大白話就是:

怎麼舒服怎麼來,自行腦補。

因此當你想學習 git-flow 模式開發的時候,而後去網上搜了一下博客,發現 git-flow 模式有點抽象。這個時候請不要懼怕,我不認爲這種標準抽象的 git-flow 就是屬於你如今項目的 git-flow

你應該去學習這種標準 git-flow 模式的思想。

好比經過幾個關鍵性的 branch 來對版本的生命週期進行精細控制,經過 branch 來分割各個生命週期的代碼,保證版本的各個生命週期代碼的純潔性。

純潔性是什麼意識?

舉個例子:下個版本的代碼,你也開發一半了,那這些代碼就不能出如今如今版本的線上代碼中,純潔性就能夠這樣理解。

我想說的就是:

比起要學會如何使用 git-flow , 咱們更應該去體會一個很棒的版本控制系統 的解決方法,其背後的思想。當深刻理解了思想,那後面用其餘版本控制系統的軟件,也能遊刃有餘了。

在大型項目中 git-flow 怎麼實施

這裏以我目前參與的一個大型項目做爲例子,說一下如何在實踐中,總結出屬於本項目的 git-flow 流程。

這裏介紹一下項目的分支結構,沒有所謂的 feature 分支,有 develop 分支,但也是簡寫成 dev ( 怎麼方便怎麼來 )。

dev 分支有兩個做用:

一個是充當 feature 分支,一個是充當 develop 分支。當要發佈一個新的版本的時候,就從 dev 上切一個 dev-xx 系列的分支,用來發佈一個版本。嗯,就是這麼簡單直接。

項目開始的時候:

項目代碼是託管到內部的 gitlab 上的,項目一開始的時候,是沒有 CR 的。全部開發者均可以向 dev 分支上提交代碼。

爲何要這樣呢?

是爲了提升開發效率,由於項目處於一個急速開發的階段,若是太注重質量上的保證的話,就會增長人力成本,下降開發效率,最後和急速開發背道而馳,這也算是符合那句俗語:過早的優化就是地獄。

可是提升開發速度的同時,就意味着要承擔對應的風險。

好比,同事進行了錯誤的操做,致使代碼缺失。我說一下我這邊遇到的一個經典案例 ( 簡要說一下這一部大片 ) 就是:

你 ( 表明一個同事 ) 在 merge 的時候處理不當,而後成功的把其餘同事的不少代碼搞沒了,可是你並不知情,覺得本身操做是對的,而後提交代碼到 dev 分支 。而此時,commit 時間線又在持續的往前走,走了很久,你才發現,而後忽然 at 全體人員,而後咱們就懂了。而後當你發現的時候,你果斷的想本身去處理這個問題,可是你沒有考慮到全面,只想到用 SourceTree 將代碼回滾到 merge 錯誤的索引處,可是你又不當心點錯分支了,將 dev 分支代碼回滾到了上個版本。因而,遠端 dev 分支,從上個版本到如今這個版本的代碼都沒了,記錄也沒了...

上面這個例子基本上算是除了刪庫之外,在 git 操做過程當中出現的最大的問題了,爲何會這樣說,理由能夠歸納爲如下幾點:

  1. 把一個分支 merge 到另外一個分支時,處理不當。
  2. 沒有及時發現本身的錯誤操做,致使各個產品線的開發在錯誤的代碼上持續走了半天,因爲項目涉及到的人員過多,致使半天時間內就有不少次 commit,而後你懂的。
  3. 沒有考慮清楚,就使用 reset 這種可怕的命令,去操做其餘 codercommit
  4. reset 錯分支了, 致使一個大版本的代碼被幹掉了,遠端記錄都沒了。

我給出的理由是否是挺充分的,那麼這個事情怎麼解決的呢 ? 通過討論,有兩種方案:

第一種: 經過將這次分支回滾到 到 merge 錯誤以前的 commit 。 而後將在錯誤後面繼續提交的那些 commit 挨個加進去。這種方式有個問題,因爲遠端記錄都沒了,致使只能依靠有相對完整記錄的某個開發來作這件事,可是誰也不能保證這個記錄就是完整的。

第二種: 留給各個產品線本身去認領,本身解決本身的代碼丟失,哪裏丟失,補哪裏,採用責任制。

最後採用了哪一種方案呢?

經過討論,採用了第二種方案。

有人可能要問了,爲何不使用第一種方案? 理由以下:

第一:遠端記錄都沒了,這點很傷。

第二:相信某個開發的本地記錄是不可靠的,最後還得讓各個產品線去 CR 本身的代碼,看有沒有修復完整。

綜合一下,最後採起了第二種方案,直接讓各個產品線去認領,雖然麻煩了你們,可是可讓各個產品線去 CR 本身的代碼,更爲保險和可靠。

此次事故也充分證實了,在提升開發效率的同時,若是不去合理的限制權限,那麼在未來就可能會出現你不想看到的事故。

有人可能又會問,爲何沒有 CR 機制,好比爲何一開始沒有上 gerrit

對於這個問題,我我的的觀點是這樣的:

gerrit 就意味着操做複雜度的增長和人力成本的增長,好比對於一個 APP 級別的項目,須要騰出更多的人力去 CR。而通常項目剛開始的時候,人力都是緊張的,那麼這樣作無疑是增長了項目成本。若是你們能經過我的技術素養,保證不會出現代碼問題,那就能夠先不上 CR 機制。在沒有上的狀況下,項目迭代了不少版本,也沒有出現任何版本控制上的問題,從這點也能夠說明,有些優化不必定要從一開始就上,仍是要結合實際狀況去制定符合本身的一套 rule 。 可是隨着人數愈來愈多,出錯的機率大大增長,而後就出錯了(滑稽臉),出錯了怎麼辦,那就上 CR 機制吧。

CR 機制怎麼上,如何去 CR 一個 APP 級別 ( 參與開發達到幾十的規模 ) 的項目,能夠繼續往下看,下面有專門介紹 gerrit 的知識。

git 中級 之 git 理論知識 和 git 實戰技巧

上面大體是 git 的科普,還有對項目開發過程當中遇到的問題的一些思考。我把上面的部分稱爲 git 初級。

而下面我要說的就是 git 的中級知識

若是你可以靈活運用 git 知識去解決版本控制過程當中的各類問題,那就能夠說你是屬於中級水平了。

這裏我想說一點:

我是用的命令行形式去進行 git 操做的,固然也有不少人是用的 SourceTree VsCode WebStorm 這種軟件去操做 git。 不過每一個人應該都有主次之分,好比我,就主用命令行,VsCode 我也用。

我通常的使用規律就是:

除了我須要去閱讀文件,對比文件先後版本,或者查看多個歷史版本時,我會用 VsCode 外, 其餘操做都統一用命令行解決。

PS: 用命令行玩轉 git 的話,那基本的 linux 知識仍是要掌握的,若是有興趣能夠去學學 linux 。 推薦書籍:

《鳥哥的 Linux 私房菜: 基礎學習篇》

由於生命不止,學習不止。

git 中級之理論知識

不少人只是在記 git 的命令操做,並不清楚這樣作的底層緣由,從而致使了 知其然不知其因此然,最後就沒有辦法在一個大的知識層面上對 git 進行一個更爲抽象和深入的理解。

下面我會站在別人的肩膀上( 不重複勞動 ),根據我所學習的 git 知識來簡要分析一下 git 的一些中級理論知識。

這裏我用網上的一張圖來簡單歸納一下 gitAPI 級別的原理,圖片以下:


而後我再展現大佬 Vamei 的兩張 git 分析圖( 圖 加 文字分析 ):

第一張圖:


第二張圖:

上面三張圖分別是一張 API 級別的 圖 和 大佬 Vamei 的兩張 git 原理分析圖。

若是對上面的三張圖理解深入的話,是能從圖中就能感覺到 git 的設計思想和不同凡響的特色。若是能理解深入,那其實也能夠說你已經掌握了中級的理論知識了。

可是不理解不要緊,下面我會簡要分析一下 git 的中級理論知識。

git init 幹了什麼

要想知道 git init 幹了什麼,咱們就要去執行 git init , 而後去分析執行先後的具體變化。

咱們重新建目錄開始,而後初始化一個全新的 git 倉庫,具體執行的代碼以下:

// godkun
mkdir 0112-git-test
// 新建的目錄,用 ls -a 查看,是沒有任何東西的
cd 0112-git-test
git init
cd .git/
ls -a
ls -l
複製代碼

git init 執行完後,如圖所示

從上圖中,咱們能看到執行 git init 命令後,在當前目錄下新建了一個 .git 目錄,咱們再經過 ls -l 能夠看到 .git 目錄下的全部文件和目錄,同時包括這些文件和目錄的權限。

下面我不在命令行下使用其餘 linux 命令去分析具內容了體,我來使用 code . 打開 VsCode 來具體看一下 .git 目錄下的真相,VsCode 中的 .git 截圖以下:

咱們從圖中能夠分析出幾個信息

第一個: 在 0112-git-test 空目錄下進執行了 git init 後,生成的 .git 目錄下的 objectsrefs 目錄和他們的子目錄都是空目錄,很純潔。

第二個: .git 目錄下的 HEAD 文件中寫了一行代碼 ref: refs/heads/master , 咱們按照這個路徑去找,卻發如今 refs/heads 目錄下並無 master

上面的狀況是咱們在空目錄下執行了 git init 的結果,那若是在一個非空目錄下執行 git init 呢? 好比:

mkdir 0112-git-test-2
cd 0112-git-test-2
vi 1.txt
// 寫入文件而後保存退出
git init
複製代碼

咱們按照上面分析的步驟去分析非空目錄下進行 git init 的操做,會發現 .git 目錄下沒有任何變化。

咱們會發現

通過兩次分析,咱們能夠看到,在進行 git init 後,無論當前目錄有沒有文件, .git 目錄都是同樣的,同時 HEAD 默認是指向 master 分支,看下圖:

圖中能夠看到,執行完 git init 後,當前分支就指向 master 分支了,因此這時候咱們就能解決掉下面這個問題了:

爲何會 git init 後默認指向 master , 經過上面簡單的操做,咱們就能夠從中級層面去理解這個事情了。

如今咱們繼續推,對非空目錄下執行 gst , 顯示以下圖:

咱們看箭頭處,會發現這個文件是 untrack ,咱們結合 git init 命令先後的 .git 並無發生任何變化。

能夠推出:

1.txt 沒有被歸入到版本控制系統中,untrack 就表明沒有歸入到版本控制中。

**PS:**咱們在分析 .git 目錄的時候,必定要帶着版本控制的思想去分析。

思考時間

我分析了 git init ,那麼類推一下, git clone 幹了什麼呢? 這裏留給小夥伴們分析吧。

總體分析 .git 目錄

上面咱們經過 git init 後,生成了一個 .git 目錄,可能你對 .git 目錄還比較陌生,若是想掌握好 git 的中級理論知識,那麼 .git 目錄是要去征服的。

從上面 git init 後的目錄截圖咱們能夠知道(簡要介紹一下)

第一:.git 根目錄下,有不少一級子目錄和一級子文件。

第二:看 hooks 目錄,從命名咱們聯繫到 react 最新的 Hook 特性,萬物都是相通的。裏面有不少文件,好比 pre-commit.sample 文件,這是一個樣本文件,咱們按照樣本文件的寫法進行編寫代碼,而後把 .sample 去掉,寫成 pre-commit ,這樣就能夠在你執行 git commit -m 'xxx' 時,去執行 pre-commit 文件中的代碼。這就是 git 中的生命週期鉤子。

第三:看 objects 目錄,這是一個存放各類數據的目錄。咱們的項目,無論是什麼形式的數據,圖片也好,音頻也好,代碼也好,都會被轉換成統一的數據格式存放在 objects 目錄下。

關於 objects 目錄的基本信息,能夠看下面這篇介紹 git-objects 的博客:

Git-Internals-Git-Objects

第四:refs 目錄下有 headstags 目錄。以及子文件 HEAD 中寫着 ref: refs/heads/master , 這是 git 當前指向的分支。

有什麼感覺

我但願在總體分析時,你們能把 .git 目錄當成一個前端工程去分析,好比你能夠把 objects 目錄當成前端項目中的 dist 目錄。其餘類推,只要能有助於你去理解,那都是好的類推。

PS: 這裏是總體分析,沒有去深刻介紹,總體瞭解一下就好。

git add 後發生了什麼

當我把一個不在版本控制系統中的文件,使用 git add · 加到暫存區後,我來看一下 .git 目錄的變化,如圖所示:

咱們會發如今 Object 目錄下增長了一個名爲 60 的目錄。該目錄下有一個二進制文件。同時 .git 根目錄下多了一個 index 文件,也是一個二進制文件。

從這裏咱們能夠分析出幾個信息:

第一個:git add 操做會把不在版本控制下的文件歸入到版本控制中,爲何會這樣說,從中級角度看,是由於 .git 目錄有實質性的改變了。

第二個: git add 操做會在 objects 目錄下生成子目錄爲 60 ,文件名爲 d4a4434d9218d600c186495057bb9b10df98ad 的一個二進制文件。

第三個:git add 操做會在 .git 根目錄下生成一個命爲 index 的二進制文件。

咱們看一下 d4a4434d9218d600c186495057bb9b10df98ad 文件中的內容是什麼?

執行:

git cat-file -t 60d4a4434d9218d600c186495057bb9b10df98ad
複製代碼

執行結果以下圖所示:

就輸出了一個單詞,blob

blob 是什麼?

blobbinary large object 翻譯一下就是二進制大對象。那咱們能夠這樣理解,這個文件是一個二進制大對象,OK ,繼續往下分析。

文件爲何要用一串字符串命名

好比文件 d4a4434d9218d600c186495057bb9b10df98ad ,不理解不要緊,繼續往前端上去聯想,是否是想到了 webpack 打包後的文件名,能夠在前面加上 hash 前綴。有種豁然開朗的感受了吧,留給你們自行去分析吧。

git addblob 和 文件名 d4a4434d9218d600c186495057bb9b10df98ad 的關聯

沒有執行 git add 的時候,目錄下是空的。當 git add 後,多了一個 blob ,同時生成了一個 40 個字符的 hash 串,而後目錄和文件用 hash 表示。也就是說:

git add 後生成了一個 blob 對象,blobId60d4a4434d9218d600c186495057bb9b10df98ad

看到這你是否是又有點感受了,記住一句話:

萬物皆可推

咱們日常的各類 git commit -m 'xxxxxx' 其實生成一個 commit 對象,同時生成了 commitId 也是40位的 hash 字符,存在 objects 目錄下。

根目錄下多了一個 index 文件,它是什麼?

如今肯定的一點是,當用 git add 把文件放到暫存區的時候,index 文件就生成了,這個 index 文件是一個二進制文件,我使用下面命令去查看 index 的內容:

od -tx1 -tc -Ax index
複製代碼

如圖所示:

上面圖中的那一串數據是 index 文件中的二進制數據。

這裏咱們看一下圖中我標註的紅框。

能夠看到,index 文件中包含了不少信息,好比 1.txt2.txt ,還有 TREE 。目前從表現上看,我只能瞭解到這麼多的信息,它們之間確定有某種聯繫。其實瞭解過暫存區的應該能夠聯想到,index 文件就是一個暫存區。

能夠看這篇直接給結論的官方文檔:

Git-Internals-Plumbing-and-Porcelain

思考時刻

留幾個問題給各位小夥伴思考:

若是你的項目尚未一個 commit 的話, 在上面這張狀況下,咱們使用 git stash會發現有如下報錯:

爲何會報這個錯誤提示?

爲何 40 字符的 hash 串要拿出前兩位做爲目錄?

這個作法其內部的道理是什麼,這樣作是和算法有關係嗎,目的是爲了更好的性能嗎,前端可不能夠借鑑這種思想,仍是說前端已經有了這種思想,那這種思想是什麼?

爲何 git 要用二進制數據格式來存儲數據?

自行想想,也許會有一些有趣的收穫呢。

如何去理解 git stash

這裏我會經過實踐去告訴你們,git stash.git 目錄是如何表現的。

首先我進行一次 commit , 項目如今只有一個 commitId ,以下圖所示:

這個時候,我使用下面命令:

vim 2.txt
// 編輯 2.txt
git add .
複製代碼

git add 後,咱們看 .git 目錄,以下圖所示:

關注一下上面的箭頭所指的文件。

點擊 ORIG_HEAD 能夠看到是一個字符串 0991ddc42dbda1176858b89008b8dece5f91093b 對照着在 objects 目錄下找,發現確實有,咱們再用下面命令

git cat-file -p 0991ddc42dbda1176858b89008b8dece5f91093b
複製代碼

咱們看到了 treetree 也有一個 treeIdtreeId33b62884583995b8723d4d5ef75e44aa7d07fbf3

再結合 git log

再看下面這張圖:

對比兩張圖, 會發現 ORIG_HEAD 文件中的 hash 值 相等於 HEAD 中所指向的文件位置中的 hash 值。話不能說太透,後面的自行領悟吧。

執行 git stash 會發生什麼?

看下圖:

圖中的左邊是我把 2.txt 經過 git add 放到暫存區的 index 文件的內容。右邊是我使用 git stash 後的暫存區的 index 文件的內容。能夠看出,git stash 先後的 index 文件差異。

請看下面我演示的 gif 圖:

能夠看到,當我 git commit 的時候,refs/heads 目錄下的 matser 文件中存放的 commitId 變成了最新提交的 commitId ,而 ORIG_HEAD 沒有改變。由此能夠知道,HEAD 文件存放的路徑,其路徑下的文件的 hash 值是當前目錄下最近的一次 commit

能夠參考這篇博客:

Git暫存區原理

git merge 和 git rebase

mergerebase 的問題大概是 git 中最著名的問題了吧,在面試中也是考察的最多的知識點。好比,你知道 mergerebase 的區別嗎?這種相似的問題,不勝枚舉。

網上教程也一大堆,若是你想深入瞭解 git mergegit rebase 的話,那就請按照我上面的那種分析方法,一步一步去操做,而後觀察 .git 目錄下的各類變化,而後根據重要的變化來去細緻的分析其中的緣由和道理。

可是,不少教程寫的過於複雜了,我拿 rebase 來作一個我我的理解的通俗解釋。

好比當前分支爲 dev ,而後我執行:

git rebase master
複製代碼

上面的命令怎麼理解

一個最關鍵的一點就是: 要知道 rebase 是變基的意識。rebase master 是以 masterbase ,而後把 dev 分支的補丁打到 master 後面,打的過程當中生成的 commitId 是新的 commitIddev 原有的 commitId 被丟棄,時間線也就變成了直線。

最終,matser 和個人 dev 分支合併,讓最新的 commmitId 以個人最新提交的爲準( 這裏就是我在 dev 分支上的最新提交 )。因此當我 push 後,我提交的代碼就成爲了基準。

rebase 就這麼簡單。

能夠看看個人兩篇簡潔 issues

讓 git rebase 的概念終結於此吧

通俗易懂的 git merge 的策略解釋

git 中的 blob commit tag tree 是怎麼串起來的

其實這是一個很是關鍵的問題,不少人都不清除這些 單詞 背後的的真理到底是一種什麼樣子的美麗。

可是我不打算造輪子了,由於好文章太多了,這裏我還想放上面的一張圖,由於這張圖太經典了。

解釋已經在圖中的文字中了,好比知道了這些,你就知道了咱們在給版本打上 tag 的時候,到底是作了什麼。咱們不能浮於表面,只知道要打 tag ,咱們還要知道打 tag 背後的緣由。只有這樣,才能作到知其然知其因此然。

終結 tag : github.com/godkun/git-…

其餘零碎的知識點

COMMIT_EDITMSG 文件

此文件裏面寫的內容是本地最後一個提交的信息

packed-refs

clone倉庫時全部的引用

git 中級之實戰技巧

我把在使用 git 進行版本控制過程當中,我所用到的全部 git 操做高度提煉一下。

個人這些 git 操做的目的能夠歸納爲如下幾點:

  1. 第一個目的:處理合並,解決衝突
  2. 第二個目的:提交代碼
  3. 第三個目的:提升開發效率
  4. 第四個目的:合理的優化
  5. 第五個目的:當本身出現錯誤操做時,作到快速且正確的處理掉
  6. 第六個目的:幫助同事解決他們的一些 git 操做問題

下面簡要分析一下上面各個目的過程當中的一些心得。

處理合並,解決衝突

git 處理合並和解決衝突的能力 碾壓 svn 。好比 svn 處理一個衝突,因爲是集中式的倉庫管理,倉庫只有遠程一個,可想而知,解決衝突就是一場提交競賽。

我本人是如何在項目中處理各類衝突和合並的呢?

按照個人這幾個步驟來,基本不會存在任何衝突解決失敗的狀況。

首先,當我去 pull 遠端代碼的時候,好比執行

git pull origin dev
複製代碼

執行完以後,我發現的控制檯多了不少 conflict 提示,我看了下,不少都是別人代碼的衝突,這個時候我怎麼會呢?

我會絕不猶豫的 git reset --hard

回滾掉此次 merge ,而後我已經知道了這樣是不行的,可是我又不能去等着別人把衝突修改掉,怎麼呢?我會先在當前分支的基礎上新切一個分支

git checkout -b dev-backup
複製代碼

至關於備份一下目前本地的代碼,dev-backup 分支用來保存本地代碼。而後這時,我

git checkout dev
複製代碼

切換到 dev 上,切換後,我要怎麼辦呢,這時我會將 dev 分支的代碼所有替換成遠端的 dev 分支:

git reset --hard origin/dev
複製代碼

這時,我本地的 dev 分支已經所有采納遠端 dev 分支代碼了,這個時候我還須要將我本地修改的代碼合併進去,可是這個時候我就可使用一個命令:

git checkout dev-backup pages/xxxx
複製代碼

經過上面的命令,咱們就能夠將 dev-backup 分支上的 xxx 目錄下或者 xxx 文件的代碼單獨合併到 dev , 而這部分代碼就是我本地本身修改的代碼,因此就算有 conflict , 我也能夠迅速解決掉,而後安全 push 遠端倉庫上。

上面的解決衝突的方法,雖然方式簡單,可是是我我的認爲能夠完美解決掉 git 版本控制中的全部合併和衝突問題。

在版本控制系統中,合併一直都是一個核心節點,咱們要去理解合併和解決衝突在版本控制系統中究竟佔有多大的重要性。

提交代碼

提交代碼這個應該沒什麼問題,但其實你把本地代碼提交到遠端倉庫這一步驟,是一個很是重要的時刻,爲何我說很是重要呢?想必你以前聽過外國一個程序員由於同事常常 git push -f 而把同事給終結掉了,😂。因此懼怕了吧,莫事,不慌,你只要遵照這幾個原則就 ojbk 了:

  1. 千萬不要用 git push -f 除非你已經作好不想活的準備了。😂
  2. 不要把衝突提交了,提交前檢查一下有沒有衝突。
  3. 寫好你的 commit message
  4. git commit 以前先 git status 看一下,檢查一下有沒有無心間改動了其餘文件。

其實我我的的感受就是,若是是本身的業務項目,除了第一點,第二點,第四點須要去注意外,像 第四點,commit message 這種,開心就好吧,不用很刻意的。

提升開發效率

談到這個,我想你們都有一些本身的總結吧,在用多了 git 後, 慢慢的會發現有一些能夠加快使用 git 進行版本控制的小技巧。下面我總結一下我本身總結的幾點提高開發效率的方法吧。

配好最適合你本身的 alias

好比我配的有:gst 表明 git status , 固然你還能夠更加簡單,開心就好。

優化你的 stash

用好 stash 也是一個既簡單又能夠提升開發效率的方法,具體用法不說了,個人 github 有相關詳細資料,它主要是起一個暫存的目的,可是通常你們都是 git stash

合理的優化

談到優化,其實我想說優化是一個相對的概念,若是對 git 控制版本的過程進行優化的話,我我的以爲我目前用到的優化也很少,大概就是如下幾個:

  1. 我會偶爾用 git rebase -i 對個人一些我都看不下去的 commit 進行處理。

當本身出現錯誤操做時,作到快速且正確的處理掉

這個固然是本身蠢了,不當心把東西搞砸了,那就要快速解決掉本身的錯誤操做,怎麼解決,思想也很簡單:

通常我是本着一個原則:以最快的速度把錯誤操做從遠端倉庫中移除掉,從而最大化的減小對其餘 coder 的影響。

好比經過本地切分支快速備份我本身的代碼,而後切換回去,快速把本身的錯誤代碼回滾掉,而後 push 到遠端倉庫,解決遠端倉庫的代碼衝突問題,而後我再繼續解決本地我本身代碼的問題。

幫助同事解決他們的一些 git 操做問題

我感受若是一個項目很大的話,參與者不少的話,隨時有新的 coder 參與進來,你是沒法保證全部人的 git 操做都會很正確的,而這個過程當中,一些人可能有進行了錯誤的 git 操做,本身也沒法解決,而後會找其餘同事尋求幫助,我也幫助過一些同事。我在幫助其餘同事處理 git 問題的時候,使用的命令仍是比較多的,有時候還得使用一些不經常使用的技巧,好比正則,過濾等,這裏就不細說了。

實戰過程當中本身的一些感悟

我以爲,咱們沒有必要在項目開發過程當中把 git 操做複雜化,一些黑科技什麼的,也沒有必要去關注,有句話是這樣說的:能用簡單的操做解決複雜的問題纔是大牛。因此上面我介紹的實戰技巧,能夠說沒有什麼高大深的技巧,當理解的足夠深刻的時候,經過簡單的操做也能夠保證項目的有序進行。

git 高級 -- 你可能不知道的 git 知識

這裏呼應一下文章開始所說的那一句話:

從初級到中級再到我都 hold 不住的高級。

爲何我說我都 hold 不住呢?是由於我真的 hold 不住。可是我仍是去學習了一番,從新簡單翻了一遍 CC++ 語言,嘗試着去理解一下。

簡單看一下 github 上的 git 源碼

首先把 github 上的 git 倉庫 clone 下來。

先看一下 git 項目 代碼量

這裏我用到一個代碼行數分析工具 cloc ,能夠經過下面進行安裝:

<!--安裝 homebrew 後-->

brew install cloc
複製代碼

安裝完畢後,在 .git 目錄執行:

cloc *.c *.h *.sh
複製代碼

獲得如圖所示:

從圖中咱們能夠發現,當前 github 上的 git 項目是由不少語言組成的,master 分支的總代碼行數大約 50 萬左右( PO File 不算)。主要語言有 C sh(Bourne Shell) Perl C/C++ Header 。給個人感受有幾點:

第一點:代碼量不算大,50萬行左右,與 linux 內核這種千萬級別的代碼仍是有差距的,只能算是一個工具。

第二點:涉及到的語言不少,可是核心語言基本就 C sh C/C++ Header 這三種。

下面我要怎麼分析它呢。

先降維分析

目前因爲目錄過於複雜,我想到了去看 git 項目第一次 commit 的內容,通常來講,第一次 commit 的代碼量是比較小的。我在 github 上找到的截圖以下:

我進入git 目錄 執行了

git checkout e83c5163316f89bfbde7d9ab23ca2e25604af290
複製代碼

去看一下第一次 commit 的代碼內容,以下圖所示:

命令行:

VScode 截圖:

我好奇的使用 cloc 看了下代碼量,下圖所示:

驚了!只有848行,是否是瞬間有了信心。那就開始終結它吧!

按照慣例,我去 README 中看了下項目介紹:

如圖所示:

編輯者是 Linux Torvalds

GIT - the stupid content tracker

"git" can mean anything, depending on your mood.

這兩句是做者本人對 GIT 的介紹,是本尊無疑了。

我閱讀完 README 後,得到了如下幾點信息:

All objects are named by their content, which is approximated by the SHA1 hash of the object itself. Objects may refer to other objects (by referencing their SHA1 hash), and so you can build up a hierarchy of objects.

第一點:全部對象都是用他們本身的內容來命名,經過 SHA1 hash 值來標識本身。對象能夠經過引用其餘對象的 SHA1 hash 來引用其餘對象。因此能夠創建起一個有層次的對象模型。

第二點:對象內容都是用 zlib 進行壓縮,同時 SHA1 哈希始終是 是壓縮後的對象內容的哈希值,而不是原始對象內容的哈希值。

第三點:A "blob" object is nothing but a binary blob of data, and doesn't refer to anything else. 簡單點說就是: blob 沒有任何其餘屬性,僅僅表示文件的內容。

The "current directory cache" is a simple binary file, which contains an efficient representation of a virtual directory content at some random time. It does so by a simple array that associates a set of names, dates, permissions and content (aka "blob") objects together. The cache is always kept ordered by name, and names are unique at any point in time, but the cache has no long-term meaning, and can be partially updated at any time.

第四點:當前目錄緩存,能夠理解爲是暫存區,暫存區也是一個二進制文件,它經過一個簡單的數組來記錄着時間,權限,和對象內容。

第五點:使用了 SHA1 ,因此改變和內容是值得信任的。

README 的信息仍是很足的。說明了不少事情。

第一次 commit 的源碼分析

這裏我就不造輪子了,找到了一篇文章,基本把第一次 commit 的源碼各個文件的做用解釋的較透徹。

Git源碼學習

簡單分析一下最新的 git 源碼

執行 git checkou master 切到 master 分支

從圖中咱們能夠看到,有不少不少東西,一點都不想分析,那就不分析了,都1萬多字了,寫不動了。就這麼愉快的贊成啦!開開心心過完年後,再單獨寫一篇( 嘿嘿嘿 )。

gerrit 原理知識

這個原理知識就不說了,簡單點說就是搭一個 gerrit 服務器,而後經過 UI 界面去進行代碼的 CRCR 經過,點擊 submit 就會把代碼同步到 gitlab 上。

gerrit 實戰總結

本人負責給項目實施 gerrit , 並解決同事在過渡到 gerrit 方式的過程當中出現的各類問題。我在解決各類問題的時候,對整個 gerrit 的流程和操做都理解了狠多,下面就分享一下我在幫助同事過渡 gerrit 的過程當中遇到的問題和總結的一些心得吧。

gerrit 基本設置

這個就不說了,基本的像 ssh 認證 、 remote 設置、郵箱設置、這種我就不造輪子了,按照網上的基本教程來。

提交 gerrit 時提示缺失 Change-Id

問題描述

這個錯誤,是在過渡到 gerrit 的過程當中出現最多的錯誤,沒有之一,幾乎都會遇到。

錯誤以下圖:

從圖中能夠看到,提示 [8a5fca6] missing Change-Id in commit message footer

什麼意識呢,就是說 commitId8a5fca6 的提交沒有 Change-Id ,因此就提交失敗了。

同時咱們能夠看到打印信息裏面有給解決這個問題的方法,先執行:

gitdir=$(git rev-parse --git-dir); scp -p -P 29418 name@git.co.com:hooks/commit-msg ${gitdir}/hooks/

再執行:

git commit --amend

可是在解決這個問題的過程當中,我發現上面的提示,有時候並不能成功。我總結出了幾種狀況,下面一一列出。

缺乏 Change-Id 的 commitId 是 head 指向的 commitId

若是是 head 的話,也就是 git log 的第一個 commitId 。 那能夠直接按照上面提示的命令去執行。

這裏提一下,在執行 git commit --amend 時,會進入 vi 界面,進入後能夠不用修改任何東西的,直接保存退出便可,就能夠從新刷新 head 指向 的 commitId 的 值了。

缺乏 Change-Id 的 commitId 不是 head 指向的 commitId

若是不是 head 的話,好比是第 6 個 commitId 缺乏 Change-Id ,那怎麼辦呢? 針對這種狀況,有兩種辦法:

第一種:git reset --soft

使用 git reset --soft commitIdcommit 記錄 軟回滾 到缺乏 Change-Id 的這個 commitId 處,好比上圖的 commitId[8a5fca6] 是第6個 commitId,那此時就能夠這樣操做:

git reset --soft 8a5fca6
git commit --amend
複製代碼

而後就能夠 push 成功了。可是美中不足的地方就是,軟回滾了其餘的 commit 。 可是問題不大,若是都是你本身的 commit,那就直接 soft 吧,不是的話,能夠採用下面第二種方法。

第二種:git rebase -i commitId

git rebase -i commitId 這個命令能夠幫助你去編輯 commit,好比把幾個 commit 合併成一個 commit 。而這裏咱們想要作的是,經過這個命令來完成只修改上圖中的指定 commit , 同時不會對其餘已存在的 commit 形成任何影響。具體操做是:

注意:git rebase -i commitId 中的 commitId 並非提示的 commitId 。 而是提示中 commitId 的前一個 commitId 。好比執行 git log

// .....
commit 7b7b7b7
commit avacaba
commit 8a5fca6
commit godkun666
複製代碼

那這個 commitId 就是 godkun666

git rebase -i godkun666
複製代碼

而後進入 VI 界面,以下面:

pick 8a5fca6 i am godkun1
pick avacaba I am godkun2
pick 7b7b7b7 I am godkun3
複製代碼

直接把缺乏 Change-IdcommitId[8a5fca6] 前面的 pick 修改成 reward,而後保存退出就行了。這種方法也試用一次性修改多個缺乏 Change-IdcommitId 。保存退出後,就能夠直接 push 了。 對於 rebase -i 的相關知識,請自行谷歌百度,這裏不作講解。

上述二者方式都試了,仍是不行

這種狀況出如今一個同事身上了,兩個狀況的解決方法都試了,仍是不行,而後我仔細看了下,在執行:

gitdir=$(git rev-parse --git-dir); scp -p -P 29418 name@git.co.com:hooks/commit-msg ${gitdir}/hooks/

出現了一個報錯,因爲我沒有保存截圖,大體意識就是 hook is not directory 可能我這樣說出來,感受很簡單啊,可是在過程當中,這個提示是很不明顯的,後面我進入 .git 目錄看了下才知道怎麼回事, hooks 是一個文件了,不是目錄,這也是夠秀的,我初步猜想是在複製這個命令的時候,複製的不全,致使生成了 hooks 文件 。而後我刪除 hooks 後,又新建了一個 hooks 目錄,從新執行了上述命令就行了。

commiter email address xxxx does not match your user account

出現這種問題,是由於圖中提示的 commitId 其所綁定的郵箱不正確。須要你先設置正確的郵箱,在設置完正確的郵箱後,咱們繼續其餘操做,我總結的有三種方法能夠解決這個問題:

第一種方法:把這個有問題的 commit 撤銷掉,可使用軟回滾 git reset --soft commitId 回滾掉。

第二種方法:若是這個 commitId 就是 head 的指向,那直接 git commit --amend 刷新這個 commitId

第三種方法:若是這個 commitId 就是 head 的指向, 那經過 rebase -ireword 這個 commitId

原則:若是按照怎麼舒服怎麼來,那我就用 git -reset --soft,若是嚴謹點,那我就按條件劃分使用下面兩種方法了。

gerrit cannot merge and Submit including parents

不造輪子了,基本操做問題都在下面這篇博客中有提到:

如何解決gerrit代碼衝突

可是,沒有本身的見解的話,那和鹹魚有什麼區別呢?

我來分析一下 including parentnot merge

如何所示:

表面緣由:

coder 本地開發後,產生了 commit 而後 pushgerrit 上後,CR 者會根據狀況進行拒絕,若是拒絕了,可是 coder 本地的 commit 並無撤銷,那麼就會致使後續提交的系列 commit 出現上圖這種狀況,由於如今的 commit 依賴前面的幾回 commit 。可是前面提交的 commit 並無贊成。因此就致使了不少 CR 問題。

核心緣由:

coderCR 者的 commit 時間線不一致。

如何解決:

核心是把 commit 時間線作到一致

若是尚未出現上述的問題,如何作預防:

第一種:

coder 成功把本地的 commit pushgerrit 上後,記得要 reset 掉,若是不放心,那能夠軟回滾,而後 stash ,等 CR ,若是拉下來發現沒問題,就能夠把 stash 放棄掉。

第二種:

push 後,切新分支進行備份,而後切回去,再把本地的 commit reset 掉。這樣就不會存在上面圖中的各個不能合併的問題的。當 CR 後,你 pull ,發現代碼都對的時候,就能夠把備份分支刪掉了。

若是已經出現上述問題了,怎麼辦?

核心思路:如今 coder 須要把本地的那些已經被 gerrit abandon 掉的那些 commit 幹掉。

第一種:

直接 從新 git clone

第二種:

切一個分支進行備份,而後切回去,使用:

git reset --hard origin/dev
複製代碼

放棄本地全部代碼,所有采用遠端代碼。。而後使用 cherry pick 把備份分支的 你須要的 commit 合到 dev 上。

PS: 固然這些只能是本地 coder 去解決這個問題。

第三種:

使用 rebase 去挨個修改或者使用 git reset --soft 把前面的不少 commit 都回滾掉。 不建議使用第三種方法,操做要求高,容易出錯。

how to make SourceTree push to Gerrit

git 倉庫代碼根目錄下執行:

git config remote.origin.push refs/for/dev
複製代碼

how to make TortoiseGit push to Gerrit

小烏龜 push gerrit 時會出現這種錯誤,以下圖所示:

怎麼解決呢?請看下面截圖:

用小烏龜推送 gerrit 的時候應該要在 remote 前邊手動加上 refs/for/

參考博客: TortoiseGit推送代碼到Gerrit的過程

如何快速高效的 CR ( coder review )

當各個產品線提交的代碼都要你來 CR 的時候,你會發現根本無法去 CR ,由於你自己就不熟悉他們的代碼,怎麼 CR 呢,最後我決定這樣作:

各個產品線的 coder 須要 CR 的話 羣裏 at 我一下,我在 CR 的過程當中,有三個原則:

第一個原則:我默認相信各個產品線對本身負責的代碼作出修改,也就是相信 coder 修改本身負責的代碼,責任制。

第二個原則:我會嚴格關注各個 coder 有沒有改動其餘 coder 代碼,若是改動,我會去私聊詢問,爲何要這樣作。

第三個原則:我會嚴格關注各個 coder 有沒有改動公共部分的代碼,好比登陸模塊,若是改動,我會去私聊詢問,爲何要這樣作。

只要不符合上訴三個原則,一概 abandon

git FAQ 傳送

發個關於 git FAQ 的連接:git.wiki.kernel.org/index.php/G…

參考連接

  1. github.com/git/git
  2. fabiensanglard.net/git_code_re…
  3. nvie.com/posts/a-suc…
  4. git-scm.com/book/en/v2/…
  5. schacon.github.io/git/user-ma…
  6. blogs.msdn.microsoft.com/devops/2018…
  7. aosabook.org/en/git.html 這是一個很不錯的介紹git的網站
  8. learngitbranching.js.org/ 這是一個在線實驗 git 的網站
  9. mirrors.edge.kernel.org/pub/softwar…
  10. mirrors.edge.kernel.org/pub/softwar…
  11. mirrors.edge.kernel.org/pub/softwar…
  12. mirrors.edge.kernel.org/pub/softwar…

上面幾篇關於 git 的文章都是我認爲很不錯的文章,能夠閱讀閱讀,會有驚喜的。

備註

  1. 有一些知識是點到爲止,就這都寫了12000多字了,理解一下 ( 笑哭 )。
  2. 文章內容確定有錯誤,歡迎小夥伴討論指出哈。
  3. 文章有點長,閱讀體驗可能不佳,可是又不忍心分開寫,先就這樣吧 ( 心 塞 )。

交流 + 福利

我把我日常在工做和學習中總結的 git 知識整理了一下,把最經常使用的,以 issues 的形式放在了個人 gayhub 上,有須要的小夥伴能夠點擊下面連接自取:

github.com/godkun/git-…

掘金系列技術文章彙總以下,以爲不錯的話,點個 star 鼓勵一下,一個 star 開心一年(手動滑稽) ,也能夠 gayhub 關注我一波,持續輸出精品文章。

github.com/godkun/blog

我是源碼終結者,歡迎技術交流。

相關文章
相關標籤/搜索