位元詩人 我忘了 git pull 就開始重構專案:一場由「偷懶」引發的血淚史

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

楔子

我手邊有一個 JavaScript 工具程式,採取 Node.js CLI 與 ES module 雙模式運行。

雖然目前跑起來沒問題,但我心中始終有個念頭:把它重構成 Erasable TypeScript。一來是為了讓其他 TypeScript 專案調用時更方便,二來是為了未來的擴充性——誰說這東西以後只會待在瀏覽器裡呢?

暴風雨前的寧靜

下定決心後,我開始了熟練的「轉換儀式」:修改副檔名、安裝 NPM 開發依賴、配置 tsconfig.json,以及最關鍵的——為每個變數加上靈魂(型別定義)。

中間發生了一個小插曲:clipboardy 在 WSL 環境下有些相容性的小脾氣。為了解決它,我果斷刪除了 Windows 端的老舊副本,確保它能按照原本的意圖乖乖運作。

經過一番千辛萬苦,看著毫無紅字的程式碼,我對成果感到非常滿意。輸入 git push,我已經準備好去泡杯咖啡犒賞自己了。

風暴降臨

[rejected] —— 螢幕上跳出斗大的紅色錯誤訊息。

那一瞬間,我甚至還沒反應過來發生了什麼事。盯著訊息看了一會,發現是遠端分支已有更新。我直覺地認為「這應該只是普通的 fast-forward 問題」,於是草草調整了 Repo 設定並嘗試 Merge。

失敗。這時,我的頭皮開始微微發燙。

「好,那硬上 git pull --rebase 看看吧。」

這一次,Git 看起來安分多了。我回到 VS Code,快速掃描了一下衝突的地方。「小 Case 嘛!」我自信滿滿地處理掉衝突,再次執行 git push。這次,成功了。

肆虐成災

為了確保萬無一失,我在命令列跑了一下程式,運行正常。然而,當我打開檔案準備欣賞漂亮的新代碼時,我整個人僵住了。

「我的型別呢?」

原本寫好的型別資訊憑空消失,更詭異的是,程式碼變回了修改前的樣子,但目錄名稱卻又是新的。我這才驚覺:剛才在解衝突時,我不小心讓「舊的 Code」覆蓋掉了「新的 TypeScript」。

現在的 Repo 處於一種詭異的「量子疊加態」:結構變了、檔案還在,但內容卻退化回了原始狀態。

災後重建

此時的專案已經陷入混亂:VS Code 裡未儲存的暫存檔、Git Commit 裡的殘骸,我已經分不清楚誰是誰了。

看著這團亂麻,我決定快刀斬亂麻。我直接刪掉整個在地 Repo,重新 git clone 一份乾淨的代碼。然後——是的,我重新手動轉了一次 TypeScript。

再次上傳成功後,我看著那慘不忍睹、充滿修補痕跡的 Git Log。幸好這只是個個人專案,如果是公司專案,大概會被 Senior 工程師電到飛起來。

反省:關於開發者的自我修養

事後回想,這次的災難來自於多個層面的疏忽:

  1. 重構的必要性:或許我根本不該為了重構而重構。如果只是要提供型別支援,寫個 .d.ts 檔或許是更經濟的做法。
  2. Git 基本功不足:面對非預期情境(Non-fast-forward)時,習慣性地想用指令快速跳過,卻沒有真正掌握 Repo 的真實狀態。
  3. 對 Conflict 的輕蔑:處理衝突時,「直接選一個版本」是最危險的行為。沒有細看差異,就是對程式碼的不負責。

結論: 開工前先 git pull 不是建議,是保命符。

關於作者

位元詩人 (ByteBard) 是資訊領域碩士,喜歡用開源技術來解決各式各樣的問題。這類技術跨平台、重用性高、技術生命長。

除了開源技術以外,位元詩人喜歡日本料理和黑咖啡,會一些日文,有時會自助旅行。