2017年11月16日星期四

GitHub 及 EGit ( PART 6 / 8 ) + Git Conflict

接上回【GitHub 及 EGit ( PART 5 / 8 ) + Git Branch

Git 最困難的部份就是出現衝突 ( Conflict ) 時,要如何處理,多數人都會用【Merge】解決,雖然還有【rebase】這個方法,但這個方法會重寫 commit history,用法不當會讓參與的人相當困擾。

不想按部就班看,可直接前往 總結,看看本篇文章有什麼能用。

3.6 Git Branching - Rebasing
https://git-scm.com/book/en/v2/Git-Branching-Rebasing

———————————簡易的分隔線————————————

事前準備︰


看似只有一個分支,其實是兩分支

現在的 History 是這樣的。

複製及改名

因為會把 Local Repo 玩爛,所以先備份好。怕有 FileLock 之類的問題,所以複製之前,記住先把 Eclipse 關掉。

———————————簡易的分隔線————————————

Merge︰


沒有衝突,自動 Merge︰


先到master Branch

目標是 Another_Business 合併到 master,先 【Check Out】到 master。

開始做合併

在 master 右鍵,選【Merge…】。

注意 FF 及 NFF 選項

這裡要注意 Fast forward options,那兩個 Option 分別是說【Fast forward】及【non Fast forward】。如果什麼都不做,選 Another_Business 後按 Merge 就是【Fast forward】。

註︰試完【Fast forward】,回復備份再試【non Fast forward】。當然回復備份前後都是先關掉 Eclipse。

【Fast forward】結果︰


Fast Forward 結果

【non Fast forward】結果︰


Non Fast Forward 結果

———————————【Fast forward】 VS 【non Fast forward】( start )————————————

如果想 History 簡潔,用【Fast forward】比較好,但一定要很清楚自己在做什麼,因為【Fast forward】是把原本 master 的 reference 向前移動,不能追蹤原本的 master reference 在那裡,假如之後發現 Another_Business 這個 branch 有問題,想回到合併前會有困難 ( 當然可以用 Tag 這個技巧,把原本的 reference 記錄下來 )。

【non Fast forward】合併時會弄一個 Merge commit,所以搜尋 Merge 這個 keyword 會很容易找到合併時,兩個 Parent commit ID 是什麼。

個人喜歡用【non Fast forward】合併進 master,因為有更多訊息可以追蹤出問題地方。保持 branch 簡潔用【Fast forward】,如果有個 branch 只會自己用,在公開出去之前,清乾淨多餘的 History 才合併進 master,這個時候或許還會用上【rebase】等技巧。

———————————【Fast forward】 VS 【non Fast forward】( end )————————————

制作衝突、手動修改及 Merge︰


發生衝突

正常來說,【FETCH】回 Local Repo ,在修改途中,有新 commit 進了 Remote Repo,做【PUSH】時就會發現 SHA-1 不對,衝突 ( Conflict ) 出現,這時候要跟人協商如何修改。

回復備份

不知道在說什麼沒關係,或許現在難度太高,向下調整吧,只用 Local Repo 模擬出衝突 ( Conflict ) 怎處理,所以先回復備份,這是回復備份後的狀態。

跟步驟做

吹氣娃娃共享這生意實在太蠢,成本太高,而且開始了一天就被打臉,來個飛機杯共享不好嗎?或許會像 Silicon Valley 那套美劇一樣,聯想到新的檔案壓縮法。

  1. 【Check Out】到 master。

  2. Working Tree 右邊有寫檔案路徑,把 Windows 的檔案總管開出來,圖片拉進去 img 資料夾。

  3. 修改index.html 後 Save。

  4. 在 Working Tree 右鍵【Add to Index】。

  5. 寫 Commit Message 後 Commit。

【共享Mary】推出後即出事 公安以「擾亂社會治安」拉人
http://hk.apple.nextmedia.com/realtime/china/20170916/57219285

飛機杯搭配特攝片 《自慰戰士テンガマン》見參!
https://www.youtube.com/watch?v=NNyToBTwb2k

———————————簡易的分隔線————————————

再次備份 Local Repo︰


再備份

因為會玩爛,所以再次備份 Repo。叫【5thCommit_backup】吧。

———————————調整 Editor 文字大小 ( start )————————————

更改字大小

中文字太小?調大一點看看。

———————————調整 Editor 文字大小 ( end )————————————

Merge︰


跟上面做法一樣,在 master branch 右鍵,按【Merge…】後,選 Another_Business branch,按 Merge。結果說 Conflicting。

衝突!

