Git 并不像 Subversion 那樣有個(gè)中心服務(wù)器。 目前為止所有的命令都是本地執(zhí)行的,更新的知識(shí)本地的數(shù)據(jù)庫(kù)。 要通過(guò) Git 與其他開發(fā)者合作,你需要將數(shù)據(jù)放到一臺(tái)其他開發(fā)者能夠連接的服務(wù)器上。 Git 實(shí)現(xiàn)此流程的方式是將你的數(shù)據(jù)與另一個(gè)倉(cāng)庫(kù)同步。在服務(wù)器與客戶端之間并沒(méi)有實(shí)質(zhì)的區(qū)別 —— Git 倉(cāng)庫(kù)就是 Git 倉(cāng)庫(kù),你可以很容易地在兩者之間同步。
一旦你有了個(gè) Git 倉(cāng)庫(kù),不管它是在你自己的服務(wù)器上,或者是由 GitHub 之類的地方提供, 你都可以告訴 Git 推送你擁有的遠(yuǎn)端倉(cāng)庫(kù)還沒(méi)有的數(shù)據(jù),或者叫 Git 從別的倉(cāng)庫(kù)把差別取過(guò)來(lái)。
聯(lián)網(wǎng)的時(shí)候你可以隨時(shí)做這個(gè),它并不需要對(duì)應(yīng)一個(gè) commit
或者別的什么。
一般你會(huì)本地提交幾次,然后從你的項(xiàng)目克隆自的線上的共享倉(cāng)庫(kù)提取數(shù)據(jù)以保持最新,將新完成的合并到你完成的工作中去,然后推送你的改動(dòng)會(huì)服務(wù)器。
簡(jiǎn)而言之 使用 git fetch
更新你的項(xiàng)目,使用 git push
分享你的改動(dòng)。
你可以用 git remote
管理你的遠(yuǎn)程倉(cāng)庫(kù)。
不像中心化的版本控制系統(tǒng)(客戶端與服務(wù)端很不一樣),Git 倉(cāng)庫(kù)基本上都是一致的,并且并可以同步他們。 這使得擁有多個(gè)遠(yuǎn)端倉(cāng)庫(kù)變得容易 —— 你可以擁有一些只讀的倉(cāng)庫(kù),另外的一些也可寫的倉(cāng)庫(kù)。
當(dāng)你需要與遠(yuǎn)端倉(cāng)庫(kù)同步的時(shí)候,不需要使用它詳細(xì)的鏈接。Git 儲(chǔ)存了你感興趣的遠(yuǎn)端倉(cāng)庫(kù)的鏈接的別名或者昵稱。
你可以使用 git remote
命令管理這個(gè)遠(yuǎn)端倉(cāng)庫(kù)列表。
如果沒(méi)有任何參數(shù),Git 會(huì)列出它存儲(chǔ)的遠(yuǎn)端倉(cāng)庫(kù)別名了事。默認(rèn)情況下,如果你的項(xiàng)目是克隆的(與本地創(chuàng)建一個(gè)新的相反),
Git 會(huì)自動(dòng)將你的項(xiàng)目克隆自的倉(cāng)庫(kù)添加到列表中,并取名“origin”。
如果你執(zhí)行時(shí)加上 -v
參數(shù),你還可以看到每個(gè)別名的實(shí)際鏈接地址。
$ git remote origin $ git remote -v origin git@github.com:github/git-reference.git (fetch) origin git@github.com:github/git-reference.git (push)
在此你看到了該鏈接兩次,是因?yàn)?Git 允許你為每個(gè)遠(yuǎn)端倉(cāng)庫(kù)添加不同的推送與獲取的鏈接,以備你讀寫時(shí)希望使用不同的協(xié)議。
如果你希望分享一個(gè)本地創(chuàng)建的倉(cāng)庫(kù),或者你想要獲取別人的倉(cāng)庫(kù)中的貢獻(xiàn) ——
如果你想要以任何方式與一個(gè)新倉(cāng)庫(kù)溝通,最簡(jiǎn)單的方式通常就是把它添加為一個(gè)遠(yuǎn)端倉(cāng)庫(kù)。
執(zhí)行 git remote add [alias] [url]
就可以。
此命令將 [url]
以 [alias]
的別名添加為本地的遠(yuǎn)端倉(cāng)庫(kù)。
例如,假設(shè)我們想要與整個(gè)世界分享我們的 Hello World 程序。 我們可以在一臺(tái)服務(wù)器上創(chuàng)建一個(gè)新倉(cāng)庫(kù)(我以 GitHub 為例子)。 它應(yīng)該會(huì)給你一個(gè)鏈接,在這里就是“git@github.com:schacon/hw.git”。 要把它添加到我們的項(xiàng)目以便我們推送以及獲取更新,我們可以這樣:
$ git remote $ git remote add github git@github.com:schacon/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push)
像分支的命名一樣,遠(yuǎn)端倉(cāng)庫(kù)的別名是強(qiáng)制的 —— 就像“master”,沒(méi)有特別意義,但它廣為使用,
因?yàn)?git init
默認(rèn)用它;“origin”經(jīng)常被用作遠(yuǎn)端倉(cāng)庫(kù)別名,就因?yàn)?git clone
默認(rèn)用它作為克隆自的鏈接的別名。此例中,我決定給我的遠(yuǎn)端倉(cāng)庫(kù)取名“github”,但我叫它隨便什么都可以。
Git addeth and Git taketh away. 如果你需要?jiǎng)h除一個(gè)遠(yuǎn)端 —— 不再需要它了、項(xiàng)目已經(jīng)沒(méi)了,等等 —— 你可以使用 git remote rm [alias]
把它刪掉。
$ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) $ git remote add origin git://github.com/pjhyett/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/pjhyett/hw.git (fetch) origin git://github.com/pjhyett/hw.git (push) $ git remote rm origin $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push)
簡(jiǎn)而言之 你可以用 git remote
列出你的遠(yuǎn)端倉(cāng)庫(kù)和那些倉(cāng)庫(kù)的鏈接。
你可以使用 git remote add
添加新的遠(yuǎn)端倉(cāng)庫(kù),用 git remote rm
刪掉已存在的那些。
Git 有兩個(gè)命令用來(lái)從某一遠(yuǎn)端倉(cāng)庫(kù)更新。
git fetch
會(huì)使你與另一倉(cāng)庫(kù)同步,提取你本地所沒(méi)有的數(shù)據(jù),為你在同步時(shí)的該遠(yuǎn)端的每一分支提供書簽。
這些分支被叫做“遠(yuǎn)端分支”,除了 Git 不允許你檢出(切換到該分支)之外,跟本地分支沒(méi)區(qū)別 ——
你可以將它們合并到當(dāng)前分支,與其他分支作比較差異,查看那些分支的歷史日志,等等。同步之后你就可以在本地操作這些。
第二個(gè)會(huì)從遠(yuǎn)端服務(wù)器提取新數(shù)據(jù)的命令是 git pull
。
基本上,該命令就是在 git fetch
之后緊接著 git merge
遠(yuǎn)端分支到你所在的任意分支。
我個(gè)人不太喜歡這命令 —— 我更喜歡 fetch
和 merge
分開來(lái)做。少點(diǎn)魔法,少點(diǎn)問(wèn)題。
不過(guò),如果你喜歡這主意,你可以看一下 git pull
的
[官方文檔]()。
假設(shè)你配置好了一個(gè)遠(yuǎn)端,并且你想要提取更新,你可以首先執(zhí)行 git fetch [alias]
告訴 Git 去獲取它有你沒(méi)有的數(shù)據(jù),然后你可以執(zhí)行 git merge [alias]/[branch]
以將服務(wù)器上的任何更新(假設(shè)有人這時(shí)候推送到服務(wù)器了)合并到你的當(dāng)前分支。
那么,如果我是與兩三個(gè)其他人合作 Hello World 項(xiàng)目,并且想要將我最近連接之后的所有改動(dòng)拿過(guò)來(lái),我可以這么做:
$ git fetch github remote: Counting objects: 4006, done. remote: Compressing objects: 100% (1322/1322), done. remote: Total 2783 (delta 1526), reused 2587 (delta 1387) Receiving objects: 100% (2783/2783), 1.23 MiB | 10 KiB/s, done. Resolving deltas: 100% (1526/1526), completed with 387 local objects. From github.com:schacon/hw 8e29b09..c7c5a10 master -> github/master 0709fdc..d4ccf73 c-langs -> github/c-langs 6684f82..ae06d2b java -> github/java * [new branch] ada -> github/ada * [new branch] lisp -> github/lisp
可以看到自從上一次與遠(yuǎn)端倉(cāng)庫(kù)同步以后,又新贈(zèng)或更新了五個(gè)分支。 “ada”與“l(fā)isp”分支是新的,而“master”、“clang”與“java”分支則被更新了。 在此例中,我的團(tuán)隊(duì)在合并入主分支之前,將提議的更新推送到遠(yuǎn)端分支以審核。
你可以看到 Git 做的映射。遠(yuǎn)端倉(cāng)庫(kù)的主分支成為了本地的一個(gè)叫做“github/master”的分支。
這樣我就可以執(zhí)行 git merge github/master
將遠(yuǎn)端的主分支和并入我的本地主分支。
或者,我可以 git log github/master ^master
看看該分支上的新提交。
如果你的遠(yuǎn)端倉(cāng)庫(kù)叫做“origin”,那遠(yuǎn)端主分支就會(huì)叫做 origin/master
。幾乎所有能在本地分支上執(zhí)行的命令都可以在遠(yuǎn)端分支上用。
如果你有多個(gè)遠(yuǎn)端倉(cāng)庫(kù),你可以執(zhí)行 git fetch [alias]
提取特定的遠(yuǎn)端倉(cāng)庫(kù),
或者執(zhí)行 git fetch --all
告訴 Git 同步所有的遠(yuǎn)端倉(cāng)庫(kù)。
簡(jiǎn)而言之 執(zhí)行 git fetch [alias]
來(lái)將你的倉(cāng)庫(kù)與遠(yuǎn)端倉(cāng)庫(kù)同步,提取所有它獨(dú)有的數(shù)據(jù)到本地分支以合并或者怎樣。
想要與他人分享你牛鼻的提交,你需要將改動(dòng)推送到遠(yuǎn)端倉(cāng)庫(kù)。
執(zhí)行 git push [alias] [branch]
,就會(huì)將你的 [branch] 分支推送成為 [alias] 遠(yuǎn)端上的 [branch] 分支。
讓我們?cè)囋囃扑臀覀兊闹鞣种У较惹疤砑拥摹癵ithub”遠(yuǎn)端倉(cāng)庫(kù)上去。
$ git push github master Counting objects: 25, done. Delta compression using up to 2 threads. Compressing objects: 100% (25/25), done. Writing objects: 100% (25/25), 2.43 KiB, done. Total 25 (delta 4), reused 0 (delta 0) To git@github.com:schacon/hw.git * [new branch] master -> master
挺簡(jiǎn)單?,F(xiàn)在如果有人從該倉(cāng)庫(kù)克隆,他會(huì)得到我提交的完完全全的一份歷史記錄了。
如果有個(gè)像之前創(chuàng)建的“erlang”分支那樣的主題分支,想只分享這個(gè),該怎么辦呢?你可以相應(yīng)的只推送該分支。
$ git push github erlang Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 652 bytes, done. Total 6 (delta 1), reused 0 (delta 0) To git@github.com:schacon/hw.git * [new branch] erlang -> erlang
現(xiàn)在當(dāng)人們從該倉(cāng)庫(kù)克隆時(shí),他們就會(huì)得到一個(gè)“erlang”分支以查閱、合并。 用這種方式,你可以推送任何分支到任何你有寫權(quán)限的倉(cāng)庫(kù)。 如果你的分支已經(jīng)在該倉(cāng)庫(kù)中了,它會(huì)試著去更新,如果它不再,Git 會(huì)把它加上。
最后一個(gè)當(dāng)你推送到遠(yuǎn)端分支時(shí)會(huì)碰到的主要問(wèn)題是,其他人在此期間也推送了的情況。
如果你和另一個(gè)開發(fā)者同時(shí)克隆了,又都有提交,那么當(dāng)她推送后你也想推送時(shí),默認(rèn)情況下 Git 不會(huì)讓你覆蓋她的改動(dòng)。
相反的,它會(huì)在你試圖推送的分支上執(zhí)行 git log
,確定它能夠在你的推送分支的歷史記錄中看到服務(wù)器分支的當(dāng)前進(jìn)度。
如果它在在你的歷史記錄中看不到,它就會(huì)下結(jié)論說(shuō)你過(guò)時(shí)了,并打回你的推送。
你需要正式提取、合并,然后再次推送 —— 以確定你把她的改動(dòng)也考慮在內(nèi)了。
當(dāng)你試圖推送到某個(gè)以被更新的遠(yuǎn)端分支時(shí),會(huì)出現(xiàn)下面這種情況:
$ git push github master To git@github.com:schacon/hw.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'git@github.com:schacon/hw.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
你可以修正這個(gè)問(wèn)題。執(zhí)行 git fetch github; git merge github/master
,然后再推送
簡(jiǎn)而言之 執(zhí)行 git push [alias] [branch]
將你的本地改動(dòng)推送到遠(yuǎn)端倉(cāng)庫(kù)。
如果可以的話,它會(huì)依據(jù)你的 [branch] 的樣子,推送到遠(yuǎn)端的 [branch] 去。
如果在你上次提取、合并之后,另有人推送了,Git 服務(wù)器會(huì)拒絕你的推送,知道你是最新的為止。