跳到主要內容

發表文章

【Git 救援篇】不小心 git reset --hard?別慌:用 reflog 與底層物件找回丟失的 Commit / [Git Recovery Guide] Accidentally Ran git reset --hard? Rescuing Commits with Git Reflog

在終端機按下 Enter 的瞬間,許多工程師都曾有過心臟漏跳半拍的經歷:原本只想清除暫存,手指卻下意識敲出 git reset --hard HEAD~1 ,剛剛花了一下午寫完並提交的功能代碼,瞬間在編輯器視窗裡消失得無影無蹤。 The moment Enter is pressed, many developers experience that stomach-dropping panic: intending only to clear staged files, fingers muscle-memory typed git reset --hard HEAD~1 , instantly wiping an entire afternoon of newly committed code from editor buffers. 請先深吸一口氣,停下鍵盤上的所有強制寫入指令。Git 的底層設計並不是像磁碟碎紙機一樣立即覆寫資料;事實上,只要那個修改曾經被 git commit 記錄過,你的程式碼絕大多數時候都依然毫髮無傷地躺在硬碟的物件資料庫裡。 Take a deep breath and immediately step away from destructive write commands. Git's underlying architecture is not a shredder that overwrites disk blocks immediately; if your work was committed even once, the files almost certainly remain intact inside the local object store. 圖一|reset --hard 僅後移分支與 HEAD 指標,Commit 物件仍完整保留於物件庫中(原創架構示意圖,非產品截圖) Figure 1 | reset --hard only moves branch and HEAD pointers; the commit object remains intact in the object database (Original architecture ...

Git 顯示 detached HEAD,不是壞掉:先替實驗提交留一個分支 / Detached HEAD Is Not Broken: Name the Commit You Want to Keep

現象:看到 detached,不代表壞掉 / Detached does not mean broken 假設你為了查看舊版本,執行 git switch --detach HEAD~1,終端機卻跳出 detached HEAD。畫面裡的「detached」很像檔案壞了,其實 Git 只是告訴你:現在的 HEAD 沒有掛在任何分支名稱上。你仍能查看檔案、修改內容,甚至提交;真正要留意的是,新提交暫時沒有分支名稱替它保留位置。 Suppose you inspect an older revision with git switch --detach HEAD~1 and the terminal reports a detached HEAD. “Detached” sounds like something broke, but Git is only saying that HEAD is not attached to a branch name. You may still inspect files, edit, and commit. The part that needs attention is that a new commit is not yet retained by a branch name. HEAD 與分支原本怎麼連 / How HEAD normally follows a branch 平常在 main 上工作時,HEAD 是符號參照,也就是它先指向 refs/heads/main,而 main 再指向某個提交。建立新提交後,Git 會把 main 移到新提交,HEAD 因為跟著 main,也間接移動。官方資料模型則把 detached HEAD 定義成另一種合法狀態:HEAD 直接保存提交 ID,沒有目前分支。[1][2] While working on main, HEAD is normally a symbolic reference: it points to refs/heads/main, and main points to a commit. Creating a commit moves main to the new commit, so HEAD follows indirectly. Git’s off...

為什麼 free 很小,Linux 卻還能開程式?看懂 MemFree 與 MemAvailable / Why a Tiny free Column Does Not Mean Linux Cannot Start Programs

現象:free 很小,不代表開不了程式 / A tiny free column is not “cannot start” 假設你在 Linux 終端機打出 free,free 欄只剩幾十 MB,used 卻佔了一大半。很容易先想到「記憶體爆了,得重開或加 RAM」。先停一下:這個畫面常常不是實體記憶體已經沒了,而是把「完全沒被用到的頁」,當成「還能開新程式的空間」。 Suppose you run free in a Linux terminal and the free column is down to a few dozen megabytes, while used takes most of the row. It is easy to jump to “memory is exhausted; reboot or add RAM.” Pause first: that picture often does not mean physical memory is gone. It means idle pages have been mistaken for the space still available to start a new program. /proc/meminfo 的 MemFree,依 Linux 7.4-rc1 的 proc 文件,是目前的 free RAM;在 highmem 系統上,它是 LowFree 與 HighFree 的和。它變小,只表示幾乎沒有完全閒置的頁。核心會把讀過的檔案留在記憶體裡,這份快取叫 page cache,也就是頁快取。那些頁還佔著 RAM,所以 MemFree 下降,但它們不是某個程式私有、放不掉的佔用。[1] In Linux 7.4-rc1’s proc documentation, MemFree in /proc/meminfo is the current free RAM; on highmem systems it is the sum of LowFree and HighFree. A small value only means there are almost no completely idle pages. The kernel keeps files it has re...

改了 DNS 記錄,為什麼還有人看到舊網站?看懂 TTL 與快取 / Why Your DNS Change Is Not Live Yet: TTL and Caching

