沒有辦法把作品推上遠端倉庫的原因就是本地端的版本比遠端倉庫舊,簡單說就是發生衝突了。
這部分我們之前在合併分支發生衝突的章節已經介紹過,處理的方式其實都差不多,
不過我們現在想演練一下多人開發時經常發生的這種狀況。
環境
現在檔案下載平臺在主機中有一個正式環境和一個遠端倉庫 repo
目前裡面只有 master 主幹道,還沒有其他分支。
這個專案有兩位開發者,分別是 logo 與 shrove
我們想要演練的是寫好作品結果無法推上倉庫的狀況。
理論上在一開始大家把專案 clone 回來後都不會有問題,但隨著不同開發者與不同功能的開發,
也許兩個開發者改到一樣的檔案而發生衝突,或者 shrove 將作品推上倉庫後,因為 logo 還在進行其他功能的開發,
等過了三天 logo 開發好其他功能後想推上去就會發生推不上去的狀況,因為 shrove 手腳比較快。
開分支
目前 logo 與 shrove 都把專案 clone 下來了,而且雙方的 commit 進度一樣。
我們先來開個 dev 分支來實驗,以免正式環境被我們弄壞了。
# logo 檢查本地分支
$ git branch
* master
# logo 檢查遠端倉庫分支
$ git branch -r
origin/HEAD -> origin/master
origin/master
# 遠端分支會有 origin 字樣, origin 是我們在 git remote add 時決定的名字
# logo 在本地端建立 dev 分支
$ git branch dev
# logo 切換到 dev 分支
$ git checkout dev
# logo 在 index.php 把 檔案更新日期的註解改成今天的日期並進行本地提交
$ git add .
$ git commit -m '修改 index.php 的修改日期註解為今日'
[dev 482f45f] 修改 index.php 的修改日期註解為今日
1 file changed, 1 insertion(+), 1 deletion(-)
# logo 將分支推上倉庫
$ git push origin dev
logo@gaga.tw's password:
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 339 bytes | 339.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To gaga.tw:/home/repo
* [new branch] dev -> dev
# logo 檢查遠端分支
$ git branch -r
origin/HEAD -> origin/master
origin/dev
origin/master
# 看起來一切正常,現在換 shrove 上場
# shrove 檢查遠端分支
$ git branch -r
origin/HEAD -> origin/master
origin/master
# shrove 為什麼沒看到遠端的 dev 分支啊,因為沒有更新呀
# shrove 更新遠端資訊
$ git remote update
Fetching origin
shrove@gaga.tw's password:
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 319 bytes | 2.00 KiB/s, done.
From gaga.tw:/home/repo
* [new branch] dev -> origin/dev
# shrove 再次檢查遠端分支
$ git branch -r
origin/HEAD -> origin/master
origin/dev
origin/master
# shrove 檢查本地端分支
$ git branch
* master
# shrove 直接切到 dev 分支
$ git checkout dev
Switched to a new branch 'dev'
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
$ git branch
* dev
master
# 雖然也可以,但比較好的做法可能是把 dev 分支 pull 回來再切換會比較合理
### 衝突
# 接著,shrove 把 index.php 的第一行刪掉然後推上倉庫
$ git add .
$ git commit -m '把 index.php 的第一行刪掉'
[dev 1d5ca54] 把 index.php 的第一行刪掉
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push origin dev
shrove@gaga.tw's password:
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 320 bytes | 320.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To gaga.tw:/home/repo
482f45f..1d5ca54 dev -> dev
# 提醒一下,origin 後面要寫清處是哪個分支,並不是處在哪個分支就會自動預設那個分支喔
# 那因為logo 要改的功能比較多,所以沒有即時 pull 等專案完成了準備推上倉庫
# logo 是把 index.php 裡的版號註解改成 1.1.0
$ git add .
$ git commit -m '把 index.php 的版本註解改成 1.1.0'
[dev a6f60a9] 把 index.php 的版本註解改成 1.1.0
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push origin dev
logo@gaga.tw's password:
To gaga.tw:/home/repo
! [rejected] dev -> dev (fetch first)
error: failed to push some refs to 'logo@gaga.tw:/home/repo'
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.
不妙,reject 就是被拒絕了,git 告訴我們目前本地端的版本比較舊,應該要先把遠端倉庫的拉回來整理好再一起推上去
$ git pull origin dev
logo@gaga.tw's password:
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 300 bytes | 2.00 KiB/s, done.
From gaga.tw:/home/repo
* branch dev -> FETCH_HEAD
482f45f..1d5ca54 dev -> origin/dev
Auto-merging index.php
Merge made by the 'recursive' strategy.
index.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
他會跳出預設編輯器要我們寫 commit 訊息,這次比較幸運的是雙方雖然改同一個檔案,但改的地方是不同的,所以不需要手動合併。
到這邊應該要有印象,其實就是處理 merge 衝突的狀況而已。
至於實際發生衝突應該改成什麼,就由專案負責人去決定,這部分跟 git 無關。
自動解決衝突後會自己幫我們 push 上去。
然後換 shrove 這時候他如果還沒開始新的開發,就會先 pull 回來,那就沒問題,直接更新專案為最新進度。
但如果他也開發到一半,那就會發生跟 logo 一樣的狀況,處理方式當然也是一樣了。
硬推
比較特殊的狀況就是在 push 時加上 -f
可以把本地端的專案硬推上去蓋掉倉庫現有的專案進度。
如果只有自己一個人在開發那就沒什麼問題,反正自己知道自己在做什麼,也完全知道改了哪些東西。
懶得處理衝突又有把握手上的是最新進度,那就硬推上去吧。
但如果是多人開發的,一旦發生衝突就應該是解決衝突或者協調,不應該直接去蓋掉別人的作品。
因為我們可能不知道對方改了什麼,而且你寫的東西也不一定就是對的,此時需要有一個專案經理人做這件事。
也就是去看雙方更改的內容決定該怎麼做,這位經理人也應該是最了解整個專案的人才是。
那萬一不小心蓋掉了怎麼辦?只好請本地端有舊 commit 的開發者再強制推上來把他蓋掉囉。
刪除遠端分支
因為指令比較特別,就在這裡提一下。
我們把剛剛的分支 dev 刪掉好了,不過我們想要直接刪除遠端的分支。
# 刪除遠端分支 dev
$ git push origin :dev
logo@gaga.tw's password:
To gaga.tw:/home/repo
- [deleted] dev
# 檢查遠端分支
$ git branch -r
origin/HEAD -> origin/master
origin/master
# 檢查本地分支
$ git branch
* dev
master
特別的地方是使用 push 指令,然後在分支名稱前面加個冒號。
指令的意思是推一個空的分支去把原本的分支蓋掉。
雖然有點不直覺,但勉強解釋得通。
至於本地端的分支,我們在之前的章節已經介紹過刪除方式了。
回憶
記得之前使用硬推的情境是當時剛學 git 對很多概念跟指令都還不懂,那時只會 add, commit, push, pull 而已。
我就直接在 master 開發,沒經過仔細測試就推上正式環境,然後整個內部系統就掛了。
那時候很慌張,就亂查了一下指令,從正式環境退版,本地跟正式環境改來改去改到亂掉,不知道哪份才是對的。
最後,只好用本地環境把程式改好然後硬推上去才解決問題。
也就是因為這個經驗,讓我下定決心一定要趕快把 git 學好,當時我很自責,為什麼接受了那麼重要的任務卻沒有準備好呢?
然後造成災難,告訴自己要努力,不能再讓這樣的事情發生。
※最後更新時間:2020-04-26 15:19:29 From:1.161.139.7 By:特種兵