git remotes

世有因果知因求果發表於2015-05-24

簡單地說,一個remote repository是一個非本地的repo。它可以是在你公司網路上的另外一個git repo,也可以是在internet上,甚至在你本地檔案系統中的一個repo,關鍵點是它和你的my-git-repo project是不同的。

我們已經知道branch在單個的repo中是如何組織你的工作流的,但是要知道branch本身在Git的跨repo分享commit也有重要作為。Remote branches和我們一直本地使用的local branch沒有太多區別,他們僅僅代表著存在於另外一個repo中的一個branch而已。

這意味著我們可以利用我們branch的merging/rebasing技能很方便擴充套件到合作開發上去。下面我們假裝在我們的樣例web專案開發中有多個開發人員一起工作,他們是如何利用Git來協作的。

我們假設Mary,作為我們的graphic designer,她需要加入我們的web專案開發中。

Clone the Repository(MARY)

首先,mary需要一個她自己的repo以便可以開始工作。Git支援分散式的工作流,但是為了簡便我們直接在本地檔案系統中模擬不同repo協作。

cd /path/to/my-git-repo
cd ..
git clone my-git-repo marys-repo
cd marys-repo

上面的命令將在本地不同目錄clone了一個my-git-repo專案repo,命名為marys-repo。

Configure The Repository (Mary)

首先,mary需要配置她clone的repo以便我們知道誰對專案做了貢獻。

git config user.name "Mary"
git config user.email mary.example@rypress.com

注意由於我們沒用--global選項,上面的user.name,email都只對這個repo有效,儲存在.git的config檔案中。

Start Mary’s Day (Mary)

今天,Mary開始開發她的bio page,她首先要在我們repo的clone repo中(對她來說,就是一個local repo)建立一個獨立的分支以便隔離工作。

git checkout -b bio-page

Mary可以方便地在她的repo中建立和check out branches。她的repo是一個完全和我們的repo隔離的環境,她可以不用考慮任何在我們原始my-git-repo中的開發活動,任意地做她想做的開發工作。

就如branches是working directory的一種抽象,the staged snapshot, a commit history, a repository是一個branch的抽象。

Create Mary’s Bio Page ,commit,merge(Mary)

在marys-repo中,更改about/mary.html,隨後commit該change到bio-page branch上,merge該branch到master上,以便準備分享mary自己的工作

git commit -a -m "Add bio page for Mary"
git log -n 1
git checkout master
git merge bio-page

現在我們專案repo和mary的repo是這樣的:

注意:兩個repo都有常見的,本地branches,兩個repo之間還沒有任何互動動作,所以我們沒有看到remote branches.在切換到my-git-repo之前我們看看mary的remote connections:

View Remote Repositories (Mary)

git remote -v

Mary可以使用git remote命令列出她有哪些和其他repo之間的connections。明顯地,她有一個被命名為origin的remote.注意,當你clone一個repo時,git自動增加一個指向original repo的remote,git預設你希望將來對這個cloned repo做協同。

Return to Your Repository (You)

注意這時你的repo中mary的bio page依然為空的。注意這個repo和mary repo是完全隔離的,這一點很重要。當她修改她的bio page時,我們可以在my-git-repo中做任何事情。我們甚至可以在我們的repo中修改她的bio page,而這將來在pull她的變動時就可能產生conflict.

Add Mary as a Remote (You)

在我們可以使用mary的bio page之前,我們需要訪問她的repo.我們通過檢視我們repo中的remote: git remote,你會發現我們還沒有任何remote(注意我們沒有做過任何clone,所以就不會有origin這個remote),我們需要手工建立mary這個remote.

git remote add mary ../marys-repo
git remote -v

現在我們就可以使用mary來引用Mary的repo了(位置在../marys-repo) git remote add命令用於bookmark另外一個git repo以方便訪問,我們兩個repo之間的關聯如下圖:

既然我們的remote repository已經建立好了,我們下面將花時間探討remote branches.

Fetch Mary’s Branches (You)

正如前面提過,我們可以使用remote branches來訪問來自另外一個repo的snapshots。我們先看看我們當前的remote branches,使用:git branch -r命令,同樣地,我們沒有任何remote branch,為了獲得這些remote branch list,我們需要fetch the branches from Mary's repository:

