Fork 项目來進行自己的修改,可以讓你輕鬆地整合自己的貢獻,但是如果你沒有將這些修改發回上游 - 也就是發回父親倉庫 -- 你就有可能失去對他們的跟踪,這可能會導致你的版本庫中出現不同的線路。為了確保所有貢獻者都從同有個地方獲取信息,你需要了解一些關於 git forking 與 git upstream 如何交互的原理。在這篇博客中,我將向你介紹基礎知識,疑難雜症,甚至給你留下一個很酷的小技巧,讓你走在曲線的前面。
Git Upstream: 保持最新做出貢獻#
讓我先詳細介紹一下與上游倉庫交互的常見設置和最基本的工作流程。
在一個標準的設置中,你通常有一個 origin 和一個 upstream remote - 後者是項目的守門人,或者你希望貢獻的真實來源。
首先,缺人你已經為上游倉庫設置了一個 remote, 並希望也設置了一個 origin:
$ git remote -v
origin [email protected]:my-user/some-project.git (fetch)
origin [email protected]:my-user/some-project.git (push)
如果你沒有上游,你可以很容易地用遠程命令添加它。
git remote add upstream [email protected]:some-gatekeeper-maintainer/some-project.git
檢查 remote 添加成功:
git remote -v
origin [email protected]:my-user/some-project.git (fetch)
origin [email protected]:my-user/some-project.git (push)
upstream [email protected]:some-gatekeeper-maintainer/some-project.git (fetch)
upstream [email protected]:some-gatekeeper-maintainer/some-project.git (push)
現在你可以用 fetch 收集上游倉庫的最新變化。每次想要獲得更新時,都要重複這個動作。
(如果項目的標籤害沒有合併到 master, 你還應該這樣做:git fetch upstream --tags
)
git fetch upstream
一般來說,你要保持本地分支作為上游主分支的近似鏡像,並在特性分支中執行任何工作,因為它們以後可能會成為 pull request .
在這一點,使用 merge 還是 rebase 並不重要,因為結果通常是一樣的。讓我們使用 merge:
git checkout master
git merge upstream/master
當你想合上游的維護者分享一些工作時,你可以從 master 分支中創建一個特性分支,當你滿意時,把它推送到你的遠程倉庫。
你也可以用 rebase 來代替,然後合併以確保上游有一套乾淨的提交(最好是一個)來評估。
git checkout -b feature-x
#some work and some commits happen
#some time passes git fetch upstream
git rebase upstream/master
如果你需要把幾個提交壓成一個,你可以在這時使用厲害的 rebase 互動。
Publish with git fork#
經過以上步驟後,通過簡單的 push, 在遠程 fork 中發布你的作品。
git push origin feature-x
如果你在發布遠程分支 feature-x 後,因為上游維護者的一些反饋而不得不更新它,就會出現一個小問題,你有幾個選擇:
創建一個新的分支,包含你和上游的更新將上游的更新合併到你的本地分支,並記錄一個合併提交,這將使上游版本庫混亂在上游跟新的基礎上重新建立本地分支,然後強制推動到遠程分支
git push -f origin feature-x
我個人更傾向於盡量保持歷史記錄的乾淨,選擇方案三,但不同的團隊有不同的工作流程。注意:只有在使用你自己的 fork 時,你才能這樣做,重寫共享庫和分支的歷史是絕對不應該做的。重寫共享庫和分支的歷史是你永遠不應該改做的事情。
Tip of the day: Ahead/Behind number in the promt#
fetch 之後,git status 會顯示你比同步的遠程分支提前或落後多少次提交。如果你能在你忠實的命令行提示符下看到這些信息,豈不是更好?我也是這麼想的,所以我開始用我的 bash chopsticks 並製作了它。
下面是你配置好後,它在你的提示符上的樣子:
nick-macbook-air:~/dev/projects/stash[1|94]$
這就是你需要添加到你的.bashrc 或等價物中的東西,只是一個函數:
function ahead_behind {
curr_branch=$(git rev-parse --abbrev-ref HEAD);
curr_remote=$(git config branch.$curr_branch.remote);
curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);
git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';
}
你可以用這個新函數 ahead_behind 來豐富你的 bash 提示,已達到想要的效果。我把著色的工作留給讀者去做。
Sampl prompt:
export PS1="\h:\w[\$(ahead_behind)]$"
內部結構#
對於那些喜歡細節和解釋的人來說,這是如何工作的。
我們得到當前 HEAD 的符號名稱,及當前分支。
curr_branch=$(git rev-parse --abbrev-ref HEAD);
我們得到當前分支所指向的遠程:
curr_remote=$(git config branch.$curr_branch.remote);
我們得到了這個遠程分支應該被合併到的分支上(通過一個廉價的 Unix 技巧來丟棄包括最後一個斜線 [/] 在內的所有內容)。
curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);
現在我們有了我們需要的東西來收集我們領先和落後的提交次數
git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';
我們使用古老的 Unix tr 將 TAB 轉換為分隔符 |.
Getting started with git upstream#
這就是 git upstream 的基本演練,-- 如何設置 git upstream, 創建新分支,收集修改,用 git fork 發布,還有一個貼心的提示,這就是你的遠程分支領先 / 落後於你多少次提交。