1. 程式人生 > >衝突解決

衝突解決

衝突解決


衝突問題
可是人生哪有處處都如意的時候呢,程式碼也是如此。如果A和B都同時修改了同一個檔案會發生什麼呢,此時就會發生衝突。

比如張三修改了 A.java檔案上傳到了中央伺服器,然後李四在本地也修改了 A.java檔案,李四想提交檔案時,會提示因為衝突而無法提交。系統要求你先把程式碼拉下來,合併了A.java的衝突(這個合併過程,其實有時候git會自動合併,但是複雜的合併git做不了,所以就要求李四自己去合併衝突),然後李四才能提交上去。。。

當然,你工作電腦上提交,然後 自己私人筆記本上又修改了同一個檔案,這和上面是同樣的意思,只是 我們用張三李四來方便表達。

所以為了避免潛在衝突一個好習慣就是 你在修改你的程式碼之前,先git pull一下,把伺服器的最新程式碼拉下來,這是一個好習慣。

因此我們開發時,有時候早晨來了第一件事情,就是先把程式碼git pull一下,進行更新。這是個好的開發習慣。避免你寫了很多程式碼,你同事也寫了很多程式碼,然後衝突了,你們倆合併的時候,比較浪費時間。

我現在就實際做一個衝突的demo,演示衝突是怎麼發生的,又怎麼解決。其實都很好解決。。

我在自己的mac電腦上修改了 FixedThreadPool.java檔案,然後 在mac電腦上 操作了 add,commit,push操作,提交到了遠端伺服器上。

而同時,我在自己的工作電腦上, 也對 FixedThreadPool.java檔案 進行了修改。然後我在自己的工作電腦上, 執行了add,commit操作,現在我要提交了,我執行了 push操作,看看會發生什麼。

圖片名稱:git_push_error_because_confilt.png
git_push_error_because_confilt.png-63.2kB

此時再看看對方 termial提示了什麼。輸出了一大串內容,前面的內容不用關心。
我們只看最後一段主要內容。

To [email protected]:yaowen369/ConcurrentDemo.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to ‘

[email protected]:yaowen369/ConcurrentDemo.git’
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., ‘git pull …’) before pushing again.
hint: See the ‘Note about fast-forwards’ in ‘git push --help’ for details.
翻譯其中的主要內容:

! [拒絕] master -> master (fetch first)
錯誤:向’[email protected]:yaowen369/ConcurrentDemo.git’執行 push時,發生了某些錯誤。
提示:更新之所以被拒絕是因為 遠端分支包含了你本地沒有的內容,這通常是因為 另一個庫推送了同樣的檔案(ref是索引的意思,可以翻譯成檔案)。你可以在推送之前先合併這些遠端的變化(比如,試試 git pull)。

你可以看看 git push --help中的 Note about fast-forwards瞭解更多的細節。
其實在命令列中,help是很有用的,可以提示很多有用的幫助資訊,不過有些termial要求直接 命令後面輸入 help就好了,有些要求輸入 -help,有些要求輸入 --help,或者有些直接輸入 -h/–h也行,但是我們始終要有這個意識,因為太多東西不用記憶,有個大概的印象就好。(計算機常識)

看到這些提示內容,即使你第一次碰到這個問題,你下一步準備怎麼做?人家已經給你提示了啊。直接 git pull啊。

Notice:在你輸入 git pull時,有時候termial會要求你輸入密碼,有時候不會,但是 很快的,termial就會完全的跳轉到一個新的頁面,這應該是你第一個碰到這種情況。

圖片名稱:git_pull_confilit_vi.png
git_pull_confilit_vi.png-35kB

這其實是個vi編輯器,(vim是vi的升級版,因為vim顏色高亮做的比較好,看起來更舒服)。
我們在之前執行 git commit -m "相關提交的log內容"這個命令時, 直接輸入 一行提交說明內容,所以沒那麼複雜,我們就寫一句話 說明一下而已(搞那麼複雜幹啥子)。但是你如果執行 git commit, 不帶-m 然後你直接敲擊回車,也會進入這個vi頁面。(因為你沒使用一行的提交說明模式,系統會以為你想長篇大論的去寫提交資訊呢,所以專門給你準備個編譯器,你好好寫吧,想寫多少,就寫多少)

