iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
JavaScript

從看不懂到做出來,用 PawPal 走過前端新手村系列 第 22

Day 22|合併衝突!我第一次和 Git 打起來。

  • 分享至 

  • xImage
  •  

今天的故事

在課程裡,我其實早就學過 Git 的 merge conflict,也練習過怎麼解。

但那時候的衝突,是為了練習而刻意製造出來的。

所以就算畫面跳出 conflict,我也大概知道:

這裡會發生衝突
↓
找到衝突的位置
↓
確認要保留的內容
↓
處理完成

真正到了 PawPal,感覺完全不一樣。

我印象很深的是,有一次 PR 開出去之後,組員在 PR 頁面提醒我:

這邊要解衝突。

看到的當下,我其實知道 merge conflict 是什麼。

但我第一個想到的不是:

指令要怎麼下?

而是:

我要留哪一邊?

如果我解錯了,會不會把組員的程式刪掉?

原本正常的功能會不會被我弄壞?

那也是我第一次真的感受到:

merge conflict 最麻煩的地方,不是 Git 跳出警告,而是它把「最後要留下什麼」這個決定交給了我。


課堂上的 conflict,和真實專案真的不一樣

課堂上的 conflict,通常是我們自己刻意做出來的。

例如兩個 branch 都去修改同一段文字,再讓它們合併。

所以看到衝突時,其實心裡已經知道:

好,這就是今天要練習的地方。

但真實專案不是這樣。

PawPal 是多人一起開發,每個人都在不同的 branch 上做自己的功能。

等到大家的程式開始整合時,發生衝突的兩邊,很可能都不是「錯的」。

一邊可能是我正在做的新功能。

另一邊也可能是組員剛完成,而且已經正常運作的功能。

所以不能單純想:

這是我的
所以留我的

也不能看到另一邊來自 dev,就直接全部留下。

真正要確認的是:

兩邊到底各自在做什麼?

哪些內容需要留下?

有沒有可能兩邊其實都需要?

這才是我覺得真實專案裡,解 conflict 最難的地方。


Git 不是在告訴我「哪一邊錯了」

以前看到 conflict,我很容易覺得:

Git 又出問題了。

但後來我才慢慢理解,merge conflict 本身不代表程式壞掉。

可以用一個「簡化概念」理解:

我的 branch 修改了某段程式
↓
Git 發現 dev 也修改了同一個地方
↓
Git 無法自己決定最後要保留哪一種結果

Git 可以告訴我:

這裡有兩份不同的修改。

但它不知道最後的功能應該長什麼樣子。

它不知道要留 A、留 B,還是兩邊其實都需要。

因為 Git 看得到程式碼的差異,卻不知道這些程式背後真正負責的是什麼功能。

所以:

Git 可以幫我找到衝突,但不能替我決定正確答案。


我那時候第一個動作,是把 PR 給 AI 看

第一次真的遇到這種情況時,我不太敢直接亂改。

因為這次不是自己做練習。

PR 裡除了我的修改,也牽涉到其他組員已經放進 dev 的內容。

所以我當時的做法很直接:

把 PR 頁面的網址複製給 AI。

請它先幫我看問題可能出在哪裡、為什麼會發生 conflict,以及兩邊大概修改了什麼。

AI 可以先幫我整理狀況,也可以提供處理建議。

但最後哪些程式要留下,我還是會自己再看過一次。

因為如果我連兩邊的程式在做什麼都不知道,就直接照著建議把其中一邊刪掉:

就算 conflict 消失了,也不代表功能是對的。

這也是我後來越來越習慣的方式。

AI 可以先幫我理解問題。

但最後做決定的人,還是我自己。


真實專案裡,兩邊的修改可能都要留下

後來回頭看 PawPal 的 Git 紀錄,我才發現,開發期間其實真的留下了不少 conflict 的紀錄。

其中有一次,是我在做「新增寵物基本資料」功能的時候。

當時我的功能分支需要和最新的 dev 同步。

同步的過程中,管理寵物資料的 petStore.js 發生了 conflict。

簡單來說,就是:

我正在修改寵物資料功能,而 dev 裡也出現了新的寵物資料處理。

真正去看內容後,會發現兩邊都有需要留下來的東西。