發生衝突會怎樣?首先 Git 會修改發生衝突的檔案,還會自動幫你 Save,記錄這個檔案是 Conflicts status。

自動修改後的檔案狀態

<<<<<<< HEAD
( 內容 )
=======
( 內容 )
>>>>>>> refs/heads/Another_Business

以上是 Git 幫你修改的內容,要做的事很簡單,就是決定用什麼版本,直接把不用的東西刪除,然後 Save,在 Unstaged Changes 做【Add to Index】後會自動産生 Commit message。圖片加文字還是不懂,可以參考之後 rebase 的影片,做的東西差不多。

解決衝突後的狀態

就像這樣。

git-status - Show the working tree status
https://git-scm.com/docs/git-status

———————————簡易的分隔線————————————

Compare Editor︰


<<<<<<< HEAD
( 內容 )
=======
( 內容 )
>>>>>>> refs/heads/Another_Business

覺得這樣有點難看?EGit 當然有提供 GUI 工具用。

內建的比較 View

這是回復【5thCommit_backup】那個備份後的 History。

要清楚自己在什麼 Branch 及 HEAD 在那裡,那是關乎你的 Working Tree 是什麼狀態,對 Compare 很重要。有件遺憾的事,雖然叫 Editor ,但很難用來處理 Merge conflict,只能用人手修改。

註︰在 Compare Editor 也能右鍵用【Compare with】,試試看。

Merging changes in the compare editor
https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.platform.doc.user%2Ftasks%2Ftasks-68dg.htm&cp=0_3_8_2

———————————簡易的分隔線————————————

Synchronize View︰


這個 View 限定 workspace 跟 Repo 的 references 同步,所以不需要每次都【Compare with】,會自動顯示各檔案現在的狀態。(PS︰當然有時候要手動 Refresh View )

首先,跟【HEAD】同步是沒有什麼意義,【HEAD】通常會標籤著 Working Tree 即是 workspace,只會得到 no change 這個訊息。

這個 View 用來同步其他 commit,看看有沒有 conflict,有多少個 conflict 等等,有人會定期做【FETCH】這個動作,然後跟【FETCH_HEAD】同步,盡早發見及解決 conflict 會做少很多 merge 的工作。

同步

在 Synchronize View 那個 Tag,按下箭頭,找【Synchronize…】。


選 Git 按 Next。


看 Repo 選你想做 Synchronize 的 reference。因為這次全都在 Local Repo 進行,所以我選了 refs/heads/Another_Business,按 Finish。

提示

會提示你可以用 Team Synchronizing Prespective,不過 Git 這個 Prespective 已經有齊所有用到的 View,所以就不用了,按 【No】。

sync view 可以看到什麼

我回復了 5thCommit_backup 那個備份,回到還沒做 Merge 的 Repo。預先知道 merge 會發生 conflict,什麼檔案、那些內容會有 conflict,如果發生 conflict 很多,直接把對方的 Repo【FETCH】進來重做或許比修正 conflict 更快。

Local File Workspace 相等於現在的 Working Tree 而 Remote File 相等於 下面 History View 顯示縮寫 Commit ID 的檔案。

Egit Synchronization State
https://wiki.eclipse.org/EGit/User_Guide#Synchronization_State

———————————簡易的分隔線————————————

Rebase︰


不知道 rebase 會為其他人帶來什麼困擾之前,先了解 rebase 是什麼。簡單來說,把以前的 commit 回收,接在其他 branch 末端,再次逐一 commit,做 Merge 或 Fast forward。

通常只需要以下幾個步驟就完成。最後,可選擇刪除不需要的 branch reference,簡化 Git History。

  1. switch to branch

  2. rebase

  3. resolve conflict

  4. (optional) delete branch


Conflict 可能不只一個,重覆 resolve conflict 及 Continue,直到完成。這個例子把 master branch 的 commit 回收後,加到 Another_Business 後面,自動 Fast forward 那個 master branch reference。

 Git_rebase_1

這是 rebase 後的 History,就像沒有開過 branch 一樣。

Git_rebase_2

不像 merge 有 commit message,要在 Reflog View 查詢 rebase 了什麼。

Git_rebase_3

rebase 成一條 branch 後,Another_Business 這個 branch 沒有用,可以右鍵把 branch 刪除。

Git_rebase_4

這是刪除 Another_Business 這個 branch 的 History。

——————————— rebase 高危事項 ( start )————————————

改動 Git History,而且是公開的 Repo,就是危險,雖然用 reflog 可能救到,但分析 log 是件難事。

