Category Archives: Git

如何在本地端取出和送出 Github 的 Pull Requests

最近意識到 Github 上的 pull requests 其實也是 branches 參照,所以你也可以根據 pull request 編號在本地端直接取出。對於常用 pull request 功能來作 code review 的團隊來說,是蠻方便的小技巧。

首先編輯專案下的 .git/config,加上 fetch = +refs/pull/*/head:refs/remotes/origin/pr/* 這一行,例如:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = git@github.com:ihower/sandbox.git
    fetch = +refs/pull/*/head:refs/remotes/origin/pr/*

接著打 git fetch origin 就會抓下來這些 remote branches:

From github.com:eduvo/keybridge
 * [new ref]         refs/pull/1/head -> origin/pr/1
 * [new ref]         refs/pull/2/head -> origin/pr/2
 * [new ref]         refs/pull/3/head -> origin/pr/3

接著用 checkout 取出,例如 git checkout pr/3 即可。

喜歡的話,也可以設成 git 全域設定,直接套用所有專案:

git config --global --add remote.origin.fetch "+refs/pull/*/head:refs/remotes/origin/pr/*"

參考資料: Checkout Pull Requests Locally

本地端送 Pull request

舉一反三,那要怎麼在本地端送 Github 的 Pull Request? 參考 HSATAC 的這篇:用 Commandline 發 Github Pull Request

首先安裝 github 的 hub 工具:

brew install hub # 或
gem install hub

編輯 ~/.bash_profile

# https://gist.github.com/hSATAC/5591270#file-gistfile1-sh
# Usage: pr (pull request current branch into develop)
# Usage 2: pr stable (pull request current branch into stable)
# Notice: replace "team" with your github team account.
 
function pr() {
    base=$1;
    if [ "$1" == "" ]; then
        base="develop"
    fi
    hub pull-request -b team:"$base" -h team:`git rev-parse --abbrev-ref HEAD`;
}

Yet another introduction to Git – from the bottom up 投影片

這是今年在 COSCUP 演講的投影片,Git 應該是近幾年最流行的版本控制系統,也有非常多的入門和教學資料。這次我則是從底層的原理和操作來介紹,探討 如何不用 git add 和 git commit 指令進行 commit 動作?

了解 Git 的資料結構之後,就會發現其實內部原理並沒有很複雜。對照發明人 Linus 的話,就會讚嘆版本控制系統就該這樣設計啊!

I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers
his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships. by Linus Torvalds

p.s. 會後有會眾問我關於 workflow 和 rebase 的問題,可以參考我之前的課程投影片

Git rebase 和 merge 合併操作示範錄影

昨天的 Git 教學投影片 還蠻多人按贊分享的,謝謝大家。不過其中少了重要的 live demo 覺得有點可惜,所以來把其中最有趣的一段錄下來好了。第一次錄影加聲音發現還蠻緊張的,獻醜哩。

這是先用 rebase 整理 commit history (包括修改訊息、修改內容、刪除、拆開、新增和調換commit順序),然後再進行 merge 的示範。但誠如我投影片 p117~p118 所說,這是一種追求完美潔癖龜毛的作法,但是看到最後的 commits 線圖這麼漂亮,就覺得值得啦,而且中間的過程很有趣不是嗎? XD (除非一直發生 rebase conflict 情況啦,那就不好玩了,請量力而為 :p)

情境是我們想把 feature/forum 這個 branch 合併進主幹 master,這是合併前:

以下是直接在 master branch 做 merge feature/forum 的合併結果。你會發現 feature/forum 與 feature/chatroom 這兩個分支出現交疊的情況,當合併的分支一多,線圖就容易變得雜亂:

以下是改成用 rebase 整理之後再 merge 的方式,做出漂亮的合併線圖:

ps. 如果有人想練習的話,可以 fork sandbox 這個專案(純粹練習git,是個亂寫的rails專案)。

Git 教育訓練課程投影片 (2012)

Update(7/21): 我把 p117 rebase 和 merge 操作的示範 錄成影片啦 :)

這是最近準備的 Git 教育訓練課程,加上 live demo (一般操作、merge、reset、rebase等等示範) 時間大概是三個小時。其內容主要基於去年的演講,新增了Git內部原理(p36~51)、分支模式(p143)和更多 Tips(p168)內容等等,總頁數從117成長到198… XD

再次感謝 schacon 公開的 OmniGraffle Diagrams 圖檔讓我可以改來用 :>

如何建立一個沒有 Parent 的獨立 Git branch

首先,你一定會問幹嘛這樣做。我在上回的Git 演講中有提到:在 Git,你可以建立毫不相關的分支,用來保存其他資訊,就像目錄一樣。這 Branch 的用途不會被 Merge 進開發主幹,是完全獨立的。

實務上的一個應用,就是 Github 提供的 Github Pages 功能,只要你在專案下開一個 branch 叫做 gh-pages,那這個 branch 的內容就會變成靜態網頁。我之前的作法是先分支出來,然後再 commit 一次砍掉所有檔案。雖然結果是ok可以用啦,但是總覺得 gh-pages 這 branch 有 parent 就是意思不對勁,它是完全獨立於主幹的,不應該有 parent 資訊。

剛突然想到這個問題可以 google 看看(Github Pages上其實也有寫 XD),還真的有:

方法一 (source)

因為 git branch 預設是從 HEAD 分支,所以可以這樣 Hack HEAD:

echo ref: refs/heads/newbranch > .git/HEAD
git rm -rf .   # 砍掉所有檔案重來
.....   # 加新檔案
git add .
git commit -m 'create new branch'

方法二(source)

Git 1.7.2 之後版本有支援 –orphan 參數:

git checkout --orphan newbranch
git rm -rf .    # 砍掉所有檔案重來
...  # 加新檔案
git add .
git commit -m 'create new branch'

方法三(source)

git symbolic-ref HEAD refs/heads/newbranch
rm .git/index
git clean -fdx
...  # 加新檔案
git add .
git commit -m 'create new branch'

結果就像是這樣,master 跟 newbranch 的節點完全是分開的:

我的 Git 偏好設定

1. 讓 Command Line 指令列顯示目前處在哪一個 Git Branch 分支,最早是在 RGBA 看到這一招,非常方便。另外我最近看到一個點子是顯示從上一次 commit 之後過了多久時間,這可以提醒你是不是該 commit 了 XD

請修改家目錄的 ~/.bash_profile 檔案 (我是用 Bash)。

結果如下,各位可以看到目前處在 master 分支,並且這個專案已經過了 1821 個小時沒有 commit 了.... :p

2. 安裝 Git 的 Bash autocompletion,這樣按 tab 就會有自動完成的效果,它甚至包括 git checkout 時都可以抓到你的 branch 名稱。這裡我用 Homebrew 來安裝 bash-completion,這套件其實包括很多 autocompletion script,你可以去 /usr/local/etc/bash_completion.d 這個目錄找找看。

brew install bash-completion
cp /usr/local/etc/bash_completion.d/git-completion.bash ~/.git-bash-completion.sh

編輯 ~/.bash_profile 加入

[ -f ~/.git-bash-completion.sh ] && . ~/.git-bash-completion.sh

3. 打開 Git 的 color 顏色設定,這樣 Git 指令的輸出結果才會加上顏色,像是 git status 等:

git config --global color.ui true

4. 設定你偏好的文字編輯器和 diff 工具

git config --global core.editor
git config --global merge.tool opendiff

5. 最後,我個人喜歡以下的 alias:

git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.br branch

這樣只要輸入 git st 就是 git status 了。

FYI,以上 git 設定檔的位置在 ~/.gitconfig,你也可以直接修改這個檔案。