看了Git官網的這一段介紹更加迷惑了
用 Git 的話,就算你在飛機或火車上,都可以非常愉快地頻繁提交更新, 等到了有網路的時候再上傳到遠端倉庫。同樣,在回家的路上,不用連接VPN 你也可以繼續工作。換作其他版本控制系統,這麼做幾乎不可能,抑或非常麻煩。
連結地址》》》
有商城專案正在上線,團隊中有A、B、C三個程式設計師,使用git來進行版本控制。總共開發天數是10天,從第1天到第5天,A、B、C都是一起開發,一起提交,一個版本。但是到了第6天開始,C就不提交到遠端了,一直都是在自己修改製作。整個專案很多文件,C會把專案中的文件GlobalFunction.php改動很多次,修改了許多函數,也加入了許多自己的函數。隨著開發的持續,C還會不斷的陸續修改GlobalFunction.php檔案。而一直都提交到遠端的A和B,也需要經常修改GlobalFunction.php文件,但是A和B會保持同步到Git遠端。
到了最後第10天,C可以連網了,然後C提交到遠端。可C電腦裡面的GlobalFunction.php 跟 Git遠端上的 GlobalFunction.php 已經完全不一樣了,而且遠端的這個檔案還經歷過了二十多個版本。而C自己電腦裡面的也經歷過了幾十個不同版本。
根據git介紹中的那一段的描述,這樣的情況下,Git是可以自動合併?如何自動合併?
C已經在GlobalFunction.php中加入了很多函數且期間也經歷過了很多次的版本。
看到很多介紹Git的文章重點都放在了不需要push到遠程,不需要聯網就可以使用,真的覺得實在是難以理解。我在sf問也有很多人也在說不用push,/q/1010000000605934#c-1020000000606050-1050000000609657,更讓人迷惑。不用push,假如1年或10年了從來都不push到遠程,那麼還算是協同工作了嗎,git會那麼神奇10年之後再聯網push過去還可以自動合併
怪我咯2017-04-26 09:03:40
把一個場景推到極端是不好的論證方法。
首先,提交的目的是什麼,如果只是為了同步程式碼給別人,那git真沒什麼亮點,甚至離線提交還會讓人誤解,算是個缺點。
我個人認為提交的目的,一是暫存程式碼(版本管理,方便後續回滾神馬的),二是作為代碼的階段性節點(這個功能做完了,提交一下),三才是向伺服器同步,與他人分享。
Git的離線提交可以很好地完成以上目的一二兩點。而且因為是離線提交的,你可以在與伺服器同步之前很方便地修改提交記錄,這有什麼用呢?例如我寫了一個方法,它可以工作,但是我想重構一下,又不確定重構後能否正常工作,於是先提交一個版本,萬一重構壞了還可以回滾回來。等我重構完了之後,再提交一次。此時就有了同一功能的兩次提交,而你可能只希望別人看到一個提交“XX功能完成”,於是你可以很方便地將這兩次提交合併起來。最後合併之後,程式碼版本庫是非常乾淨的,所有的提交記錄都是因為真的要提交而提交,這些提交是可以直接寫入release note的,而不會出現“測試XXX”,“嘗試XXX”之類的提交。基於同樣的結果,你還可以將一次寫的程式碼分多次提交,例如我寫了三個功能,我可以選擇提交三次,最後再同步到伺服器,而在SVN中,你(在沒網時寫的程式碼)只能一次提交上去,然後寫“完成了A,B,C三個功能”,這樣其實不便於別人查看你的程式碼,也不利於對A,B,C分別進行回滾。
所以git的離線提交帶來的是彈性。並不是說有了離線提交,我就可以不需要伺服器,空氣會自動幫我完成協同工作。這是我開頭說的「把一個場景推到極端是不好的論證方法」的意思。 git相比svn,協同的方式會多一些,也靈活一些,但並不能避開協同的問題,你仍然需要協同和合併代碼。
在合併程式碼的過程中,和SVN一樣,也會遇到衝突的情況,所以條件允許的情況下及時與伺服器同步程式碼仍然是個好習慣。但是,由於git是基於內容追蹤版本,svn是基本檔案追蹤版本,導致git的合併比svn可靠得多,也就是同樣情況下,git碰到衝突的可能性比svn小很多。
最後,由於git的離線提交特性,再加上git的分支特別好用,還可以實現本地多分支管理功能,也就是你在做一個功能時可以在本地開一個分支,然後在本地提交,做完了再合併進去推到伺服器,對其他人而言,這個分支是不存在的,但是對自己而言,卻可以隨時切換工作狀態(寫新功能還是切回去改bug)。 svn也可以切分支,但它的分支是無法只存在本地的,如果採用分支協作的方式,會導致分支非常多,而有一些根本是其它人不需要關心的。而更麻煩的是,如果你新功能寫到一半,要切回去改bug,此時只能將未完成的程式碼提交到svn伺服器,我個人認為(將未完成的程式碼提交到版本庫伺服器)是一種非常不好的行為,而用git,因為分支是離線的,我提交完,切回去改bug,再切回來就是,等功能做完後合併一下提交記錄就一切圓滿了。
巴扎黑2017-04-26 09:03:40
如果雙方改的是不同的文件,或是同一個文件的不同部分,那麼 git 是可以自動合併的。
如果修改的是同一個檔案同一個部分,git 就沒辦法自動合併了,這種情況被稱為「衝突」,git 會列出雙方的最新版本,然後由進行合併的人(例子裡是C ), 手動進行編輯。
在實際開發中,如果兩個人只是往這個文件裡加函數,是完全可以自動合併的,衝突主要是出現了修改了同一行的情況。
對補充的回答:
很多介紹強調 git 可以離線 commit, 意在於和 svn 做對比。 svn 中,只有伺服器上有完整的版本庫,客戶端的程式碼歷史並不是完整的。而 git 中每一個客戶端都是一個完整的版本庫,push 和 pull 操作其實是在多個完整的版本庫之間做同步。
這樣的優勢就是不怕伺服器掛掉或遺失,每個人都有完整的版本庫,這也是Linus 開發git 的出發點(Linux 是一個多人分散式協作的開源專案). 而劣勢就在於權限管理比較差,每個人都有完整的版本庫有可能會導致程式碼(歷史上所有的修改)不小心洩漏。
迷茫2017-04-26 09:03:40
終於遇到一個可以好好回答的問題了;
先說一下題主的假設:
C 從第 6 天開始不跟遠端互動了,自己玩了,這是做什麼?這不是分散式,分散式是在網路可用下說的,分散式不是隔著幾天不跟遠端同步;
既然檔案叫 GlobalFunction.php
,那么这个文件就是修改极少的(或者说修改相同的一行是很少的),类似于 Python 下的 setup.py
,有时只是到了新版本发布的时候你才会修改一下 version
變數吧;
GIT 的分支概念個人理解是可以更快速的基於某個或某些分支進行協作開發,例如一個倉庫 foo
有一个 分支叫做:master
,那么 A,B,C 进行协同开发的时候会在本地基于这个共同的分支 master
进行本地分支,比如 A 的本地分支叫 a-dev
,B 的叫:b-dev
,C 的叫 c-dev
,怎麼來的呢?
git checkout -b xxx origin/master # xxx 代表 `a-dev` 等
其次說一下題主說的合併:
如果 C 在本地基於 master
进行了本地分支 c-dev
那么 C 一直可以更新本地 master 分支而不影响本地分支 c-dev
(而此时你可以一直在 c-dev
分支):
git pull origin master:master
那麼把本地 master
更新了,怎么反应在 c-dev
呢? 很簡單,GIT 已經想好了:
git rebase master # 此时你在 `c-dev` 分支
經過了上面,可能會發生的情況無非是兩種:1. 出現合併衝突,rebase
失败;2. 无冲突(可能会自动 merge
),本地 master
最新状况跟本地 c-dev
合併;
對於1 的出現,GIT 不會自動搞定衝突,卻可以嘗試自動合併(merge
),失敗了,叫出現了衝突;出現了衝突一般是指不同的提交,修改了同一文件的同一行(幾行)內容,需要開發者自己搞定衝突;
個人總結
分支是 GIT 區別於其他集中式版本控制系統的殺手鐧(
killer feature
);rebase
是 GIT 分支的殺手鐧;GIT 的分散特性離不開 GIT 良好的分支特性;
高洛峰2017-04-26 09:03:40
簡單地說,可能可以自動合併,也可能不行。
Git的每一個commit保存的是diff,樓主可以隨便git show一個commit看一下。 diff會在原始修改行的前後若干行裡去嘗試匹配,如果能匹配上,那麼這個diff就可以自動合併。
舉個例子,如果C的patch修改了第五行的一個值,他沒有提交。 A和B一直在GlobalFunction.php裡添加新的功能,這些功能都在檔案的尾部添加,那麼C在合併的時候,Git可以透過cherry-pick的方式完成合併。
如果雙方都修改了同樣的地方,那就得手動解決衝突了。