工作中用到的 git 指令

來分享一下最近工作上常用的 git 指令,以及相關工作流程。

首先介紹一下工作的場景,在怎樣的情境下,怎樣的需求,讓我產生了這樣的 git 工作方法。我們主要的 VCS 其實是 mercurial (hg),但是有一個 git 的 mirror,所以可以在 git 的環境下改 code,但是最後必須到 hg 那邊把 patch push 上去,因此比較特別的是,我不會用到 git push,除非要 push 到自己維護的 repository,然後我必須要生成 hg 可讀的 patch 格式。

對應工具


editor: vim
difftool, mergetool: p4merge

alias


可以參考我的 .gitconfig,因為 programmer 的懶人特性,alias 會隨著時間越加越多
[alias]
  lo = log --oneline
  br = branch
  cbr = rev-parse --abbrev-ref HEAD
  df = diff
  dt = difftool
  mt = mergetool
  st = status
  co = checkout
  ci = commit
  cp = cherry-pick
  showt = "!f() { git dt $1^ $1; }; f"
  files = "!f() { git diff --name-status $1^ $1; }; f"
說明一下,絕大多數的 command 都縮寫為兩個字母,其中:
  • git lo -- 因為 git log 顯示的資訊太多,比較習慣一行一個 commit,所以設計了 lo,少個 'g' 表示比較短 XD。
  • git cbr -- current br 單純顯示目前 branch 名稱,主要是拿來給其他 script call 的。
  • git showt -- git show 可以顯示一個 commit 的 change 內容,加上 't' 表示要用 diff tool 顯示。
  • git files -- 列出某個 commit 修改了哪些檔案。

自訂 git 指令


可以在系統中放入 git-xxx 等以 'git-' 為 prefix 的指令 (放在 PATH 指得到的地方),然後之後就可以用 git xxx 使用那些自訂指令,打的時候會有 tab complete,所以當一個功能用 alias 做很複雜的時候,就可以把他獨立成一個檔案,結果就是一包各式各樣的 git-xxx,這包是從前人那裡 fork 來的,有很多功能,不過我沒有全部用到,fork 之後也立馬加了自己的東西,之後會提到。

開始修改


因為 git 的 light weight branching  特性,一般都是一個功能 (bug) 拉一個 branch 出來弄,所以在開始之前,我會先 sync 最新的 code 然後拉一個 branch 出來。另外為了避免弄髒,我會保持 master 跟 origin 同步,不會直接在上面操作自己的東西。
git co master
git pull
git co -b <branch>
開一條有最新 code 的 <branch> 出來。

改 code


就用喜歡的 editor 改 code,測試,這邊沒什麼。

檢查修改


當修改差不多到一個段落後,改動越小越好,但看起來必須有相關起且完整的。這時候我會開始 commit,但是真正 commit 前會先看一下改了哪些檔案
git st
輸出大概像這樣
# On branch demo
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       modified:   dom/icc/tests/marionette/test_icc_card_state.js
#       modified:   dom/icc/tests/marionette/test_icc_info.js
#       modified:   dom/system/gonk/RadioInterfaceLayer.js
#       modified:   dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js
#       modified:   dom/telephony/test/marionette/test_outgoing_radio_off.js
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
留意哪些檔案是有修改的,以及下面 untracked 的地方有沒有新增的檔案,有的話記得 commit 時要特別加進去。

當發現某個檔案出乎預期被動到了,會進去仔細看看改了什麼
git df <filename>
覺得太複雜就用 diff tool 開
git dt <filename>
如果發現是不小心動到,或是一些 debug/TODO 資訊,覺得可以拿掉了,就用
git co -- <filename>
那個檔案就會被 reset

commit


一般來說整個改動可能很大,會想把他拆成若干個 commits。我們先看最簡單的情形,打算把全部的改動放到一個 commit。
git ci -am "<commit message>"
複雜一點的,照 file 分割。
git add <file1> <file2> ... <fileN>
git ci -m "<commit message>"
注意 -a 是拿掉的,有時後不小心打了,就會全部連沒有 add 的也一起進去,解決辦法是參考後面會提到的「如何把一個 commit 拆成多個」。

最麻煩的,想將一個 file 內的改動,分散在多個 commits,這時候就要用 patch mode。
git add -p [<filename>]
有 file 的話就會只看那個檔案,不然的話就會全部一起看。執行結果如下
diff --git a/dom/icc/tests/marionette/test_icc_card_state.js b/dom/icc/tests/marionette/test_icc_card_state.js
index 535ff77..6f213b9 100644
--- a/dom/icc/tests/marionette/test_icc_card_state.js
+++ b/dom/icc/tests/marionette/test_icc_card_state.js
@@ -4,13 +4,15 @@
 MARIONETTE_TIMEOUT = 30000;

 SpecialPowers.addPermission("mobileconnection", true, document);
-SpecialPowers.addPermission("settings-write", true, document);

 // Permission changes can't change existing Navigator.prototype
 // objects, so grab our objects from a new Navigator
 let ifr = document.createElement("iframe");
