對於 Git 來說,團隊合作和個人獨立工作最大的不 同在於,你會提交代碼,別人也會提交;你會 push
,別人也會 push
,因此除了把代碼上傳,每 個人還需要把別人最新的代碼下載到自己的電腦。 而且,這些工作都是並行進行的。
這節的內容,就是簡單闡述一下團隊合作中 Git 的 基本工作模型。
1.把別人的新提交拿到本地
先說一種簡單的情況:你的同事剛把他寫的幾個提 交 push
到了中央倉庫,你怎麼把它們取下來。
2.假裝同事創建本地倉庫
除非你能要求同事陪你練習 Git,否則,你只能一 人分飾兩角:時不時地模擬一下同事的操作,來達 到獨自一人做多人項目練習的效果。而爲了模擬同 事的操作,你需要在你的 /git-practice
目錄旁 再 clone
一次中央倉庫到本地,來當做你的模擬同 事的本地倉庫:
git clone https://github.com/rengwuxian/git-pract
注意!執行這條之前別忘了先
cd ..
切換到父 目錄,不然你會把新的本地倉庫創建在原先的 本地倉庫的工作目錄內。
爲了目錄名稱不衝突,這次的 clone
需要加一個 額外參數(上面那行代碼最後的 git-practiceanother
)來手動指定本地倉庫的根目錄名稱。在這條指令執行完成後,你就有了兩個內容相同的本 地倉庫:
現在,你就可以假裝 /git-practice
這個目錄是 你的電腦上的工作目錄,而那個新建的 /gitpractice-another
是你同事的電腦上的工作目 錄。
如果你對這樣的操作有擔心,大可不必,這種操作 不會有任何問題。因爲 Git 的管理是目錄級別,而 不是設備級別的。也就是說, /git-practice
目
錄內的 .git
只管理 /git-practice
裏的內 容, /git-practice-another
目錄內的 .git
也 只管理 /git-practice-another
裏的內容,它們 之間互不知曉,也互不影響。
3.幫同事提交代碼並 push 到中央倉庫
要在你的電腦上把同事的代碼取下來,首先 GitHub 上要有你同事的新代碼,也就是說,你首 先要把同事的代碼 push
到中央倉庫去。所以第一 步,切換到同事的目錄:
cd git-practice-another
現在,到了同事的工作目錄,你的身份就是你的同事了。幫他寫點代碼吧!
幫他提交:
git add shopping\ list-colleague.txt
git commit
提交完成以後, push
到 GitHub:
git push
4.把同事 push 的新代碼取下來
GitHub 上有了同事的新代碼以後,你就可以變回 自己,把「他」剛 push
上去的代碼取到你的倉庫 了。首先,切回 /git-practice
目錄:
cd ../git-practice
然後,把同事的代碼取下來吧!從遠程倉庫更新內 容,用的是一個新的指令: pull
。
git pull
這時候再看看你的本地目錄,可以看到,同事提交 的 shopping list-colleagure.txt
已經同步到本 地了。也就是說,現在你的本地倉庫和同事的又一 致了。
5.多人合作的基本工作模型
這就完成了一次簡單的合作流程:
- 同事
commit
代碼到他的本地,並push
到 GitHub 中央倉庫 - 你把 GitHub 的新提交通過
pull
指令來取 到你的本地
通過這個流程,你和同事就可以簡單地合作了:你 寫了代碼, commit
, push
到 GitHub,然後他pull
到他的本地;他再寫代碼, commit
, push 到 GitHub,然後你再 pull
到你的本 地。你來我往,配合得不亦樂乎。
但是,這種合作有一個嚴重的問題:同一時間內, 只能有一個人在工作。你和同事其中一個人寫代碼 的時候,另一個人不能做事,必須等着他把工作做 完,代碼 push
到 GitHub 以後,自己才能把 push
上去的代碼 pull
到自己的本地。而如果 同時做事,就會發生衝突:當一個人先於另一個人 push
代碼(這種情況必然會發生),那麼後 push
的這個人就會由於中央倉庫上含有本地沒有 的提交而導致 push
失敗。
爲什麼會失敗?
因爲 Git 的push
其實是用本地倉庫的commits
記錄去覆蓋遠端倉庫的commits
記 錄(注:這是簡化概念後的說法,push
的實 質和這個說法略有不同),而如果在遠端倉庫 含有本地沒有的commits
的時候,push
(如果成功)將會導致遠端的commits
被擦 掉。這種結果當然是不可行的,因此 Git 會在push
的時候進行檢查,如果出現這樣的情況,push
就會失敗。
怎麼辦?
6.push 發生衝突
在現實的團隊開發中,全隊是同時並行開發的,所 以必然會出現當一人 push
代碼時,中央倉庫已 經被其他同事先一步 push
了的情況。爲了不讓 文段顯得太過混亂,這裏我就不帶着你一步步模擬 這個過程了。如果你希望模擬的話,這裏是步驟:
- 切到
git-practice-another
去,假扮成你的 同事做一個commit
,然後push
到 GitHub - 切回
git-practice
變回你自己,做一個不 一樣的commit
。
這個時候,遠端中央倉庫已經有了別人push
的commit
,現在你如果push
的話:
git push
從上圖中的提示語可以看出來(好吧這麼長的提示 語有人表示不想看) 由於 GitHub 的遠端倉庫上push 發生衝突語有人表示不想看),由於 GitHub 的遠端倉庫上 含有本地倉庫沒有的內容,所以這次 push
被拒 絕了。這種衝突的解決方式其實很簡單:先用 pull
把遠端倉庫上的新內容取回到本地和本地合 並,然後再把合併後的本地倉庫向遠端倉庫推送。
git pull
在上面的文段裏,我已經舉過一次 git pull
的 例子,但這次的 git pull
操作並沒有像之前的 那樣直接結束,而是進入了上圖這樣的一個輸入提 交信息的界面。這是因爲當 pull
操作發現不僅 遠端倉庫包含本地沒有的 commit s
,而且本地倉 庫也包含遠端沒有的 commit s
時,它就會把遠端 和本地的獨有 commit s
進行合併,自動生成一個 新的 commit
,而上圖的這個界面,就是這個自 動生成的 commit
的提交信息界面。另外,和手 動的 commit
不同,這種 commit
會自動填入一 個默認的提交信息,簡單說明了這條 commit
的 來由。你可以直接退出界面來使用這個自動填寫的 提交信息,也可以修改它來填入自己提交信息。
這種「把不同的內容進行合併,生成新的提交」的操作,叫做合併,它所 對應的 Git 指令是
merge
。事實上,git pull
這個指令的內部實現就是把遠程倉庫使 用git fetch
取下來以後再進行merg
e 操 作的。關於更多merge
的介紹,我會在後面 說,這節先不講了。
在退出提交信息的界面後,這次 pull
就完成 了:遠端倉庫被取到了本地,並和本地倉庫進行了 合併。在這個時候,就可以再 push
一次了。由 於現在本地倉庫已經包含了所有遠端倉庫的 commits
,所以這次 push
不會再失敗:
git push
這樣,就把 push
時本地倉庫和遠端倉庫內容衝 突的問題解決了。
7.小結:多人合作的基本工作模型 2.0
這樣,就把剛纔的那個「多人合作的基本工作模 型」進行了改良:
- 寫完所有的
commit
後,不用考慮中央倉庫 是否有新的提交,直接push
就好 - 如果
push
失敗,就用pull
把本地倉庫 的提交和中央倉庫的提交進行合併,然後再push
一次
到此爲止,這個工作模型已經是一個最簡單的可用 的工作模型了。一個小團隊如果對版本管理沒有什 麼要求的話,這個工作模型已經可以讓團隊用來合 作開發了。