初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

原始碼終結者發表於2019-01-28

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

本文是站在別人的 commit 上去 mergepatch 我自己獨具特色的理解,從而生成一個更好的 commit ,然後留給大家日後更好的 mergepatch ,技術在一次次 patch 中不斷進步。

開門見山

在實際專案開發中,能靈活的使用 gitgerrit 是一個很重要的事情,一方面可以提高團隊開發效率,另一方面可以把控專案的程式碼質量。不過對於 gerrit , 可能一些同學沒有用過,還有 git 的一些你可能沒有掌握的技巧,今天就一起分享給大家,分享的同時,自己也有很多即時收穫。

PS: 為什麼我會說我自己也會有很多收穫呢,因為是這樣的:當我選擇寫一篇部落格的時候,我會自己先去深入的理解我寫的這篇部落格的相關知識點,在深入理解這個過程中,我會去閱讀各種資料,然後去分析,最後總結出屬於我自己特色的學習心得,這對我來說,就是一種即時收穫和高階進階。

為什麼會出現 git

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

git 是一個開源的 分散式版本控制系統 ,原始碼也在 github 上開源了,可以自行搜尋。提到分散式版本控制系統,那應該聯想到 集中式版本控制系統 ,具有代表性的比如 SVNSVN 的全稱是 Subversion

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

SVN:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】


GIT:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

從圖中基本可以分析出兩者的主要區別。比如:

  • git 可以離線開發,svn 不能離線
  • git 處理 merge 的優勢碾壓 svn

當然其他的區別還有很多,比如 git 的內容更完整,使用了 SHA-1 演算法,git 可以更加靈活的操作 branch 。 等等,這裡就不造輪子了,參考下面這篇部落格:

SVN與Git比較的優缺點差異

看到這裡,我們可能比較偏向於, SVNGit 差一些的觀點,但其實這是兩種不同的版本控制系統,不能簡單的認為誰好誰壞,這裡有一篇為 SVN 洗白的部落格,挺有趣的,大家可以看看:

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

要是高度總結一下,那就是: SVN 更適用於專案管理, Git 更適用於程式碼管理。

為什麼會出現 gerrit

我們看維基介紹:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

言簡意賅:從維基上可知,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 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

首先,完全沒有必要這麼複雜,各位小夥伴不要被這種部落格嚇到了,嚇到都不敢用 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 級別的原理,圖片如下:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】


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

第一張圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】


第二張圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

上面三張圖分別是一張 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 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】
從上圖中,我們能看到執行 git init 命令後,在當前目錄下新建了一個 .git 目錄,我們再通過 ls -l 可以看到 .git 目錄下的所有檔案和目錄,同時包括這些檔案和目錄的許可權。

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

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

我們從圖中可以分析出幾個資訊

第一個: 在 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 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

為什麼會 git init 後預設指向 master , 通過上面簡單的操作,我們就可以從中級層面去理解這個事情了。

現在我們繼續推,對非空目錄下執行 gst , 顯示如下圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

我們看箭頭處,會發現這個檔案是 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 目錄的變化,如圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

我們會發現在 Object 目錄下增加了一個名為 60 的目錄。該目錄下有一個二進位制檔案。同時 .git 根目錄下多了一個 index 檔案,也是一個二進位制檔案。

從這裡我們可以分析出幾個資訊:

第一個:git add 操作會把不在版本控制下的檔案納入到版本控制中,為什麼會這樣說,從中級角度看,是因為 .git 目錄有實質性的改變了。

第二個: git add 操作會在 objects 目錄下生成子目錄為 60 ,檔名為 d4a4434d9218d600c186495057bb9b10df98ad 的一個二進位制檔案。

第三個:git add 操作會在 .git 根目錄下生成一個命為 index 的二進位制檔案。

我們看一下 d4a4434d9218d600c186495057bb9b10df98ad 檔案中的內容是什麼?

執行:

