1-介紹 clone

1-1-說明

1-1-1-Git Remotes

remote (遠端) repository 並不會很難了解. 藉由現在的雲端運算,可以很輕易地了解到在 git remote 裡面有很多有趣的事情,但它們其實其實就只是你的或者是其它電腦上的 repository 的副本。你可以輕易地透過網路去發送你的 commit 給其它的電腦。

也就是說,remote repository 有很多有趣的地方:

  • 第一,remote 是用來備份的! 本地端的 git 有能力可以回復文件到前一個狀態 (你知道的),但是全部的資訊還是儲存在本地端。如果你在其它的電腦上面有你的 git repository 的副本,則你可以在資料不小心遺失的時候進行救援備份
  • 更重要的是, remote 讓大家一起來 coding!現在你的 project 放在 remote 上面,你的朋友可以很容易地對你的 project 做出貢獻(或者是 pull 你最後的修改)。

使用網站去對 remote repository 做視覺化非常流行(例如 [Github](https://github.com/) 或者是 [Phabricator](http://phabricator.org/)),但這背後最大的功臣是 remote repository,因此我們務必要了解它

1-1-2-我們去建立 remotes 的指令

到目前為止,Learn Git Branching 著重在解釋本地端的工作(branching, merging, rebasing 以及其它指令), 但是我們現在想要學習針對 remote 的指令,我們需要一個指令去建立環境,git clone 就是我們需要的指令

技術上來說, git clone 表示你想要把遠端的 repository 複製一份下來放在本地端( 例如從 github 複製)。 雖然 git clone 實際上是把遠端的 repository 複製下來放在本地端,在 Learn Git Branching 中,我們用的這個指令會有一點不同。雖然他跟真實的指令的意思相反,但是它可以建立起本地端以及遠端的一個連結,現在讓我們看看如何使用它吧。

讓我們慢慢來,並且看看 remote repository 是長什麼樣子(在我們的視覺化圖形中)。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-1-介紹 clone-1

就是那樣!現在我們有了一個放置了我們 project 的 remote repository。除了一些視覺上的改變之外,它們看起來很像,在之後的關卡中你就會看到我們如何分享我們的工作到這些 repository 上面。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-1-介紹 clone-2

1-2-解題

要完成這個關卡,只要打 git clone,其它的學習會在後面的關卡中出現。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-1-介紹 clone-3

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-1-介紹 clone-4


2-remote branch (遠端分支)

2-1-說明

2-1-1-git remote branch

現在你已經知道 git clone 在幹嘛了,讓我們仔細看看到底發生了什麼事。

你首先看到的是在你的本地端(local repository)出現了一個新的 branch 叫作 o/master,這種型態的 branch 叫作 remote branch (遠端分支),因為特殊的需求,因此 remote branch 有特殊的性質。

remote branch 反應了 remote repository 的狀態(因為你最後接觸的是這些 remote repository),最重要的是,在你想要分享你的 commit 給其他人時,你必須知道你現在的 commit 跟 remote repository 有哪些不同,而 remote branch 的狀態就是在告訴你這些資訊。

remote branch 有特別的特性,當你移動到 remote branch 時,你就進入到分離 HEAD 的狀態,git 這樣做的原因是告訴你不能夠直接影響這些 branch。你必須要在其它的 branch 工作,並且分享到 remote (分享之後,你的 remote branch 就會被更新)。

2-1-2-什麼是 o/ ?

你也許會對於 remote branch 前面的 o/ 感到困惑,喔!remote branch 也(需要) 一個命名法則,或者是一般表示 remote branch 的格式。

因此,當你看到一個 branch 叫做 o/master,就表示這個 branch 叫做 master,而且這個 remote 的名稱叫作 o

很多程式設計師實際上會把他們的 remote 命名為 origin,而不是 o,這在 git 是很常見的事情,因為當你使用 git clone 時,git 會自動把你的 remote 命名為 origin

但是很不幸的是 origin 並沒有辦法完全顯示在我們的 UI 上面,所以我們用 o 來簡化它(只要記住當你使用 git 的時候,實際上是命名為 origin)。

有很多事情需要說明,現在讓我們來看看吧!

讓我們移動到(checkout)一個 remote branch 並且看一下會發生什麼事情
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-2-remote branch (遠端分支)-1

就像你看到的, git 讓我們進到 detached HEAD 狀態,同時,當我們加入一個新的 commit 時, o/master 都沒有更新,這是因為只有當 remote 更新的時候, o/master 才會更新。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-2-remote branch (遠端分支)-2

2-2-解題

要完成這一關,先在 master branch 上面做一次 commit,再移動到 o/master 上做一次 commit,這有助於我們了解到 remote branch 的不同,它們只會反應 remote 的狀態。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-2-remote branch (遠端分支)-3

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-2-remote branch (遠端分支)-4


3-git fetch

3-1-說明

3-1-1-git fetch

透過 git remote 其實就是把資料接收或傳送到其它的 repository,只要我們可以將資料傳進及傳出,我們就可以分享任何被 git 所追蹤的 repository 的更新(例如分享工作進度,新的檔案,新的想法,以及情書等等…)。

在這個教學中,我們會學習到如何從 remote repository 來 fetch (抓取)資料,這個指令叫作 git fetch

你將會注意到當我們的 remote repository 更新的時候,相對應的 remote branch 也會反應該更新,這個跟我們之前所提到的 remote branch 的特性是吻合的。

在講到 git fetch 的細節之前,我們要先來看一下例子!在這裡我們有一個新增了兩個 commit 的 remote repository,而且我們的 local repository 並沒有包含這兩個 commit。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-3-git fetch-1

看吧!commit C2 以及 C3 已經被下載到我們的 local repository,而且我們的 remote branch o/master 也更新了。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-3-git fetch-2

3-1-2-fetch 做了什麼

git fetch 只有執行了兩個主要步驟,包含:

  • 下載 remote 有的 commit,但是在我們的 local repository 是沒有該 commit。還有…
  • 更新我們 remote branch 所指向的地方(例如, o/master)

基本上, git fetch 同步了我們的 local repository 以及 remote repository 的最新狀態。

假如你還記得之前的教學的話,我們說過 remote branch 反應了 remote repository 的狀態,原因在於說你最後接觸的是這些 remote repository,而你就是利用 git fetch 來接觸這些 remote repository!現在 remote branch 跟 git fetch 的關係已經很明顯了。

git fetch 通常是透過網路來跟 remote 溝通(透過一個 protocol (協定),例如 http:// 或者是 git://)。

3-1-3-fetch 沒有做的事情

然而, git fetch 並不會影響到在你的 local repository 中的 master branch,他並不會將你的 master branch 更新到最新的狀態。

這個觀念很重要,因為很多程式設計師以為 git fetch 可以讓他們在 local repository 上面的工作跟 remote repository 的工作可以同步。它是會下載同步所需的資料,但是不會更新任何的檔案,我們會在後面的教學中提到如何做到這件事情。:D

因此,你可以把 git fetch 想成是在下載資料。

3-2-解題

要完成這一關,只要透過 git fetch 並且下載全部的 commit 即可!
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-3-git fetch-3

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-3-git fetch-4


4-git pull

4-1-說明

4-1-1-git pull

現在我們已經知道如何利用 git fetch 從 remote 抓取 commit,讓我們來看一下如何將這些 commit 更新到我們的檔案!
只要在你的 local 有 fetch 到新的 commit,便有很多方法可以做到這件事情,你可以把它們視為在其它 branch 上面的一般的 commit,這表示你可以執行像這樣子的指令:

4-1-2-git pull 與 git fetch + marge

事實上,一次下載 (fetch) remote 的更新並且合併(merge) 這些更新在 git 裡面是很常見的事情!這個命令叫作 git pull

讓我們來看循序執行一個 fetch 和一個 merge 的樣子
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-4-git pull-1

看吧! 我們利用 fetch 下載了 C3 並且利用 git merge o/master 來更新資料,現在我們的 master branch 跟 remote 同步了(在這個例子中,remote repository 叫作 origin)。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-4-git pull-2

如果用 git pull 會發生什麼事情?
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-4-git pull-3

一樣!很明顯地, git pull 其實就是 git fetchgit merge 的循序執行的結果,而且 merge 的 branch 就是 fetch 所更新的 branch。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-4-git pull-4

4-2-解題

我們會解釋 git pull 的細節(包括可選擇的參數), 但現在先讓我們在這個關卡試試看!
記住喔,你可以利用循序執行的方式來執行 fetch 以及 merge 來完成這個關卡,但是相對於 git pull,你就得多打一個指令。:P
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-4-git pull-5

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-4-git pull-6


5-模擬團隊合作

5-1-說明

5-1-1-模擬合作

接下來的課程有一個很難的事情,我們需要讓你學會如何 pull remote 上的更新。
這就表示我們必須要 “假裝” remote 被你其中一個同事/ 朋友/ 合作的人在某個特定的 branch 上面送了一些特定的 commit。
為了要做到這件事情,我們要介紹一個自己設計的指令 git fakeTeamwork! 從字面上就可以很容易地看出來它在幹嘛,讓我們來看一個範例…

fakeTeamwork 的預設行為是在送一個 commit 給 master 分支
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-5-模擬團隊合作-1

我就說吧!remote 已經藉由一個新的 commit 而更新了,而且因為我們還沒有用 git fetch,所以表示我們還沒有下載該 commit。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-5-模擬團隊合作-2

你可以在命令的後面指定你要送幾個 commit 或是要送給哪個 branch。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-5-模擬團隊合作-3

我們利用一個指令將三個 commit 送給在 remote 上面的 foo branch。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-5-模擬團隊合作-4

5-2-解題

接下來的關卡會很困難,所以我們在這個關卡中會問你更多問題。
現在先把 remote 下載下來(利用 git clone),假裝送幾個 commit 給 remote 做更新,然後 pull 這些 commit 下來 。這就好像是幾個教學中的指令的總結!
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-5-模擬團隊合作-5

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-5-模擬團隊合作-6


6-git push

6-1-說明

6-1-1-Git Push 將本地 master 分支推向遠端主機分支上

ok,現在我已經從 remote 下載了一些更新,並且把它們 merge 到我的 local 上面的 branch,這聽起來實在太讚了…,但是我要如何分享我所做的更新給其它人呢?
喔,其實上傳並且分享更新跟下載更新並且 merge 是相反的兩件事情,那什麼是 git pull 的相反呢? 那就是 git push
git push 負責上傳你的 commit 到特定 remote 上面並且做出相對應的更新,只要做完了 git push,所有你的朋友都可以從 remote 上面下載你所送出去的 commit。
你可以把 git push 當作是一個”發佈”你的工作進度的指令,還有一些我們即將要講到的細節,但是先讓我們從一些簡單的步驟開始。

這裡我們有了一些 remote 所沒有的 commit。讓我們來上傳它們吧!
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-6-git push-1

我說的沒錯吧!remote 收到了 commit C2,同時在 remote 上的 master branch 也一起更新並且指向 C2,同時我們自己的 o/master 也一併更新了!
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-6-git push-2

6-2-解題

要完成這個關卡,只要上傳兩個新的 commit 給 remote,不要太得意忘形喔!因為這些課程將會愈來愈難!
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-6-git push-3

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-6-git push-4


7-diverged history (分叉歷史)

7-1-說明

7-1-1-Diverged Work (分叉作業)

到目前為止我們已經知道如何 pull 其他人所送的 commit,而且也知道如何 push 我們自己的 commit,感覺很簡單,但是為什麼有人看起來很困惑?
當 repo 的歷史紀錄是 diverge(branch 走向不同) 的狀態時就會很棘手,在討論這個之前,讓我們先來看一個例子…

想像一下你在星期一的時候 clone 了一個 repo,並且開始在設計一個功能,在星期五的時候你準備好要發佈你的新功能,但是非常不幸地,你的同事已經寫了一連串的程式碼並且已經將 commit 發佈到 remote,所以現在你的進度是在一個比較舊的版本的後面(如果與 remote 比較的話啦!)。

7-1-2-遠端主機已有新的 commit 使用 git push 無法將本地 commit 推送

在這種情況底下,使用 git push 會有問題,如果你使用 git push,那麼 git 應該要把 remote 退回到星期一的狀態?它應該要把你所寫好的程式碼一起更新進去,同時不會影響你的同事寫好的程式碼?或者是他應該要因為版本比較舊而完全忽略你的程式碼?

因為在這種情況下會很麻煩(當 git 歷史紀錄被 diverge 了),所以 git 不會允許你 push 你的 commit。在你上傳你的 commit 之前,它實際上會先強迫你先跟 remote 同步。

講太多了啦!讓我們實際看一下這個情況。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-1

看到了沒?因為指令失敗了,所以沒有任何事情發生。 git push 失敗的原因是因為你最近的 commit C3 是在 C1 的後面,但是 remote 那邊是 C2C1 的後面,所以 git 才會拒絕你的 push。

7-1-3-git rebase 解決 Diverged Work (分叉作業) 產生的問題

你要如何解決這種情況?很簡單,你只需要把 C3 接在 remote 最新的版本 C2 的後面就可以了。
有一些方法可以做到,但是最直接的方式是用 rebase,我們來做看看。

在我們 push 之前,先來做 rebase…
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-1

7-1-4-git fetch 取下遠端主機新分支後,在使用 git push 將 commit 合併後推送上遠端機主

看吧!我們利用 git fetch 下載了 remote 上面的 commit,並且 rebase 我們的 commit,使得我們的 commit 可以接在 remote 上面最新的版本的後面,接著透過 git push 就可以上傳更新了。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-2

在 remote 已經率先更新之後,還有沒有其它方法可以上傳我們的 commit?當然有阿!我們這次利用 merge 來做看看!
雖然 git merge 並不會去移動你的 commit(反而會產生一個 merge commit),這是一個告訴 git 你已經下載了 remote 上面的 commit 並且在 local repo 中已經做完 merge,而因為 remote branch 上的最新的 commit 現在已經是 merge commit 的一個 ancestor,這就表示你的 commit 已經包含了在 remote branch 上的所有 commit。
讓我們來看一下這種情況…

7-1-5-git pull –rebase 取下遠端主機新分支後,直接在本地端 master 分支 commit 接進遠端分支新的 commit ,並將本地同步遠端分支

現在假設我們不是用 rebase,而是用 merge…
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-3

看吧!我們藉由 git fetch 把 remote 上的 commit 下載下來,並且 merged 該 commit 到我們目前的 branch(這樣就表示我們產生的 merge commit 有包含了 remote 上的 commit),接著再透過 git push 上傳到 remote。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-4

太棒了! 有沒有其它可以不用打這麼多指令的方法?
當然有阿!你已經知道 git pull 就是表示一個 fetch 跟一個 merge。 有一個指令非常方便,那就是 git pull --rebase,它表示的是一個 fetch 以及一個 rebase。
我們來看如何使用這個簡化後的指令。

首先 --rebase
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-5

跟之前一樣!只是少打了很多指令。
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-6

git pull –rebase VS git pull 的 commit 差異

現在用一般的 pull
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-7

又來了,剛好跟之前的一樣!
Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-8

7-2-解題

fetch,rebase/merge,以及 push 的流程是幾乎一樣的。在之後的教學中我們會看到比這些流程更複雜的版本。但是現在讓我們先牛刀小試一下。
為了要完成這一關,請按照下面的步驟:

  • clone 你的 repo
  • 假裝送一個 commit 給 remote
  • 送一個 commit 給 local repo
  • 透過 rebase 送自己的 commit

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-9

Git 練習遊戲_learngitbranching-6-Push & Pull — Git Remotes!-7-diverged history-10