現象:改是改了,答案還是舊的 / The record changed, yet old answers persist 假設你把網站搬到新主機,在 DNS 管理介面把 A 記錄改成新 IP,自己測試也正常;幾分鐘後同事回報,看到的還是舊網站。先別急著把記錄改第二次:可能不是改失敗,而是查詢本身沒有走到你修改的那台伺服器。 Suppose you move a site to a new host and update its DNS A record in the management console; your own test works, yet minutes later a colleague reports the old site is still being served. Do not rush to edit the record a second time: the change may not have failed — the query itself may never have reached the server you edited. 在終端機輸入網址,多數情況不是直接去問「管這個網域的伺服器」,而是交給遞迴解析器代問,例如寬頻業者的解析器或公共 DNS。依 RFC 1034,DNS 定義了快取所取得資料的程序;解析器替很多使用者共用同一份答案,你問到的可能是幾分鐘前別人問過的副本,而不是最新狀態。[1] When you type a hostname, you are usually not addressing the servers that manage the zone directly. A recursive resolver asks on your behalf — the resolver of your broadband provider or a public DNS, for example. RFC 1034 defines procedures for caching retrieved data, so a resolver serves the same copy of an answer to many users: what you receive may be a copy fetche...

Linux 刪了日誌,磁碟為什麼還是滿的?看懂開啟中的檔案 / Why Deleting a Linux Log May Not Free Disk Space

刪掉的是什麼? / What was actually removed? 假設你在 Linux 伺服器刪掉一個很大的舊日誌,目錄裡已經找不到它,磁碟用量卻幾乎沒變。先別急著再刪一輪:檔名消失,不一定代表資料已經釋放。本文聚焦一種常見原因,也就是仍有程序開著那個檔案,不把所有空間問題都歸到同一個答案。 Imagine deleting a large old log on a Linux server. It disappears from the directory, yet disk usage barely changes. Resist deleting more files: a missing name does not necessarily mean the data has been released. This article focuses on one common cause—a process still has the file open—not a universal explanation for every space problem. 依 Linux 系統呼叫手冊,unlink 移除的是檔案系統中的一個名稱。只有當它是最後一個硬連結,而且沒有程序開著檔案,佔用的空間才會供重新使用。硬連結可以理解成同一份檔案的另一個名字;刪掉其中一個名字,並不是刪掉其他名字。[1] According to the Linux system-call manual, unlink removes a name from the filesystem. The space becomes reusable when that was the last hard link and no process has the file open. A hard link is another name for the same file; removing one name does not remove the others. [1] 程序開啟檔案後,會取得檔案描述符,也就是用來操作已開啟檔案的編號。即使最後一個名字被移除,只要仍有描述符指向它,檔案就會繼續存在,直到最後一個相關描述符關閉。因此,目錄中的名稱與程序手上的開啟狀態,要分成兩條線來看。[1][2...

腳本裡 cd 成功,終端機卻沒移動?看懂 Bash 的工作目錄 / Why cd in a Script Does Not Move Your Terminal

目錄怎麼又回去了? / Why did the directory revert? 假設你在 Linux 的 Bash 終端機裡執行一支腳本,裡面明明寫了切換目錄,腳本也沒有報錯,結束後卻還停在原處。這通常不是路徑失靈,而是你把「另一個 shell 的工作目錄」,當成了眼前這個 shell 的狀態。 Imagine running a script in a Bash terminal on Linux. The script changes directory without an error, yet your terminal remains where it started. Usually the path is not broken: you have mistaken another shell’s working directory for the state of your current shell. 工作目錄是程序用來解讀相對路徑的起點,不是整台電腦共用的一個游標。以 bash go.sh 執行腳本時,新的 Bash 會繼承起始目錄;它之後呼叫 cd,只改變自己的工作目錄,並不把變更回寫給啟動它的 shell。[1] The working directory is a process’s starting point for resolving relative paths, not a computer-wide cursor. Running bash go.sh starts another Bash that inherits the initial directory. Its subsequent cd changes its own working directory, without writing that change back to the shell that launched it. [1] 圖一|繼承不等於回寫:腳本結束,外層仍在原處。原創示意圖,非產品或實測截圖。 / Figure 1 | Inheritance is not write-back: the outer shell stays put after the script exits. Original diagram, not a product ...

Docker 容器為什麼不像一台完整電腦?從共用核心看懂隔離 / Why a Docker Container Is Not a Whole Computer

假設你準備接手一個小網站,筆電上已經裝了一套 Python,專案卻需要另一個版本。光想到調整環境,就有點想先去倒杯水。容器吸引人的地方,正是讓應用程式連同需要的檔案一起安排好;不過,這個看起來獨立的小空間,究竟獨立到哪裡? Imagine taking over a small website. Your laptop already has Python installed, but the project needs another version. Just thinking about adjusting the environment might send you off for a glass of water. A container helps by packaging an application with the files it needs. But how independent is this seemingly separate space? 先看看,箱子底下是什麼 / Look beneath the boxes 在一般 Linux 容器的情境裡,容器內可以有自己的應用程式、函式庫與檔案配置,執行時卻共用承載它的 Linux 核心。虛擬機器則可以有自己的客體核心。把兩者都畫成一個方框很方便,卻容易漏掉這個關鍵差別。 [1] In a typical Linux container setup, each container can have its own application, libraries, and filesystem layout while sharing the Linux kernel that runs it. A virtual machine can have its own guest kernel. Drawing both as simple boxes is convenient, but it hides this essential difference. [1] 圖一|先找核心在哪一層,比只看方框更容易理解差異。原創簡化示意圖,非產品截圖。 / Figure 1 | Locate the kernel before comparing the boxes. Original simp...