Git中的程式碼分支如何實現程式碼同步?
當提交程式碼到遠端分支,經常會遇到提交的時候被拒絕的情況
$ git push origin master To [email protected]:micaixiaoduanku/Demo.git ! [rejected]master -> master (fetch first) error: failed to push some refs to '[email protected]:micaixiaoduanku/Demo.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.
這種情況是由於本地分支程式碼的版本已經落後遠端分支程式碼版本這個時候通常會有三種選擇
1,pull遠端程式碼,再本地做merge後push到遠端分支
2,直接進行一個merge遠端分支的操作,再做提交
3,進行一次rebase再做提交
這三種操作方式操作方式和結果是有區別的,下面做一個總結
準備工作:
我在碼雲上建立一個空倉庫,並且clone下來
$ git clone [email protected]:micaixiaoduanku/Demo.git Cloning into 'Demo'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Receiving objects: 100% (3/3), done. Checking connectivity... done. $ mv Demo Demo1 $ git clone [email protected]:micaixiaoduanku/Demo.git Cloning into 'Demo'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Receiving objects: 100% (3/3), done. Checking connectivity... done. $ mv Demo Demo2 $ ls Demo1Demo2
如上所示,目前本地擁有2個倉庫的clone版本Demo1和Demo2.
下面我建立一個fileA檔案並提交到遠端分支master上面
$ cd Demo1 $ ls README.md $ touch fileA $ git add . $ git commit -m "add fileA from Demo1" [master f8a717c] add fileA from Demo1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fileA $ git push origin master Counting objects: 3, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 273 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To [email protected]:micaixiaoduanku/Demo.git 35e89fa..f8a717cmaster -> master
接下來進入Demo2倉庫,建立一個fileB檔案並提交
$ touch fileB $ git add . $ git commit -m "add fileB from Demo2" [master 8761ffb] add fileB from Demo2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fileB
這個時候去push到遠端master分支上面會出現,文章開始所描述的情況
$ git push origin master To [email protected]:micaixiaoduanku/Demo.git ! [rejected]master -> master (fetch first) error: failed to push some refs to '[email protected]:micaixiaoduanku/Demo.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.
第一種操作,根據提示進行pull
$ git pull origin master
出現了一個提示
Merge branch 'master' of gitee.com:micaixiaoduanku/Demo # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
上面的意思是,這裡進行了一個merge遠端分支master的操作,並且是作為一次commit, 當然你可以刪除這個Message忽略這次merge.(如果忽略這次merge,需要自己手動進行merge, 待會兒我會補充這種情況如何處理,下面先看不刪除Message的情況)
接著我們進行再進行push操作
$ git push origin master Counting objects: 4, done. Delta compression using up to 8 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 559 bytes | 0 bytes/s, done. Total 4 (delta 0), reused 0 (delta 0) To [email protected]:micaixiaoduanku/Demo.git f8a717c..3b567c1master -> master
這下成功了,看看提交記錄
$ git log commit 3b567c182ad6e262404ce60d7ec82447c837c98a Merge: 20fe0ea f8a717c Author: huangli <[email protected]> Date:Fri Jan 19 16:02:38 2018 +0800 Merge branch 'master' of gitee.com:micaixiaoduanku/Demo commit 20fe0ea4baca93380b12836c026b15681b5c6905 Author: huangli <[email protected]> Date:Fri Jan 19 15:58:58 2018 +0800 add fileB from Demo2 commit f8a717cb55e6e75163d66e4d62d2af0f84d96eb0 Author: huangli <[email protected]> Date:Fri Jan 19 15:57:56 2018 +0800 add fileA from Demo1
這種情況有提交“Merge branch 'master' of gitee.com:micaixiaoduanku/Demo”的提交記錄,其實我本意並不希望出現這個提交記錄,我只希望有一條“add fileB from Demo2”的提交記錄,在剛才pull的過程中,我拉下來了遠端倉庫的fileA檔案,這是一個merge的過程,在git中merge是一個commit的操作,切記(這點和SVN不同),也許有同學會問到,那我如果刪除前面提到commit的Message會不會就不會有這條提交記錄了?我們來試試看
我在刪除Merge branch 'master' of gitee.com:micaixiaoduanku/Demo 會得到這條提示
error: Empty commit message. Not committing merge; use 'git commit' to complete the merge.
再來看看log
$ git log commit 8cda7fa99bdae380b8486c70402c16664ed338e6 (HEAD -> master) Author: 351573105 <[email protected]> Date:Sat Jan 20 20:51:47 2018 +0800 add fileB from Demo2 commit 00d805930f9bd5b1dff5beb2027c3b8b7c3238c4 Author: Eric <[email protected]> Date:Sat Jan 20 20:43:03 2018 +0800 Initial commit
果然,沒有那條我討厭的merge log了,這個時候我再看看本地分支的status
$ git status On branch master Your branch and 'origin/master' have diverged, and have 1 and 1 different commits each, respectively. (use "git pull" to merge the remote branch into yours) All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: new file:fileA
我暈,merge下來的fileA是在暫存區,還沒有commit, 看來是逃不過這條commit message的。
總結:pull這種方式會存在一個merge的過程,這個merge過程是會當成一個commit提交的,這樣會造成一個結果是:我把fileA merge過來了並提交了,那麼log中將會出現一個 merge的commit裡面包含增加fileA檔案,那麼pull下來的log記錄將會和“add fileA from Demo1” 中提交fileA的這條記錄有衝突,試想一下,目前3個同事協同開發,同事A, 建立一個檔案A並提交,同事B, 建立檔案B並提交(這個時候也要進行同步,但是他用的rebase, 不會有多條commit記錄),重點看同事C , 建立檔案C並提交,他進行了pull操作,然後merge了檔案A和檔案B, 這是兩條提交記錄,一條是新增檔案C,另外一條是檔案A和檔案B的一條merge commit, 這個時候提交記錄看上去就很奇怪。
- 一是同事A和同事B的內容重複
- 二是你明明只想提交一個檔案C,卻順帶提交了檔案A和檔案B
這也是在這種情況下建議用rebase的原因.
第二種操作,直接進行merge
切換到Demo1,編輯fileA
$ vim fileA
新增一行
Demo1 修改fileA
然後往遠端分支上面提交
$ git add . $ git commit -m "Demo1 修改 fileA" [master 78cbea3] Demo1 修改 fileA 1 file changed, 1 insertion(+) $ git push origin master To [email protected]:micaixiaoduanku/Demo.git ! [rejected]master -> master (fetch first) error: failed to push some refs to '[email protected]:micaixiaoduanku/Demo.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.
這個時候又不同步了,會叫你進行pull的情況,這個時候我們選擇去merge遠端分支
先執行git fetch命令(fetch是獲取遠端倉庫所有資訊,但是不會merge到本地倉庫,這是它和pull的區別,pull是會進行merge的)
$ git fetch remote: Counting objects: 4, done. remote: Compressing objects: 100% (4/4), done. remote: Total 4 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (4/4), done. From gitee.com:micaixiaoduanku/Demo f8a717c..3b567c1master-> origin/master
再執行
git merge origin/master
Merge remote-tracking branch 'origin/master' # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
同樣會出現之前進行pull操作的情況,但這個時候它預設的message不是“Merge branch 'master' of gitee.com:micaixiaoduanku/Demo”,看出區別了嗎?一個pull的message是merge遠端的分支,而fetch後再merge的message是merge的origin/master遠端跟蹤, 它實質上是一種本地merge(本地分支和本地分支進行merge, pull是本地分支和遠端分支進行merge). 隨後我們看下log
$ git log commit 3efb4143cb6ff5fd9c1f96d3acdf6a31c7466402 Merge: 78cbea3 3b567c1 Author: huangli <[email protected]> Date:Fri Jan 19 16:12:47 2018 +0800 Merge remote-tracking branch 'origin/master' commit 78cbea3d1f3203517fcb63c06d2cbe1ad93bacc5 Author: huangli <[email protected]> Date:Fri Jan 19 16:10:49 2018 +0800 Demo1 修改 fileA commit 3b567c182ad6e262404ce60d7ec82447c837c98a Merge: 20fe0ea f8a717c Author: huangli <[email protected]> Date:Fri Jan 19 16:02:38 2018 +0800 Merge branch 'master' of gitee.com:micaixiaoduanku/Demo commit 20fe0ea4baca93380b12836c026b15681b5c6905 Author: huangli <[email protected]> Date:Fri Jan 19 15:58:58 2018 +0800
同樣會多一條merge的commit記錄.
最後再進行push
$ git push origin master Counting objects: 5, done. Delta compression using up to 8 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 542 bytes | 0 bytes/s, done. Total 5 (delta 1), reused 0 (delta 0) To [email protected]:micaixiaoduanku/Demo.git 3b567c1..3efb414master -> master
總結: 這種方式和pull很像,區別就是一個遠端直接merge,一個是先fetch再進行本地merge. 同樣它也會產生一個merge操作後commit日誌,這點是對於有提交記錄潔癖的人不太妥當.
第三種操作,進行ReBase
什麼是rebase?
再切換回Demo2,修改fileA檔案,在第一行寫“Demo2 修改fileA”,然後提交到遠端分支.
$ vim fileA $ git add . $ git commit -m "Demo2 修改fileA" [master 2aac6c6] Demo2 修改fileA 1 file changed, 1 insertion(+) $ git push origin master To [email protected]:micaixiaoduanku/Demo.git ! [rejected]master -> master (fetch first) error: failed to push some refs to '[email protected]:micaixiaoduanku/Demo.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.
這個時候先進行一次fetch操作
$ git fetch remote: Counting objects: 5, done. remote: Compressing objects: 100% (4/4), done. remote: Total 5 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (5/5), done. From gitee.com:micaixiaoduanku/Demo 3b567c1..3efb414master-> origin/master
接著rebase
$ git rebase origin/master First, rewinding head to replay your work on top of it... Applying: Demo2 修改fileA Using index info to reconstruct a base tree... MfileA Falling back to patching base and 3-way merge... Auto-merging fileA CONFLICT (content): Merge conflict in fileA Failed to merge in the changes. Patch failed at 0001 Demo2 修改fileA The copy of the patch that failed is found in: /Users/huangli/Desktop/mygit/test/Demo2/.git/rebase-apply/patch When you have resolved this problem, run "git rebase --continue". If you prefer to skip this patch, run "git rebase --skip" instead. To check out the original branch and stop rebasing, run "git rebase --abort".
fileA出現了衝突,我們先去解決衝突
<<<<<<< 3efb4143cb6ff5fd9c1f96d3acdf6a31c7466402 Demo1 修改fileA ======= Demo2 修改fileA >>>>>>> Demo2 修改fileA
看看目前status
$ git status rebase in progress; onto 7d18bfc You are currently rebasing branch 'master' on '7d18bfc'. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to check out the original branch) Unmerged paths: (use "git reset HEAD <file>..." to unstage) (use "git add <file>..." to mark resolution) both modified:fileA no changes added to commit (use "git add" and/or "git commit -a")
目前處於一個rebasing狀態,接下來要做的是,解決衝突後執行一個"git rebase --continue",好,我們先解決一下衝突,修改成如下
Demo1 修改fileA Demo2 修改fileA
然後執行
$ git rebase --continue fileA: needs merge You must edit all merge conflicts and then mark them as resolved using git add
提示說還需要add過後表明解決了衝突,ok, 我先add.
$ git add fileA $ git rebase --continue Applying: Demo2 修改fileA
最後來看看提交記錄.
$ git log commit 884a0b87c06dcecf1792cb0c900d26cb3f2f0a88 (HEAD -> master) Author: 351573105 <[email protected]> Date:Sat Jan 20 22:10:27 2018 +0800 Demo2 修改fileA
哈哈,終於沒有令程式碼潔癖厭煩的merge commit log了。
總結: 在解決本地分支和遠端分支程式碼不同步的時候使用rebase的優勢顯而易見,它既可以完成程式碼的merge工作,同時可以不出現merge commit log記錄,從而保證提交記錄的整潔。所以多人協同在同一條分支下面進行開發的情況下,出現分支程式碼不同步的情況下,一定要使用rebase進行的衝突處理.
【附錄】

資料圖