首先,温習一下 Commit ID 包含什麼,除了 Tree Object,還包含 author、committer 及時間等,心裡要記住「SHA-1 值 = Commit ID」,而 rebase 是把已有的 commit 回收,再次 commit 到指定的 branch 末端,就算 commit 內的 Tree Object、author、committer 等等全部都一樣,再次 Commit 時,日期時間一定會改變,計算出新的 SHA-1 值,理所當然 Commit ID 跟著是新的。

通常以【英文字母】代表原本的 Commit。與原本的 Commit 內容一樣,只有 Commit 時間不一樣的 Commit,會以【英文字母】加一個【'】來代表。如以下例子︰

rebase 前︰

A ---- B ---- C ( master branch )
\
\
  D ---- E ( topic branch )

rebase 後︰

A ---- B ---- C ---- D' ---- E' ( master branch )

造成困擾的過程及如何發生就不說了,以結果而言,困擾的一方,會對似曾相識 Commit message 感到疑惑,花更多心力在解決衝突上,就算不疑惑,知道是某人 rebase 不當,也要重新解決曾發生過的衝突。

———————————rebase 高危事項 ( end )————————————

Reset - mixed, hard and soft︰


Git_reset_1

後悔了,想回頭該怎做?回復備份嗎?不是。可以 check out 某個 Commit ID 回復,但會連 Working Tree 的心血同時蓋過去,有沒有想過只想回復到某種【狀態】呢? 【reset】幫到你。

Git_reset_2

首先備份已做 rebase 的 local Repo。然後,這裡會以 Initial commit 做 reset 示範,每做一次都會回覆備份,在 Egit 角度,reset 會影響到
  1. References ( Commit History )

  2. Working Tree ( workspace )

  3. Git Staging ( index / tracked files / Untracked files )
這三個項目在 Git 有各種各樣的叫法,但要說的東西其實都是一樣。

1. Reset Soft


Git_reset_soft_1

Git_reset_soft_2

只會 reset HEAD 這一個 Reference,Working Tree 的內容及 Git Stage 不變 ( 還是 Tracked files )。

2. Reset mixed


Git_reset_maxed_1

Git_reset_mixed_2

會 reset HEAD Reference 及 Git Stage ( 變成 Unstaged ),Working Tree 內容不變 。

3. Reset hard


Git_reset_hard_1

Git_reset_hard_2

Working Tree、Git Stage 及 HEAD 都會洗白,完全覆蓋過現有的所有東西。

參考︰
https://dotblogs.com.tw/wasichris/2016/04/29/225157
https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified#_step_1_move_head

———————————簡易的分隔線————————————

Stashes


緊急任務!例如發現一個很嚴重的 BUG,需要即時處理,但手頭上寫到一半的程式碼怎麼辦?要 Commit 嗎?但沒完成到某階段,就半途 Commit,會影響到 Commit History,越多半途 Commit 越混亂,有沒有暫存方案?用【Stashes】吧!

首先,做什麼都好,都要把 Working Tree 內的檔案 save 好,Git Staging 內檔案 Commit 乾淨才可以 switch branch。【Stashes】就是用來 Commit 沒完成、暫存的東西。
  1. 把未完檔 save。
  2. 做 Stashes 及輸入 Stash message。
  3. switch 去緊急任務 branch。
  4. 做完要做的任務後 save 、 Add to Index 及 Commit。
  5. switch 回未完成的 branch。
  6. Apply Stash。
這次示範,是回復【SharingEconomy_5thCommit_backup】後的操作︰


———————————簡易的分隔線————————————

總結︰


簡潔地列出提到的名詞解釋︰

  • 【Merge】—— 合併 Branch。

  • 【Fast forward】—— 合併 Branch,移動 HEAD 到最新 Commit。

  • 【non Fast forward】—— 合併 Branch,建立 Merge Commit。

  • 【rebase】—— 回收 commit,接在其他 branch 末端做 Merge 或直接 Fast forward。

  • 【Reset Soft】—— 只移動 HEAD。

  • 【Reset Mixed】—— 移動 HEAD 及 回復 Stage。

  • 【Reset Hard】—— 移動 HEAD、回復 Stage 及 Working Tree。

  • 【Stashes】—— 暫存 Commit 。

git 大部份操作都介紹過,是時候介紹 GitHub 那邊,最後是 GitHub 及 EGit 的真正互動操作。

下一回【GitHub 及 EGit ( PART 7 / 8 ) + GitHub 介面及功能

沒有留言:

發佈留言

設有留言驗證及審查,檢閱後,才會顯示留言。
本人惰性很高,留言或許會石沉大海。