說簡單點,vi和你的word,notepad,sublime text沒啥區別,包括和你電腦上新建個 文字文件,都是一回事, 都只是一個文字編譯器, 但是這個 vi的歷史可比後面的那幾個歷史早太多了,上世紀八十年代,電腦圖形化介面還沒發明呢,當時電腦操作都是黑乎乎的命令列視窗操作(其實現在window,linux也可以直接黑乎乎的termial操作,只是那麼多命令,大家都記不住,有了滑鼠和圖形化介面,黑乎乎的命令列操作都被忘記了,只剩下程式設計師使用termial了)。word 等更無從談起,但是大家很多時候也要 編譯文字啊,又沒有word等,所以vi就是一個當時環境下的 termial環境操作的 文字編譯器,完全 鍵盤操作,有無數複雜的 快捷鍵,你使用vi操作,完全不用接觸滑鼠,所以操作也比較快(當然是在你比較熟悉快捷鍵的情況下,否則你就尷尬了)。

我們這裡呢,不討論vi。vi的操作是另一個話題。(其實也不是難,而是那麼多複雜的快捷鍵組記憶著比較困難而已)。

我們可以看看 它上面的內容說了什麼。

Please enter a commit message to explain why this merge is necessary,

Lines starting with ‘#’ will be ignored
翻譯內容:

請輸入一些提交內容來解釋為什麼這次合併是必須的。
#號開頭的行都會被忽略(註釋的作用)
其實上面那句Merge branch ‘master’ of git.oschina.net:yaowen369/ConcurrentDemo,就是它預設幫你生成的 提交資訊。
反正你不用管(因為你要搞定這個,這是另一個學習內容,但是你學習這些完全沒必要,雖然也不難。這也是source tree等軟體的好處,使用了 source tree等圖形化軟體,你怎麼著也不會碰到vi介面),還記得 怎麼退出不?

輸入:q, 字元q代表是退出(quit)的意思,不過這個 vi的退出要 一個冒號+q,所以你輸入 :q,直接退出就好了。(git有些介面退出也是:q,只是你輸入命令操作的時候,git自動幫你字首一個冒號了,所以給你省事了而已)

你輸入 :q,注意左下角。

vi_q.png-60.2kB

vi當中: 開頭的都是命令模式,命令模式顯示都在左下角。你輸入 :q回車後, 左下角出現了這麼一行紅色的文字。

E37: No write since last change (add ! to override)
你輸入:q竟然沒用,相關區域提示你,因為這個檔案你啥都沒改動,所以你要加個!號去覆蓋(其實就是強制退出模式)。 然後你直接輸入 :q!就好了。(直接輸入:號就好了,那行紅色的提示就消失了,然後你接著輸入q!就好了,你試圖用鍵盤上的delte等按鍵去刪除紅色文字沒用的)

還要討論vi的相關內容。但是不討論使用者十有八九又會碰到這個問題,當年我第一次碰到vi問題,連怎麼退出都搞不定,急的滿頭大汗。所以不得不討論。

終於我們退出了 vi,看看具體提示了什麼。。

confilit_after_vi.png-29.1kB

紅色方框內的內容不要管,那是因為我第一次在vi中輸入命令 時,輸錯了 :!q,所以vi提示 q命令找不到,我又重新進入輸入了 :q!,就ok了。。

我們看重點內容:

Auto-merging java/src/thread_runnable/FixedThreadPool.java

Merge made by the ‘recursive’ strategy.
java/src/thread_runnable/FixedThreadPool.java | 4 +±-
1 file changed, 2 insertions(+), 2 deletions(-)
注意:Auto-merging自動合併了。也就是說, 因為我剛才測試的衝突比較簡單,所以 git自動比較合併了。(比如張三修改了檔案的第一行,李四修改了檔案的最後一行,這種簡單的,git就能自動合併,但是張三李四都修改了 檔案的第一行,那就只能手動合併了)。

那麼此時你使用git status來檢視狀態,

$ git status .
On branch master
Your branch is ahead of ‘origin/master’ by 2 commits.
(use “git push” to publish your local commits)
nothing to commit, working directory clean
你的工作區很乾淨,沒啥可commit的,但是 注意上面那句話
Your branch is ahead of ‘origin/master’ by 2 commits.
你當前的分支領先 遠端分支 兩次提交。

