楔子
我手邊有一個 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 工程師電到飛起來。
反省:關於開發者的自我修養
事後回想,這次的災難來自於多個層面的疏忽:
- 重構的必要性:或許我根本不該為了重構而重構。如果只是要提供型別支援,寫個
.d.ts檔或許是更經濟的做法。 - Git 基本功不足:面對非預期情境(Non-fast-forward)時,習慣性地想用指令快速跳過,卻沒有真正掌握 Repo 的真實狀態。
- 對 Conflict 的輕蔑:處理衝突時,「直接選一個版本」是最危險的行為。沒有細看差異,就是對程式碼的不負責。
結論: 開工前先 git pull 不是建議,是保命符。