昨天 Git 資料夾搬家一文發表後,讀者 Danny Lin 於小站留言提到一則重要觀念:

事實上 Git 並沒有記錄所謂的「搬檔/更名」動作,在 Git 眼中,不管搬檔案或檔案改名,都一律被視為刪除舊檔外加新增檔案,差別在於若刪除檔案與新增檔案的內容相似度達一定門檻(預設為50%),Git 就會將這組刪除加新增動作解讀為更名。而且,相似度門檻是活的,可以任意調整。

我做了幾點實驗驗證這點。

實驗一,git status 的新增加刪除,commit 後視為 rename

  1. 直接用 DOS 指令將 src/ExampleForDummy.sln 移到上一層目錄
  2. git status 查看為一個刪除動作
  3. 加一個新增動作
  4. 將刪除與新增動作 Commit 進版控
  5. delete + add 動作被解讀成 rename,數字 100% 表示刪除與新增內容完全相同

實驗二,同時搬移多個內容相同檔案

  1. 建立 a.txt
  2. 將 a.txt 複製成 b.txt,二者內容應完全相同
  3. 將 a.txt 與 b.txt Commit 進去
  4. 將 a.txt 與 b.txt 移到上一層目錄
  5. Commit 搬家後的結果,Git 沒被 a.txt 與 b.txt 內容相同混淆,成功判讀二者的搬家前後關聯(應該是用檔名輔助判別)
  6. R100 的 R 表示 Rename,100 代表 100% 相同

實驗三,更改檔名並且改掉部分內容,動態指定更名門檻

  1. 建立 a.txt,內容為 12345678 共 8 Bytes
  2. Commit a.txt 進 Git
  3. 將 a.txt 更名為 b.txt
  4. 在 b.txt 後方加上 ABC 三個字元
  5. Commit b.txt,Git 判定這算是更名,內容相似度 66%
  6. git diff 也判定這次修改是 src/a.txt 更名 src/b.txt (預設門檻 50%,相似度 66% 視為更名)
  7. git diff 加上 -M80% 參數,將更名門檻提高到 80%,則同一個 Commit 修改變成一刪一增

結論

由以上實驗可知,對 Git 而言,更名或搬檔只是一組刪除與新增相似內容的動作,git status 顯示 renamed 或 deleted + untracked 並不影響 Commit 後的判別,而相似度門檻預設為 50%,但門檻是活的,每次 git diff、git log 時可透過 -M 參數指定。所以前一篇提到使用 git mv 確保 status 呈現 renamed 狀態並絕對必要性,只要確保該次 Commit 只有搬檔或更名動作,不要摻入其他修改,Git 都可正確判讀。

覺得這算是一則冷知識的理由是,網站上真有不少用 git mv 搬檔避免 git status 出現 deleted + untracked files 討論,足見還有蠻多人不知道的,多虧 Danny Lin 補充。而這也更讓我覺得 Git 要上手不簡單,想精通更難,呵。

Explanation of how Git decide the delete + add actions are a rename or move action.


Comments

Be the first to post a comment

Post a comment