+let connection;
 let icc;
 ifr.onload = function() {
+  connection = ifr.contentWindow.navigator.mozMobileConnection;
+  ok(connection);
   icc = ifr.contentWindow.navigator.mozIccManager;

   ok(icc instanceof ifr.contentWindow.MozIccManager,
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?
他會一個一個 hunk (小改動) 問,互動式的選擇要不要 include 這個 hunk,打 '?' 可以看各個選項的意義,我常用的有:
  • y: 要
  • n: 不要
  • q: 之後都不要了,離開
  • a: 之後的都要
  • s: 如果覺得這個 hunk 太大,可以選這個,讓 git 把他分割的更小,再來問
  • e: 要,但是用 editor 開起來,可以作細部的 diff 格式修改
做完之後,就等於做完 add,可以 commit 了。
git ci -m "<commit message>"
接著 repeat,用 git st 看一下還有哪些 change,一直作到所有都 commit 進去。

commit 的合併與整理


在這之後可能還會繼續改 code、commit,會需要整理 commit,可以用 interactive rebase。
git rebase -i <commit hash>
使用後會用 editor 開起來,上面每行代表一個 commit,下面的是說明。
pick d0c994e Add setRadioEnabled API (idl)
s daac55b Add setRadioEnabled API (dom)
pick a10139d Add radiostatechange in listener (bluetooth)
r d6b0ee2 Add setRadioEnabled API (ril)
pick f18042f Add setRadioEnabled marionette test

# Rebase f17c1b3..15b27cb onto f17c1b3
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out        
一開始前面的都是 pick,我常用的有:
  • 調換 commit 的順序 (之後可能會 merge fail),
  • s: 合併到前一個 commit
  • r: 改 commit message
  • e: 在打完這個 commit 後停下來,可以做些事
  • 刪掉那行,這個 commit 就會不見 (有可能讓後面的 merge fail)
存檔關閉,rebase 就開始跑了,如果都沒事,就結束了,有 conflict 的話他會說,遇到了直接輸入
git mt
會一個一個檔案用 merge tool 開起來,three-way merge,修改完存檔就表示那個檔案 resolved 了,全部做完後
git rebase --continue
繼續 rebase,有時候 (很少) 修完 conflict 後會發現這個 change 變成空的,這時候會打不進去,可以用
git rebase --skip
跳過這個 commit,通常這在 sync 新 code 要 rebase 才會出現,例如你要改的地方已經被別人先進了。

有時候 conflict 改一改,發現弄爛了,可以砍掉重來,取消這個 rebase,就會回到 rebase 之前的狀況。
git rebase --abort

commit 的分割


有時後會需要把一個 commit 拆成多個,假設要修改的 commit 是最後一個
git reset HEAD^
可以保持檔案內容不動,但是讓 commit history 回到上一步,這時候再用之前的 git add、git add -p 之類的弄出想要的 commits。

如果要拆開的不是最後一個呢?
git rebase -i <commit_hash>
用 rebase 回到前面一點的位置,在 interactive 時把你要拆開的 commit 標成 e (edit),這時候 rebase 到那邊就會停下來,現在你要修改的 commit 就是最後一個了,套用上面教的方法,最後做完後,記得要讓 rebase 繼續跑下去 git rebase --continue。

[自訂] git qapplied


通常我會打 git qa<tab>

這是 git log 的變形,一般來說我們只關心自己加的那幾個 commit,過去的那些 history 不太需要看,這個指令會從你的 branch 與 upstream (master) 的 LCA (Lowest Common Ancestor) 開始列出 log,
Branch demo (downstream from origin/master)
f17c1b3 .. enable ril_debug
d0c994e Add setRadioEnabled API (idl)
daac55b Add setRadioEnabled API (dom)
a10139d Add radiostatechange in listener (bluetooth)
d6b0ee2 Add setRadioEnabled API (ril)
f18042f Add setRadioEnabled marionette test
有了這個之後,我就很少用 git log 了

[自訂] git qrebase


通常我會打 git qre<tab>

類似 qapplied 的概念,在 interactive rebase 時,git rebase -i <commit hash> 都要打一個 commit hash,其實這個值也可以用 LCA 帶入,因為反正會修改的都是自己的那些 commits。

Sync 到最新的 code


隨著在自己 branch 上的開發,master 也會一直往前進,在 deliver patch 前要確保自己有 sync 到最新的 code,這樣產生的 patch 才是基於目前最新的 code,別人打你的 patch 時才不會遇到 conflict。
git co master
git pull
git co <branch>
git rebase master
如果在 rebase 中遇到 conflict,就用前面提到的方法解決。

Patch 的產生


上述的那些方法,可以做出乾淨整齊的 commit,再來就是把這些 commit 輸出成 hg 格式的 patch 檔,可以給別人 review 或是到 hg 那邊 push。

我有改一個類似 git format-patch 的 git format-hg-patch,他會先用 git format-patch 產生想要區間的 patch 檔 (通常我是下 -N, 表示倒數 N 個 commit),接著自動用另一個 tool 把 git format patch 改成 hg format patch,如下:
aknow@aknow-pc:~/workspace/mcg/patch$ git qapplied
Branch demo (downstream from origin/master)
f17c1b3 .. enable ril_debug
d0c994e Add setRadioEnabled API (idl)
daac55b Add setRadioEnabled API (dom)
a10139d Add radiostatechange in listener (bluetooth)
d6b0ee2 Add setRadioEnabled API (ril)
f18042f Add setRadioEnabled marionette test
7497290 Modify related tests
a7bf0a9 .. remove enable radio when power on (ril)

aknow@aknow-pc:~/workspace/mcg/patch$ git format-hg-patch f17c1b3..7497290
0001-Add-setRadioEnabled-API-idl.patch
0002-Add-setRadioEnabled-API-dom.patch
0003-Add-radiostatechange-in-listener-bluetooth.patch
0004-Add-setRadioEnabled-API-ril.patch
0005-Add-setRadioEnabled-marionette-test.patch
0006-Modify-related-tests.patch

aknow@aknow-pc:~/workspace/mcg/patch$ showpatch
HG Part 1: Add setRadioEnabled API (idl)
HG Part 2: Add setRadioEnabled API (dom)
HG Part 3: Add radiostatechange in listener (bluetooth)
HG Part 4: Add setRadioEnabled API (ril)
HG Part 5: Add setRadioEnabled marionette test
HG Part 6: Modify related tests
showpatch 是我另一個小工具,會看目錄下的 .patch,然後列出他們的 title。

再來因為我們的 commit mesasge 中要加上 bug id,我覺得如果每次 commit 都要在 commit message 中打實在很麻煩,尤其是那個 id 一般來說都不會記得,要另外去查, 造成頭腦的 context switch,所以我選擇弄另一個 tool 出來,markbug,負責幫 patch 檔加上想要的 bug id。

至於要怎麼查 bug id 呢,我加了一個指令 git bugs,使用的話要先裝兩個 python packages:
  • bztools: 查 bugzilla 用的 api
  • colorama: 因為我很假掰的想要讓 console 的輸出有顏色,但是不想打色碼
所以一整套用下來大概是這樣:

  • git bugs: 查 bug,複製對應的 bug id
  • git qapplied: 看要生成幾個 patch 檔
  • git format-hg-patch -N | markbug <bug id>:  經過一個 markbug 的 pipe,貼上 bug id
  • showpatch: 檢查最後結果
showpatch 現在前面的綠色 HG 感覺有點雞肋,那是以前還沒弄 git format-hg-patch 時,hg patch 的生成是拆成兩步驟的,有時後會忘記 convert,所以特地在這邊檢查 patch 的格式是 git 還是 hg。

結束,其實用久了,都是那幾個而已。

選擇錢還是熱情?

可能是年紀到了,最近腦中總是有一堆人生思考,如果不偷懶的話,大概可以寫成好幾篇吧。 

這幾天在 PTT Tech_Job 版看到有人在問「錢重要還是熱情重要?」,基本上這是個月經文,也會以不同的問法出現,例如「要選擇錢多還是有興趣的工作?」。看了看討論串,覺得都沒有很點到我心中的想法,所以我決定上來喇賽一下。

首先摘要一下大部分人的觀點

  1. 「有錢就可以激發熱情」 => 錢
  2. 「做很有興趣的工作最後也會被鳥事磨光熱情的」 => 錢
  3. 有熱情才能堅持賺到錢。最終是錢」 => 錢
  4. 「應該是問興趣熱情值多少機會成本」 => 錢

我不是故意只節錄選擇錢的,而是大部分選擇熱情的都比較像是 3,或是像是成功人士愛說的「不是為了賺錢而出發,而是當你把事情做好,錢自然就會進來」。

看到這裡,你可能會覺得我會說應該要選擇興趣,是的,如果你問我這個問題,我的答案會是「熱情」,但是每個選擇的背後,都跟那個人的立場有關,所以這個答案也只適合當下的我,並非每個人都該這樣選擇,可以參考支持 passion 的論點:The 10 Reasons You Should Follow Your Passion And Not The Money

首先,這個問題是個不公平的比較。

Ref: http://tds.ic.polyu.edu.hk/td/theme1/image/maslow.jpg

根據馬斯洛金字塔,人必須先滿足生理需求、安全需求,而這些基本的食衣住行都需要錢,所以在這個階段錢非常的重要,但是熱情就比較沒有關係了。

接著到了上面的尊重與自我實現,這就比較像是熱情的部份,今天因為我已經有滿足生理需求的錢,也認為熱情可以幫助我達成自我實現,所以我的答案就是熱情。但是也有人會覺得錢可以為你帶來尊重,帶來自我實現,那麼對他來說錢就是他的目的。

你的人生要什麼


如果再往上看,其實錢跟熱情都不是你人生真正想要追求的東西,他們只是可以幫助你得到你想要的中繼品。對於這點,我比較認同在 << 更快樂 >> 一書中所說的「快樂,是人人渴望的終極貨幣」,人要的其實很簡單就是快樂的一生,每個人對於錢可以帶他多少快樂,會有各自不同的比例,而對於追求熱情也是。所以我們要換算的不是,這些熱情等價於多少錢,然後拿來跟錢多的比,而是應該通通換算成快樂的指數。

看到有一個人這樣推「不管熱情或錢 能讓你快樂過日子比較重要 自己取捨吧」,大概有點接近我心中的想法了。

我要的是什麼


於是慢慢地就會開始想,我這一生想當個怎樣的人,最近學到一個 CENTER 的概念,CENTER 是 Character、Entrepreneurship、owNership、Tenacity、Excellence、Relationship 的縮寫,藉由回答一些問題,可以更瞭解自己,知道自己想成為怎樣的人,以及該怎麼做。

我並不喜歡賺錢,我可能會說自己沒錢,錢很少,但是讓我選擇要不要把時間花在賺錢上,其實我也不想。這從一些人創業的動機就可以看出來了,有的出發是賺錢,市場評估後,覺得會賺就進去了。例如說,台灣的飲料商機非常的大,透過簡單的估計與計算,要知道在某個地方開一家店(也許是加盟)會不會賺似乎沒有很難,如果我是一個愛賺錢的人,當發現會賺的時候,就會意無反顧的進去,而且這還是打造被動收入的現金流,多好阿。那我會不會想做呢,想了想覺得還是不會,這件事對我來說就是單純的賺錢,一點都不有趣,除了可以當店長,聘請年輕美眉來打工。所以如果哪天我真的開了一家飲料店,我應該只是嘴饞自己想喝,或者是想要提供就業機會給... XD。

我喜歡寫程式,但是更進一步思考,我喜歡的應該是動腦解問題,喜歡擁抱技術,喜歡變強的感覺,就像角色扮演遊戲練功一樣。最近我每一天睡前都會問自己,今天是不是有學到新東西,今天有沒有比昨天的自己更厲害。有一個說法是「只要你每天都進步 1%,一年下來就會進步到 37 倍。」,說實在的我覺得這是屁,人的成長怎麼可能是指數型的,但是不可否認,我們依然可以選擇讓自己每天都有所成長。

有篇文章在說,喜歡寫 code 的人都不賺錢,其實沒什麼,就是選擇而已,每個人獲得快樂的來源不同,追求的東西也會不同。

所以讓我草草結論一下,認識自己真的很重要!!!

打 patch 時遇到 conflict 怎麼辦

工作中常常會需要把別人提供的 patch 檔加入自己的工作目錄,在 git 中有兩個相關的指令 git applygit am。如果 patch 成功,那就皆大歡喜,可是最麻煩的是他常常會失敗,這篇文章簡單紀錄一下我是怎麼處理這個 patch 的流程。

這個問題跟 merge 產生的 merge conflict 是不同的,merge 的過程,你會有 local、remote、base 三個版本的檔案,這三個都是完整的檔案,所以可以很簡單的透過 merge tool 的 three-way merge 解決。

而 patch 不同,你有的只有一份完整的檔案,和只記了修訂差異的 patch 檔。相當於你不知道 base 是什麼,也沒有完整的 remote,只有 remote - base 的差,所以整個資訊量是差很多的。

首先,可以用這個指令打 patch
git apply sample.patch

如果不幸失敗了,原始檔案會維持不動,等於你什麼都沒有做。可以換成
git apply --reject sample.patch

這樣 git 會把 patch 檔中可以合進去的 hunk 加進去,剩下的放在 *.rej 中,* 是對應的檔名。所以到目前為止,有稍微往前一點了,再來就是看 *.rej 裡的 change 要怎麼加。

要知道 patch 打完後,會產生哪些 *.rej,可以透過
git status

就會在 untracked 的部份看到他(如果你沒有把 .rej 加到 .gitignore)
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)

接下來可以使用 wiggle(wiggle - apply rejected patches and perform word-wise diffs),他會更積極的幫你 patch,當然加錯的風險也會變大,記得一定要檢查。

安裝
sudo apt-get install -y wiggle

然後透過這樣的方式使用
wiggle --repalce foo.cpp foo.cpp.rej

他會備份一份原始檔案在 *.porig,然後把可以合進去的 change 直接寫在原始檔中,而無法判定的部份,wiggle 會猜測可能的位置,然後把包含 merge marker 的 change 寫進去,例如
<<<<<<<
Some portion of the original file
|||||||
text to replace
=======
text to replace it with
>>>>>>>

用 diff tool 檢查一下 wiggle 寫進去的東西,例如
vimdiff foo.cpp foo.cpp.porig

然後手動把那些有 conflict marker 的整理好。如果整個都合錯的話,恭喜你,只好把備份檔 *.porig 弄回來,然後用肉眼看 *.rej,通通自己來。

最後,因為 wiggle 會把 conflict marker 寫進去,所以可以在 git commit 時,透過 pre-commit hook 的機制,檢查檔案內是否有包含 conflict marker 作多一層的保險。pre-commit 也不用自己寫,這邊剛好有一個現成的 project

透過 tmux 與 tslime 建立連結 Vim 與 REPL 的開發環境

名詞解釋


tmux:terminal multiplexer
What is a terminal multiplexer? It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal. And do a lot more. 
簡單講就是可以讓你把一個 terminal  畫面變成很多個,然後在其中切換。類似的 tool  還有  screen,不過我都用  tmux,覺得他比較強。

REPL:read-eval-print loop
A read–eval–print loop (REPL) is a simple, interactive computer programming environment. 
簡單講就是那些可以 interactive 下指令跑的  language shell,ex:python, perl, node, bash。

所以我們要解決的是什麼問題


舉例來說,當我們在寫一個 python script 時,可能會想知道某段程式,某一行寫的對不對,是不是如我們想的,甚至是某個 regular expression,想要馬上測測看是不是真的能 match 到想要的東西。這時候我們就要從 editor 的環境切換到 REPL,在互動的環境中執行那些 code,然後看結果如何。

透過 tmux,我們可以把畫面切成兩塊,一邊是 editor,另一邊是 REPL,只要把 code 從 editor 複製到 REPL,就可以兩邊對照,不需要進進出出,造成大腦多餘的 context switch。

但是這樣還是要手動複製貼上,利用 Vim plugin tslime,就可以簡單的把這段處理掉。


設定與使用方式


我的 tmux 設定檔在這邊,主要是把 prefix 設成 <C-A> (Ctrl + A),讓他跟 screen 一樣,方便無痛轉換,以及分割 key mapping,讓他跟 Vim 的概念類同,用起來比較直覺。
<C-A> v:vertical split
<C-A> s:horizontal split

tslime 的部份用 vundle 裝起來就好,然後在 .vimrc 裡寫
vmap <C-c><C-c> <Plug>SendSelectionToTmux
nmap <C-c><C-c> <Plug>NormalModeSendToTmux
nmap <C-c>r <Plug>SetTmuxVars

使用時,先用 tmux 切一塊出來,進入 REPL,例如圖中的 2、3 分別是 nodejs 跟 python 的。接著在 Vim 中按 <C-c> r,他會提示你要跟 tmux 的哪塊連結,這時候有 tab auto complete。tmux 是三層的架構,session / window / pane,以我圖中為例,左下角 > 0 < 表示  session = 0,0:vim* 表示 window = 0 (這例子有點爛 XD),如果切割比較複雜的話,pane 較不直覺,可以用 <C-a> q 顯示 (或是用 <C-a> ? 列出所有的 key binding,找一下 display panes)。

設定好後,在 Vim normal mode 或是 visual mode 按 <C-c> <C-c> 就可以把那行或是選取區間的  code 丟到設好的那塊 tmux,也就是 REPL 的環境。

大功告成,我現在都用這樣的環境在寫 shell script、python、nodejs,超方便,應該要列成「真希望我20歲就懂的事」。

關於工程師 (Reply to 三種工程師 -- Coder, Hacker and Architect)

這篇主要的目的是回應 <三種工程師 -- Coder, Hacker and Architect>,並亂扯一下我目前的心得,建議可以先看完那篇,會比較好懂。

最大的問題是我不認同這個分類,通常分類都是互斥的,但文中的這三個類別,給我的感覺卻怪怪的,另外也不該有哪一個更高尚的比較。

Coder

第一個角色是 Coder,就是僅僅把寫程式當成工作的人。廣義的來說這不受限於寫程式或是工程師。這種人就是沒興趣沒熱情,為了謀生而工作的人,或是其實不缺錢,但搞不清楚為什麼而工作的人。

這個問題是,這些人就是把工作當成雜事或是職業,而不是志業,每天早上去上班,因為必需要去,而不是自己想去,去了之後就開始期待領薪水,期待放假。而讓自己做好的主要工作動機都是外部因素,升遷、加薪、權力。

把工作看成志業的人,會認為工作本身就是他的目的,動機來自於內在因素,覺得有貢獻,覺得在實現自我。當然這種人也是在乎薪水跟升遷的!

這兩個的差別就是工作態度。會走上 Coder 之路的人,是因為他在選擇工作時,思考的是「會做什麼」,「擅長做什麼」,因為我會寫 Code,我唸了資訓相關,所以找了一份寫 Code的工作。

曾經我也很瞧不起這種人,就是因為有這種人在,造成劣幣驅逐良幣,工程師的地位低落,我甚至覺得,在一個公司裡,這樣的人大概有八九成,一個人到底有沒有興趣有沒有熱情,其實都會彰顯出來的。不過阿,就像最近很紅的這篇「我在微軟學到大學沒教的事」所說,不是每個人提到寫程式時都會滿腔熱血,是阿,It's all about priorities,也許他就是不想成為一個偉大的工程師,試著去尊重每個人的選擇。

不過雖然這麼說,這種人也讓人覺得很可惜,我認為他們應該試圖尋找一個可以當作自己人生志業的工作。就如同馬斯洛說的「一個人最好的運氣和最大的福分,就是有人付錢請他從事他衷心喜愛的工作。」

所以怎麼找到自己的志業呢?

管理學大師 Stephen Covey 認為一個人的獨特貢獻應該是天賦才能、工作熱情、需求、道德良知的交集處 (From <Great work, Great Career>)。

哈佛幸福課講師 Tal Ben Shahar 則說,可以透過 MPS (Meaning Pleasure Strength) 的流程來幫助自己,思考什麼東西做起來有意義,快樂,而自己也擅長。

LinkedIn 創辦人 Reid Hoffman 也說了一個類似的,什麼是你的競爭優勢,是資產、抱負、市場狀況,三者缺一不可。

所以,我想應該很清楚了吧!

Hacker

再來就是什麼是 Hacker,有人提到文中 Hacker 的定義怪怪的,是的,這正是我想說的,Hacking 是一種精神,擁有 Hacking 精神的人就是 Hacker,更詳細的可以看 How to become a hacker,另外說自己是一個 Hacker 也怪怪的,「You aren't really a hacker until other hackers consistently call you one」,就像你不能自己說自己是個好爸爸,這要由你的小孩來說。話說,我也想成為一個 Hacker,可以有 Hacker 來認可我嗎 XD

前陣子在 PyCon TW 聽了一個 talk,有一個我很喜歡的 Hacking 定義:「在條件限制下,達到預期外的效果」,講者說了一個 Hack checkIO 的故事,checkIO 是一個透過玩遊戲學寫程式的網站,基本上 online judge 就是給你一組 input,要求算出某個東西,然後跟標準 output 比較,看你是否正確,checkIO 使用
assert checkio(<given input>) == <golden output>
的方式比較,結果他把 == 做 operator overloading,讓結果不管是什麼,== 都是 True,瞬間通通破台了。這就是 Hack,預期外的效果。

而 Hacker 也沒有限制要是工程師,只是對於軟體工程師來說,作這樣的事有較大的優勢,很容易作些什麼就讓這個世界更好。是的,就是解決問題,然後回饋社會。用這樣的觀點來看,g0v 就是一群很棒的 Hacker。

Architect

Architect 其實是個職稱,地位比 Engineer 高很多,我認為分成會寫 code,跟不會寫 code 的,在大公司裡很多這種不會寫 Code ,只會動嘴巴的,well... 不予置評。

其實人人都是 Architect,程式設計師的三個美德,懶惰、不耐煩、傲慢。為了懶惰,自然而然就會產生有秩序,可以永久保存、重複使用的東西。這麼說好了,打造這些東西都是為了日後可以偷懶而努力。

再來原文中所說的 Architect 災難,比較像是 Joel on software 中的 architecture astronauts,當架構過了頭,而失去了原本真正要解決的問題。

最後

我發現我有點想要偷懶,開始虎頭蛇尾了 XD,不如就在這邊下個結論吧。
我一直認為工程師是很偉大的,也期許自己可以成為一名偉大的工程師,所以就讓我們一起

Hack the world and code for tomorrow!

誤打誤撞研究了 Vim 的顏色設定

緣由


不知道為什麼我的顏色設定在 vimdiff 時,剛好 highlight 的背景色與某種 syntax 的前景色一樣,導致整個字像透明一樣,diff 時看不到字,一整個就是搞笑,所以我決定一定要修好他。

在決定要修他之前,我的妥協作法是,在 diff 時手動設成別種 colorscheme。上網查發現也有人有這問題,一個解法是在 vimrc 裡設定,偵測跑 vimdiff 時就自動套別種顏色,不過根本的解法還是把顏色調好,這才是一勞永逸。

就在我調顏色時,突然發現了一個關鍵點,我的 Vim 怎麼不是 256 色!!!,我明明就設了,Oh NO。

最後終於找到,原來是 tmux,我的 Vim 是透過 gnome terminal => tmux => vim,tmux 那層沒設好,顏色就不見了,哭哭。

所以我決定把我的心路歷程寫下來。

開啟 256 色的 Vim


terminal

我會用我的環境當例子,首先是 terminal,我是用 ubuntu 的 gnome terminal。
aknow@aknow-ubuntu:~$ echo $COLORTERM
gnome-terminal

他本身就有支援 256 色,這裡有個可以測試的程式 256-xterm-colors,可以跑看看確認 terminal 顏色沒問題。

From: https://github.com/guns/xterm-color-table.vim

tmux

接下來是 tmux,記得要在 .bashrc 裡加上:
export TERM="xterm-256color"

進 tmux,再跑跑看剛剛的程式,驗證 tmux 裡也有 256 色。

Vim

在 vimrc 裡設定:
set t_Co=256

開啟 Vim,執行:
:runtime syntax/colortest.vim

你會看到一些測試的顏色,當然還有更威的 256 色測試 xterm-color-table.vim,裝完後執行:
:XtermColorTable

From: https://github.com/guns/xterm-color-table.vim

如果這些都沒問題,恭喜你有 256 色的 Vim 了。

Colorschme 的選擇


Vim 有一些預設的 colorscheme,你可以選擇你想要的,執行以下指令就可以設定:
:colorscheme <scheme>

:colorscheme 可以簡寫為 :colo (懶人的哲學)

不知道有什麼 <scheme> 嗎?可以透過這兩個方法列出已安裝的 scheme。

  • :colo <space> <tab>
  • :colo <space> <c-d> (ctrl+d)


覺得不夠的話,還可以到 vimcolorschemetest,這裡有 400 多種 scheme 可以預覽和下載。

細部顏色的微調


如果你很龜毛還是想要調顏色的話,可以去修改 scheme 裡的設定,vim help 裡有說怎麼設。
:help highlight

基本上像這樣:
hi ErrorMsg       term=standout  cterm=bold  ctermfg=7  ctermbg=1

數字就是色碼,可以從剛剛的 xterm-color-table.vim 得到,然後前面的那個是 highlight group,想知道有哪些 highlight group 可以設,跑這個指令:
:so $VIMRUNTIME/syntax/hitest.vim

他會把各 highlight group 與目前的顏色顯示出來。


fixmyjs - 自動修改 js 的 lint errors

JSHint 是由 community 維護的 static code analysis tool,會掃過你的 code,然後列出錯誤或潛在的問題,不過這不是本文的重點,所以剩下的就不介紹了。

在列出錯誤後,你可以選擇手動去修改,或是有些錯誤實在是太愚蠢了,根本可以透過程式來幫你改,於是就有了 fixmyjs
FixMyJS is a tool that automatically fixes mistakes—such as missing semicolon, multiple definitions, etc.—reported by JSHint.

他有提供幾種執行方式:

我是用 Nodejs 的方式,可以從 command line 去跑,比較方便,基本上執行時有三個模式可以選擇:
  • 直接修改
  • dry-run,列出 diff
  • 生成 patch 檔

我習慣讓他直接改,然後用 version control tool 來看他到底改了什麼,而這時有些改錯的就可以順便修正一下。所以這不是完美的,用的時候要自己注意。例如,這樣的 javascript code
"use strict";
 
var foo = {
  fun1: function() {},
  fun2: function() {}
  fun3: function() {}
}
由於 JSHint 會報出這樣的錯誤
test.js: line 6, col 3, Expected '}' to match '{' from line 3 and instead saw 'fun3'.
test.js: line 6, col 7, Missing semicolon.                                        
test.js: line 6, col 7, Expected '(end)' and instead saw ':'.                        
他覺得第 6 行少一個分號,但我想要的應該是第 5 行少一個逗點,於是根據 JSHint 的結果 fixmyjs 就把他改成了這樣,在錯誤的地方補了 "Missing semicolon" ...

我怎麼用 github 與 Vundle 管理 Vim 的設定

這應該算是篇心得文吧!說說我走過的路。

先看一下為什麼會需要這樣的管理,以及他解決了什麼問題,我個人認為最重要的兩點是:
  1. 跨電腦、平台地同步設定
  2. 套件的管理與更新

Stage 1. 用 github 同步 


首先 Vim 用久之後,上面一定會有很多你自己的設定,也奘了亂七八糟的套件,你會希望當換到另一台電腦的時候,這些東西也可以一起同步過去。

如果只是要單純的同步,使用 dropbox 之類的工具就可以解決,可以把東西都放在某個 folder,然後把 Vim 的 runtime path 指過去,或是在 vimrc 裡 source dropbox 裡的檔案。

而這部份使用 github 的處理的好處是,你不需要先把 dropbox client 設好,只要有 git 就可以使用,這在 linux / mac 上更是顯得方便。另外透過這個方法,你的 Vim 設定還會有 version control 的功能,雖然我自己從來沒用上這點。

所以呢,你要做的事就是開一個 github account,然後把 Vim 設定統統丟進去,然後到別的環境使用 git clone 把你的設定抓下來,任何時後有修改再 commit / push 回去,以及用 pull 拿到最新的版本,就是這麼簡單。

Stage 2. pathogen 套件管理 / git submodule


以往裝 vim 的套件非常的麻煩,抓下來後,套件的各檔案,可能要放到不同的目錄裡,當裝了很多套件後,你的目錄就會超亂。這時候就有人開發了 pathogen 這個管理套件的套件,他藉由調整 runtime path 的方式,讓你可以把各套件裝在各自的目錄裡,整個就變得清爽整齊。

再來是套件的更新,許多套件都會持續的開發,可能是修 bug,也可能是加功能,這時候你可能會希望這些套件都有自動更新的功能,讓你維持在最新的版本。而這就是 git submodule 出馬的時候了,可以看一下這邊 Joseph 大大的 submodule 介紹,大致上就是說,你可以把別人的 git 當作 submodule 掛到你的 git repository 的某個位置。

整個用起來的原理是這樣,pathogen 可以把套件裝到某個位置,也就是你讓 Vim 知道要去哪裡 load 套件,而這時再用 git submodule,把這個套件的 git 掛到那個目錄,這樣你只要透過 git 更新,就可以隨時拿到最新版的套件。以 vgod 大大的 Vim 設定為例,這裡面的每一個項目都是一個 submodule,指到各套件的 git repository。

這部份的設定就不說了,因為這樣的方法我覺得已經不是主流了。

Stage 3. Vundle 套件管理


使用 Stage 2 的方法,必須要把套件一個個地裝成 submodule,十分的麻煩,而且當你不用的時候,要移除 submodule 也很痛苦,就我所知大概要下個 5 行指令才能作到。

受到 pathogen 的啟發,有人開發出了 Vundle,只要在 vimrc 裡寫明那些套件的 git repository 在哪,Vundle 會幫你作對應的 git 操作,而當 plugin 不用時,只要在 vimrc 裡把那行設定 comment 掉就好。

整個設起來會像這樣,現在你只需要一個 submodule 那就是 Vundle,其餘需要的套件就寫在 vimrc 裡,由 Vundle 來幫你管理。

Stage 4. 把 Vundle 的安裝直接寫在 vimrc 裡


Stage 3 的結果已經作到剩下 Vundle 一個 submodule,如果我們可以把這個也拿掉,那一切就會更簡潔漂亮,在網路上找到一個作法,他會在 vimrc 裡檢查你是否有裝 Vundle,沒有的話就幫你裝起來。

所以目前我的設定就是這樣,可以參考我的 vimrc 的前半段,當然,把 Vundle 的安裝也寫進去,只是爽度的問題,實質上沒什麼不同。

備註


有另一個 Vundle 的改版 NeoBundle,他提供更多的功能,這邊有篇文章有詳細的說明,主要是增加了:

  1. 套件可以定版,不是總是最新,可以鎖在某個 commit 以避免新版的 bug 或不相容
  2. 不只支援 git,還加了 svn、mercurial
  3. 可以設 build command,有些套件需要編譯,例如 YouCompleteMe,這時就可以設定安裝時要自動執行哪個 script

目前我沒有改用這個,主要是這些特點對我還說還不是必要,就懶得改了。

Programmer 都愛貼 code,gist 與 gist-vim 的搭配用法

身為一個寫 blog 的 programmer,不能少的就是在 blog 上面貼 code,最近的流行是用 gist,gist 可以讓你把一段 code 放在 github 上,而且還可以有 version control。

在 gist 畫面的左邊,會給你一個 embedded this gist 的連結,像這樣
<script src="https://gist.github.com/aknow/6232870.js"></script>

把他複製貼到 blogger 上就可以了,成果如下。

但是阿,如果你是手動把 code 弄到 gist 上面,那這樣就太遜了,基本上各大編輯器都有跟 gist 互動的 plugin,如果你用 Vim,可以使用 gist-vim,可以讓你直接在 Vim 裡生成、編輯、讀取 gist,完成後還可以幫你把連結開起來。

當然,這篇用的 gist 就是這樣產生的。

保持專注的工作方法

幾個月前曾經在 facebook 上看這到這張圖的分享,上面提到了幾種保持專注的方法。我們知道如果工作或學習時能夠進入專注的「心流」狀態,這時就會發現時間似乎過得超快,而生產力也爆增。

From: http://fundersandfounders.com/focus-hacks/
所以我們得努力讓自己進入這樣的狀態,但問題是,事實上常有些因素讓我們分心,大致上可分成兩種,外界的干擾,以及自己分心,今天要跟大家分享我利用圖中這兩項
  • put on headphones
  • time yourself
所嘗試過的努力與方法,基本上這應該算是要對抗自己分心。
首先是帶上耳機,帶耳機除了可以隔絕外界的聲音,不會受干擾,也可以減少自己想去聽辦公室八卦的欲望。但是太安靜其實反而不好,所以我還是想讓自己聽一些東西,這邊有個關鍵點,千萬不要聽有聽得懂的歌詞的歌,不然是會被帶走的,所以到底要聽什麼呢?
我有聽過白噪音(white noise),科學研究白噪音有許多好處,例如幫助睡眠,阻擋分心,提昇專注力,有一個網站 SimplyNoise 可以產生這樣的聲音,他還有 pink noise,brown noise 各式變形,這稱之為 colors of noise,有興趣可以去看看。另外 android 上也有很多 app 提供白噪音的產生。
好了,但是當我聽了一陣子之後實在是受不了,覺得自己像個笨蛋。這時候我發現了 coffitivity,他會產生像是身處在咖啡廳的聲音(不過是英文的),咖啡廳的好處我想很多人都知道,這也是為什麼許多人喜歡在那邊讀書或是工作,而我自己的親身體驗也是,在大學的時代的某陣子,我甚至都待在早餐店念書。
coffitivity 建議使用咖啡廳環繞再加上略為大聲的個人音樂,所以除了這個,我還搭配了一個音樂,真的有效,不過還是一樣,在連續聽了一個禮拜過後,就開始有點膩了。我開始在 youtube 上找還有什麼適合的,用關鍵字「music productivity」,發現許多人都建立了屬於他們的 productivity 播放清單,本來我也想仿照蒐集一個,但是工程師的美德就是懶,非不得已絕不自己動手,幸運的是我發現了這個,Study Music Project,以及他的 youtube channel
The Study Music Project is a collection of music productions composed, written, and produced by Dennis Kuo.The sole purpose of the music is to help you focus as you study for school. Instrumentation, melody, and other musical articulations have been well thought-out to enhance concentration.
所以我現在的搭配是 coffitivity + Study Music Project,試用了一陣子,感覺還不錯,包括我寫這篇 blog 時也是這樣聽的。
再來是 time yourself,我是用 pomodoro 的方式在工作,詳細內容就請大家自己去看,簡單講就是以 30 分鐘為一單位,認真做 25 分鐘然後休息 5 分鐘,接著下一個循環。有許多 pomodoro 的相關工具,desktop tool,chrome extension,mobile app,我習慣用的是 android 上面的 pomodroido,有一些功能,但是我只有當作計時器用而已,好處是有一個時間的 progress bar,當看到的時候就可以提醒自己,還有多少時間,要堅持下去,不要隨便分心。
在使用 pomodoro 時,我還會準備一個 interrupt list,萬一真的分心時,趕快把想到的事情記下來,這樣你的腦袋就會知道這個東西已經記在別處了,可以不用一直掛念,趕緊回到 focus 狀態,不過其實這也是標準 pomodoro 上面教的方法。這個 interrupt list 我是用 trello 管理的,並不是說 trello 特別適合,而是因為我是用 trello 在管理我的工作事項,這些 interrupt 很有可能變成之後的 todo item,建議直接用同一個工具管理。
其他的有機會再來分享,我也要朝著有生產力的偉大航道前進。

在 Vim 中使用 clang_complete 做程式碼補完

先看一下效果,在這個例子中,我使用 standard library 中的 vector 容器,當打到 v. 的時候,Vim 會自動跳出視窗列出可以使用的 function,圖片中顯示的是再打了 p 之後的篩選結果。

之前我是使用 ctags + OmniCppComplete來做程式碼補完,他的缺點是:

  1. 要先產生 tags。
  2. ctags 其實看不懂程式碼,有些 code 會跑不出來,例如 libstdc++,所以網路上常見的作法是拿一份修改過的 header 給 ctags 跑。

總體來說,這樣搭配的補完效果跟 visual studio 是完全不能比的。

clangcomplete 這個 plug-in 跟 OmniCppComplete 不同,他是使用 LLVM compiler 的 front-end clang 去 compile 你的程式,所以可以列出最精確的補完清單,甚至你還可以給他 include path 跟 compile option,又會更準。這樣用起來,補完的資訊跟就變得跟 visual studio 一樣,都是由 compiler 提供,相信整體的品質應該會好上許多。

回到先前的例子,可以把 clangcomplete 的執行想成是先 compile 你的程式,再根據結果給你補完的清單,所以如果我前面忘了打 #include ,這個清單是會跑不出來的,會找不到。

安裝方式

  1. 安裝 clang,ubuntu 可以透過 apt-get 或是軟體商城把套件 clang,libclang-dev 裝上即可。windows 的話可以抓 source code 自己 build,或是他有提供一個基於 mingw32win32 binary
  2. 裝完後可以參考網頁上的範例,試一下,確定你的 clang 是好的。記得要把 clang / libclang.a / libclang.so / clang.dll 的位置加到 PATH 中。
  3. 安裝 Vim plug-in clang_complete,參考作者的安裝說明,或是你有使用 pathogen.vim 在管理 plug-in 的話,那麼不用我說,你一定知道要怎麼裝!
  4. 根據我的經驗,在 windows 的話,要複製一份 clang.dll 改名為 libclang.dll,這樣 clang_complete 才找得到。

使用方式

  1. 當打到 . / -> / :: 等的時候,補完視窗就會自己跳出,或是可以按 。
  2. 其他相關的使用與設定
  3. clang_complete 會跟一些補完的套件衝突,例如 OmniCppComplete,記得要先把那些停掉。

延伸 Reference

成為一個努力的人 -- 我的 MOOCs 之旅

記得在去年底的時候,我對自己許了一個新年願望:希望在新的一年,每一天都可以覺得比昨天的自己又更進步了,就如同網路上流傳的一句話「每天進步0.01!一年後你就比現在強大36倍!」。是的,我想成為一個努力的人,我想透過一連串的習慣與改變,讓自己更努力。 不過說真的,要一直努力,其...