為啥是兩次,因為本來你就commit了一次,然後 git pull時,又自動合併commit一次,所以就兩次了。

不用關心幾次,看到重點就對了,你當前的分支領先遠端分支。
另外,扯了這麼大一段,別忘記了最初咱們的目的是什麼,咱們最初的目標就是 推送程式碼到遠端伺服器。

所以接下來直接 git push,將 程式碼直接推送到遠端伺服器就好了。

這次的衝突因為我製造的比較簡單,所以自動合併了,但是有些衝突比較複雜,git無法自動合併,
那麼此時就需要你手動合併了。

下面的demo直接借用了網上別人的程式碼衝突內容 ,我針對輸出進行解釋。

$ git pull
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
系統提示你,test.txt檔案衝突了,自動合併失敗了,你需要解決衝突,然後並提交。。

好,我開啟test.txt檔案,會看到下面的情況。

Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.

Creating a new branch is quick AND simple.

feature1
其中衝突的部分都是用 <<<<<<<,=,>>>>>>>來進行標記了,他們都代表了不同分支(或者遠端/本地)不同的內容,你自己看著程式碼,把<<<<<<<,=,>>>>>>>進行刪除,該刪除的程式碼部分也進行刪除。

如果是你自己寫的程式碼,你肯定知道該刪除那些內容,如果不是你寫的程式碼,比如李四寫的,那麼你要叫著李四討論,把你們兩個的程式碼合併掉。李四寫的程式碼你又不瞭解業務邏輯,你不和他討論, 就瞎合併,這在多人團隊中,是大忌。

因為你可能把李四寫的程式碼給覆蓋掉。而程式設計師基本上寫完某個檔案就不再關心了,所以李四也不知道你把他的內容覆蓋掉了,這肯定會引起問題,如果測試人員能發現業務邏輯不對,那還好,最多被測試人員提個程式碼臭罵一頓,但如果測試不能發現,那等著線上事故吧。

比如上面的那段衝突,我們合併成如下形式,並進行檔案儲存。

Creating a new branch is quick and simple.
合併之後,相當於你又重新 修改了檔案。
所以在此重新進行提交步驟。。

$ git add readme.txt
$ git commit -m “老子把衝突合併了”
[master 59bc1cb] 老子把衝突合併了
最後再push就好了。。

到了這裡我們就理解了平時的提交程式碼,拉取程式碼的步驟,以及怎麼解決衝突。
我們再來學習一個命令。git log:

git log: 檢視之前每次提交的說明資訊:

git_log.png-78kB

直接看輸出應該一目瞭然了。每次提交的版本號。作者,時間,提交的資訊說明 都直接列出來了。咱們之前在 git commit -m “說明資訊”,這裡就有用了,否則那麼多次提交,誰也沒本事都記住啊。

尤其是你注意最上面那個 說明資訊:

commit fb095209cc6adf53a98035cc7661d109a2024de9
Merge: 5b90fa3 c63c40b
Author: yaowen [email protected]
Date: Sat Sep 9 11:45:40 2017 +0800

Merge branch 'master' of git.oschina.net:yaowen369/ConcurrentDemo

最上面的是 版本號(就是那一長串奇怪的字串),git的版本號是一長串字串,而svn的版本號就是很簡單的1,2 ,3, 4阿拉伯數字,簡單來講,因為svn你每次提交拉取時,都是直接與中央伺服器互動,而git則是先與版本庫(暫存區)互動,多人都使用git來提交程式碼,他們本地的電腦時間都不一定準確,不能使用1, 2, 3,4作為版本號,因為不能簡單的依據時間戳來比較。而svn則可以直接使用伺服器時間。

這次提交資訊,還很貼心的給你提示了,你這次提交其實是一個 合併衝突操作merge,並且提交的說明資訊Merge branch ‘master’ of git.oschina.net:yaowen369/ConcurrentDemo,就是剛才合併時系統幫我們生成的。(因為當時我們在vi介面並沒有修改預設的提交說明資訊啊)。

注意右下角的冒號, 因為我這個專案提交很多次了,所以log說明比較多,一頁顯示不完,你還記得怎麼上下翻頁,怎麼退出log提示不?(翻頁一般用不到,因為我們一般看提交log也都是看最近的幾次說明,不過怎麼退出這肯定是要知道額)