git fetch mary
git branch -r
將會輸出:
mary/bio-page
mary/master

remote branch總是以<remote-name>/<branch-name>的形式列出,這樣永遠不會和local branches混淆。上面列出的remote branch反映了Mary的repo在執行fetch命令時的狀態。但是注意如果Mary繼續在她的repo中開發,那麼我們fetch過來的狀態並不會自動體現後面Mary的改動!這也就是說我們的remote branches本身並沒有直接和Mary的repo連結起來,這意味著我們如果需要最新的Mary的update,必須再次執行fetch命令。

上面的途中顯示了我們的repo的狀態,我們已經可以訪問Mary的snapshots(以方框指示)和她的branches,雖然我們沒有一個實時的connection到Mary的repo。

Check Out a Remote Branch

我們可以在自己的repo中執行 git checkout mary/master來checkout一個remote branch以便檢查review Mary's changes.

這個動作將會使我們的工作環境進入detached HEAD state,就像我們本地開發時checkout一個歷史commit時的狀態一樣。這本身並不奇怪,因為我們的remote branches是Mary的branch的一個copy. checking out一個remote branch將會把我們的HEAD脫離local branch的tip,以下圖來示例

紅色方框為HEAD指向remote branch(mary的commit),local master則指向前一個commit。

如果我們不在local branch時,我們不能繼續開發commit(我們可以檢查程式碼)。為了能在mary/master上開發??,我們需要將該branch merge到我們的local master或者再建立另外一個分支。

Find Mary’s Changes

我們可以使用本地repo操作一樣的log-filtering語法來檢視Mary's changes.

git log master..mary/master --stat

這將不會有任何輸出,因為自從Mary clone了repo後,我們並未做改變,換句話說,我們的歷史還沒有發散,我們只是落後一個commit。

Merge Mary’s Changes

我們來apprvoe Mary的變更並且整合到我們的master branch上去。

git checkout master
git merge mary/master

雖然mary/master是一個remote branch,這仍然會產生一個fast-forward merge,因為從我們的local master到mary/master的tip之間是linear path。在merge之後,Mary remote branch上的snapshots稱為我們local master branch的一部分,比較merge前後的情況示意如下:

注意,雖然我們可以訪問到她的bio-page分支,但是我們僅僅和Mary的master branch做了互動。

Push a Dummy Branch

為了對我們的git fetch命令做一個補充,我們看看pushing命令。Fetching和pushing幾乎是相反的,fetching imports branches,而pushing exports branches to another repo.比如:

git branch dummy
git push mary dummy

上面的命令建立一個dummy branch,隨後傳送到Mary的repo中,我們切換到Mary的repo看看我們幹過什麼:

cd ../marys-repo
git branch

你會發現一個新的dummy branch在她的local branch listing.我說過git fetch和git push幾乎是相反的是因為pushing建立一個local branch,而fetching卻imports commits into remote branches!!

 現在我們站在Mary的角度上來看,當她在她自己的repo中開發時卻突然發現莫名其妙多了一個dummy branch在她的local branch list中,這是不是很唐突混亂?明顯的,push branch到其他人的repo中將會造成workflow混亂。因此,作為一個general rule,你永遠不要push你的本地變更到別的開發人員的repo中!!但是,我們到底應該如何使用push呢?

在下面的文章中,我將描述push操作對於維護public repo是非常有必要的!

Push a New Tag

git push的一個重要特性是git push不會自動push和一個branch關聯的tags。我們做下面的實驗:

git tag -a v2.0 -m "An even stabler version of the website"

git push mary master
git push mary v2.0

通過上面的命令,你將可以在Mary的repo中看到一個v2.0 tag。注意:非常容易你可能雖然建立了一個tag,卻忘記push!如果你的專案缺少一兩個tag,那麼很大的可能是你忘記push到remote repo了!

有一點需要謹記: remotes are for people, branches are for topic! 不要為了你的每一個開發人員建立一個獨立的分支,而應該給他們獨立的repo並且使用git remote add來引用。Branches總是用來做專案開發的,不是用來管理使用者的。

相關文章