我這邊有新增、選取寵物等功能。

dev 裡也已經加入其他頁面會使用到的寵物資料處理。

這時候我才更明顯感受到,conflict 並不是兩份程式裡一定有一份是錯的。

我的修改是為了完成目前正在做的功能,但 dev 裡的新內容也可能已經被其他功能使用。

如果只是因為其中一邊是自己比較熟悉的程式,就直接把另一邊蓋掉,表面上看起來 conflict 是解決了,實際上卻可能讓原本正常的功能一起消失。

所以如果直接:

只留我的

可能會把 dev 原本新增的功能蓋掉。

反過來如果:

只留 dev

也可能讓我正在做的功能消失。

真正要做的是:

先理解兩邊
↓
找出各自負責的功能
↓
再把需要的內容整合起來

這個案例不是我現在能確認的「第一次 conflict」。

因為我已經不記得第一次真正發生衝突的是哪一張 PR。

但從 Git 紀錄可以確認,這確實是我在 PawPal 開發期間處理過的一次合併衝突。

而這種經驗也讓我開始真正理解:

解 conflict 不是選「我要哪一邊」,而是判斷「最後正確的程式應該包含什麼」。


現在遇到 conflict,我不會急著按按鈕

處理 conflict 時,編輯器通常會看到:

Accept Current

Accept Incoming

Accept Both

以前看到這些選項,很容易覺得:

所以我選一個就好了吧?

但現在我比較不會急著按。

比起先記哪一顆按鈕要選,我會先看:

兩邊到底改了什麼?

所以如果現在再遇到 conflict,我的處理順序大概會是:

確認自己的 branch 狀態
↓
確認 dev 的最新狀態
↓
看 diff
↓
理解兩邊修改
↓
決定怎麼整合
↓
處理 conflict
↓
重新驗證

至於自己的 branch 要怎麼和 dev 同步,有時候可能會用 merge,也可能會用 rebase。

這些在課程裡都有學過,我在 PawPal 裡也實際使用過。

但現在我比較不會只記:

branch 落後 dev,就一定要用某一個方法。

而是先確認目前的狀態,再決定怎麼處理。


conflict 消失,還不代表結束

這也是我後來很在意的一件事。

把 conflict 解掉之後,我不會只看到 Git 顯示可以繼續,就覺得完成了。

我還會做兩件事:

重新跑網站
+
再看一次 diff

重新跑網站,是確認功能有沒有被我解壞。

再看一次 diff,則是確認:

我剛才實際改了哪些東西?

有沒有不小心刪掉原本需要的程式?

因為:

Conflict Resolved

和:

功能正常

其實是兩件不同的事。

Git 可以知道衝突已經被處理。

但它不知道按鈕還能不能按、資料還能不能正常顯示,也不知道隊友原本完成的功能是不是還存在。

這些都還是要自己重新確認。


我後來比較不怕 conflict 了

回頭看,第一次真的遇到 merge conflict 時,我怕的其實不是 Git 指令。

我真正怕的是:

自己的判斷會影響到隊友已經完成的東西。

但也是因為真的遇過幾次,我後來比較不會看到 conflict 就緊張。

現在我會把它理解成:

Git 發現兩邊都有修改,但不知道我們真正想留下什麼。

所以真正重要的不是趕快把 conflict 消掉。

而是:

理解差異
↓
做出整合
↓
確認結果

我後來才明白:

merge conflict 真正考驗的,不是會不會按下某一個按鈕,而是能不能看懂彼此的修改,再安全地把它們放在一起。

這也是我在 PawPal 第一次真正感受到「團隊開發」和自己寫程式最大的不同。


下一篇預告

當分支、PR、Code Review 和 conflict 都慢慢處理完,專案也開始越來越接近真正上線。

但我後來才發現:

程式在自己的電腦可以跑,不代表放到正式環境也會一樣。

下一篇,我想聊我在部署前開始遇到的另一個問題:環境變數與不同環境的設定。

下一篇:

Day 23|部署前的最後一哩路:環境變數與設定檔。


上一篇
Day 21|Code Review:不是挑毛病,而是讓我們一起變強。
系列文
從看不懂到做出來,用 PawPal 走過前端新手村22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言