git cat-file -t 60d4a4434d9218d600c186495057bb9b10df98ad
複製程式碼

執行結果如下圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

就輸出了一個單詞,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
複製程式碼

如圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

上面圖中的那一串資料是 index 檔案中的二進位制資料。

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

可以看到,index 檔案中包含了很多資訊,比如 1.txt2.txt ,還有 TREE 。目前從表現上看,我只能瞭解到這麼多的資訊,它們之間肯定有某種聯絡。其實瞭解過暫存區的應該可以聯想到,index 檔案就是一個暫存區。

可以看這篇直接給結論的官方文件:

Git-Internals-Plumbing-and-Porcelain

思考時刻

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

如果你的專案還沒有一個 commit 的話, 在上面這張情況下,我們使用 git stash會發現有以下報錯:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

為什麼會報這個錯誤提示?

為什麼 40 字元的 hash 串要拿出前兩位作為目錄?

這個做法其內部的道理是什麼,這樣做是和演算法有關係嗎,目的是為了更好的效能嗎,前端可不可以借鑑這種思想,還是說前端已經有了這種思想,那這種思想是什麼?

為什麼 git 要用二進位制資料格式來儲存資料?

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

如何去理解 git stash

這裡我會通過實踐去告訴大家,git stash.git 目錄是如何表現的。

首先我進行一次 commit , 專案現在只有一個 commitId ,如下圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

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

git add 後,我們看 .git 目錄,如下圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

關注一下上面的箭頭所指的檔案。

點選 ORIG_HEAD 可以看到是一個字串 0991ddc42dbda1176858b89008b8dece5f91093b 對照著在 objects 目錄下找,發現確實有,我們再用下面命令

git cat-file -p 0991ddc42dbda1176858b89008b8dece5f91093b
複製程式碼

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

我們看到了 treetree 也有一個 treeIdtreeId33b62884583995b8723d4d5ef75e44aa7d07fbf3

再結合 git log

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

再看下面這張圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

執行 git stash 會發生什麼?

看下圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

圖中的左邊是我把 2.txt 通過 git add 放到暫存區的 index 檔案的內容。右邊是我使用 git stash 後的暫存區的 index 檔案的內容。可以看出,git stash 前後的 index 檔案差別。

請看下面我演示的 gif 圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

可以看到,當我 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 是怎麼串起來的

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

但是我不打算造輪子了,因為好文章太多了,這裡我還想放上面的一張圖,因為這張圖太經典了。

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

解釋已經在圖中的文字中了,比如知道了這些,你就知道了我們在給版本打上 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
複製程式碼

得到如圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

從圖中我們可以發現,當前 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 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

我進入git 目錄 執行了

git checkout e83c5163316f89bfbde7d9ab23ca2e25604af290
複製程式碼

去看一下第一次 commit 的程式碼內容,如下圖所示:

命令列:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

VScode 截圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

按照慣例,我去 README 中看了下專案介紹:

如圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

編輯者是 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 分支

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

從圖中我們可以看到,有很多很多東西,一點都不想分析,那就不分析了,都1萬多字了,寫不動了。就這麼愉快的同意啦!開開心心過完年後,再單獨寫一篇( 嘿嘿嘿 )。

gerrit 原理知識

這個原理知識就不說了,簡單點說就是搭一個 gerrit 伺服器,然後通過 UI 介面去進行程式碼的 CRCR 通過,點選 submit 就會把程式碼同步到 gitlab 上。

gerrit 實戰總結

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

gerrit 基本設定

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

提交 gerrit 時提示缺失 Change-Id

問題描述

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

錯誤如下圖:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

從圖中可以看到,提示 [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

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】
出現這種問題,是因為圖中提示的 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

如何所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

表面原因:

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 時會出現這種錯誤,如下圖所示:

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

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

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

用小烏龜推送 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

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

初中高階的 git 和 gerrit 技巧【大型專案實戰總結 && CR 經驗】

相關文章