打造專屬於你的 Git 工作流程 — Alias、Commands、Hooks

(Ref: http://blog.openhatch.org/tag/newsletter/)

Larry Wall(Perl 之父)曾說過,程式設計師有三大美德: 「懶惰」、「不耐煩」、以及「傲慢」。所以如果你想要成為偉大的程式設計師,你一定要懂得「偷懶」!在每天的軟體開發中,版本控制系統(Version control systems )是必備的工具(如:Git、Mercurial),想想,若是我們可以省下跟那些工具打交道的時間,就可以擁有更多時間來寫 code,就更有可能改變世界。因此,本文將以 Git 為例,教你如何讓 Git 操作起來更簡潔,讓你可以更懶。

 

觀念

通常改善工作流程,可以有幾個技巧:減少重複、加速、自動化

減少重複

首先,你必須檢視自己使用 Git 的流程,試著執行

看看你都下了哪些 Git 指令,其中是否有重複的工作,是不是經常執行一系列的指令來達到某個目的,如果是的話,那你就可以考慮創建一個巨集指令,一次完成所有工作,就像寫程式一樣,發現有重複的 code,你可以使用 Extract Method 的重構技巧,將他們抽出成一個單獨的方法,下次就可以直接呼叫。

加速

同樣做一件事,但想辦法讓他更快,用最少的敲擊鍵盤數來完成同樣的指令,例如某些指令你常常打,那麼就可以創建一些 alias 讓指令變短,就像 huffman coding 一樣,使用頻率越高的指令應該要越短。

自動化

有些事不一定要手動去做,Git 可以設定 hook,當某些事件發生時,自動去做一些事,除了省事也更可靠,你不用擔心自己忘記執行。

 

具體的作法

接下來,我會介紹一些具體方法及實例來說明。

0. RTFM (Read The F***ing Manual)

每一個 Git command 都有一堆參數可以設定,讀一下 help 看看這個 command 本身可以作到哪些事情,搞不好會發現以往的作法其實是繞路,有更直接的作法只是你不知道而已。同樣的,做任何事情都一樣,看看有沒有現成的東西可以使用,重複打造輪子並不是笨蛋,至少你會學會如何打造輪子,但這浪費了時間,那些原本可以拿去做更偉大事情的時間。

如果你會用到 github 的話,可以裝一下 hub,他延伸了原本的 Git command,讓 github 的操作更簡單。

1. Alias(加速)

這招最簡單,如果你常常打一些指令,考慮弄個 alias 吧。

這相當於直接在 .gitconfig 裡加入:

設定之後,你就可以用 git co 來取代 git checkout,有沒有,一下就省了很多個鍵。再來你可以把參數一起放進去,像是:

我個人覺得 git log 很佔空間,就會用 git lo 讓他顯示一行版本的 log。

再來更進階的,你可以把 command alias 成某個 shell function,如此一來除了更 powerful,還可以利用 positional parameters,例如以下,我就可以用 git files <SHA> 得到某個 commit 中修改了哪些檔案。

如果你是 alias 的愛好者,而你使用的 shell 也支援 alias (ex:bash),我看過更懶的人在他們的 shell 中設了像這樣的東西:

以及打錯字的修正…

 2. Custom Commands(減少重複)

在 Git 中你可以創造自己的 command,之後就可以透過以下方式執行:

新增一個自訂 command 十分地簡單,只需要:

  1. 建立一個新檔案,並取名為 git-my-command,也就是你想要的指令名稱加上 git- 字首,注意沒有副檔名。
  2. 你可以用任何語言寫,Bash、Python、Ruby、…都可以,然後把檔案設為可執行。
  3. 確認檔案的位置有在 $PATH 中。

如此,你幾乎可以寫一個 Git command,做任何你想要的事情。接下來,我們來做一個很小的東西。

需求

當我們開發新功能時,根據 Git 的特性,大部分的人會開一個新 branch 出來,然後開始一系列的編輯與 commit,假設這時我們想整個瀏覽一下在 local 端我們增加了哪些 commit,可以下 git log,但是 git log 的結果會是一長串的 commit,很多是本來就在 upstream 上的,是不是可以跳過那些,只顯示我們在這 branch 上新增的 commit 呢?

PS:首先你的 branch 必須要設好 upstream,可以用 git branch -u <upstream> 設定,不過這不是重點,細節部份請自己查一下。

解法

我們打算建立一個 custom command:git mylog [<branch>] 來達到這個目的,大致作法如下:

  1. 取出 <branch> 的 upstream。
  2. 計算 <branch> 與 upstream 的 common ancestor。
  3. 列出 <branch> 上在 common ancestor 之後到 HEAD 的所有 commit。

動工,以 bash 實作如下:

以上只是一個簡單的例子,可以參考看看大家都做了哪些 custom commands:

3. Hooks(自動化)

Git 和其他版本控制系統一樣,可以在某些重要事件發生時,自動觸發自訂腳本。他分為用戶端和伺服器端,這邊我們會說明如何使用用戶端 hooks 來自動化工作流程。

在每個 Git repository 中都有一個 .git/hooks 目錄,裡面的內容大概像這樣:

這是 Git 提供給我們的 sample script,讓我們參考用的,script 的檔名就是對應的事件,這邊有一份完整可以使用的事件 list

如果我們想要安裝一個 hook,在每次 commit 前讓他自動執行某個腳本,就在這個目錄中,新增一個名為 pre-commit 的 script file(注意沒有副檔名,檔案要設為可執行),這樣在 commit 前就會自動執行這個腳本。

pre-commit 大概是最多人使用的 hook,他可以讓我們在 commit 前對我們的修正做些檢查,例如用 linting tool (ex:jshint)掃過,以便確保程式的品質。可以參考看看大家都怎麼使用 hooks:

再來做一個小玩具吧。

需求

在 gecko 中,每個 interface(寫在 .idl 中)都會有一個編號 uuid,這可以當作該 interface 的版本號或是 signature,當 uuid 一樣時,我們就認為 interface 是沒有改變的,這提供一個機制確保 interface 與其 user 是 binary compatible 的,因此當我們修改 interface 的內容時,修改對應的 uuid 就很重要,但是有時候我們會忘記,所以是不是能有一個自動的檢查,在每次 commit 時都幫我們看看,如果 interface 有改,uuid 是不是也有跟著改。

解法

考量到 idl 的 parsing 是很複雜的,所以在這個例子中,我們在只做粗略的檢查:如果 idl file 有修改,那麼這個檔案裡的 uuid 必須也有修改。實際上每個 .idl 中可能有多個 interface,我們要確定有改動的 interface 其對應的 uuid 是有改的,甚至其他受影響的也要。不過我們先弄個簡單版,幫助我們抓出粗心忘記改的錯誤,步驟如下:

  1. 檢查此次 commit 的所有檔名,看其中是否有 .idl
  2. 若有,取得此檔案的 HEAD version (此次 commit 前版本)與 staged version(此次 commit 後版本)。
  3. 取出此兩版中的所有 uuid 形成兩個 sets。
  4. 比較這兩個 set 是否一致,若一致,就表示 uuid 沒改,這是錯誤的,return non-zero 中斷 commit。

這次用 python 來寫,大致如下:

 

結論

程式設計師最酷的地方就是,他可以透過寫程式,來提昇自己下次寫程式的速度,很多我們平常習以為常的地方,都有著改善加速的機會。在本文中,我們透過 Git alias、以及撰寫 Git custom commands、Git hooks,讓軟體開發的流程更順暢、更有生產力。

您可能也會喜歡

目前找不到相關文章

共 1 則讀者回應

對此文章發表回應

你的電子郵件位址並不會被公開。 必要欄位標記為 *