M12 知識的版本管理
- 說出為甚麼純文字(Markdown)比 Word 更適合版本管理;
- 用一句一行的寫法,讓 diff 和合併都清楚;
- 判斷哪些檔案進 Git、哪些不應該,以及大檔案怎樣處理;
- 用 Git 管理 Obsidian 等筆記庫,並分清 Git 和雲端同步。
1知識也需要版本管理 Why version knowledge
M0 的三個問題,在知識工作裏一樣存在:論文改了十幾稿,想找回導師說「這段寫得好」的那一版;講義每學期修訂,想知道某個定義是何時、為何改的;三個人合寫報告,不想再用「最終版_v3_真的最終」。Git 把這些都變成命令:git log、git blame(M14)、git diff、PR(M10)。
2純文字勝過 Word Plain text first
| Markdown/純文字 | Word(.docx)、PowerPoint | |
|---|---|---|
| Git 看到的是 | 一行一行的文字 | 一個壓縮檔(內含 XML),改一個字整個檔案都不同 |
git diff | 清楚列出改了哪幾句 | 只說「二進位檔案不同」 |
| 合併 | 逐行三方合併,衝突可讀 | 無法合併,只能二選一 |
| 倉庫大小 | 每版只多存改動過的小檔 | 每存一次就多一份完整的檔案 |
| 輸出 | 可以轉成 Word、PDF、網頁(如 Pandoc) | 本身就是成品 |
實用的分工:寫作和修訂用 Markdown,交付時才轉成 Word 或 PDF;成品不必進 Git,或只在正式版本時附在 release 上(M11)。
3一句一行 One sentence per line
M5 已經看到:Git 以「行」為單位比較和合併。文章若一段寫成一行,改一個字,整段都會顯示為改動;兩個人改同一段的不同句子,也一定衝突。改成一句一行,diff 只標出真正改了的那句;兩人改的句子之間隔着至少一行未改動的句子,Git 就能自動合併。
但一句一行不是萬靈丹:Git 把相鄰兩行的改動視為同一處修改,所以一人改第 2 句、另一人改第 3 句,仍然會衝突;改第 2 句和第 4 句,才會自動合併。好處是衝突範圍只有那兩句,而不是整段。
git diff --word-diff-regex=.(或 git diff --color-words=.)逐字顯示改動。注意不要只寫 --word-diff:它預設以空格切分「詞」,而中文句子之間沒有空格,整段會被當成一個詞,結果和普通 diff 差不多;=. 的意思是「每一個字元算一個詞」。不過合併時 Git 仍以行為單位,所以新寫的文件最好一句一行。
east_asian_line_breaks 擴充(pandoc -f markdown+east_asian_line_breaks …)才會去掉中文之間的空格。反過來,有些編輯器會把單一換行顯示成真正的換行,令一句一行的段落看起來每句一行;Obsidian 的設定裏有「Strict line breaks」選項控制這一點。採用一句一行之前,先在自己的編輯器和輸出工具試一段,確認顯示和輸出都是你要的效果。
4大檔案與媒體:放對地方 Large and binary files
M3 說過,物件一旦寫入就不會改變,也不會消失。所以一個 500 MB 的影片提交進去,即使下一個 commit 刪除它,它仍然永遠留在倉庫裏,每個人 clone 都要下載。二進位檔(Word、圖片、影片)每次修改都會多存一份完整的檔案。Git 打包(pack)時會嘗試把相似的物件存成差異(delta),二進位檔也不例外;但 .docx、.mp4、.jpg 本身已經是壓縮格式,改一點內容整個檔案的位元組都會變,所以幾乎省不了空間。
git lfs install # 每部電腦做一次(先安裝 Git LFS)
git lfs track "*.psd" # 寫進 .gitattributes
git add .gitattributes design.psd
git commit -m "加入設計原稿(LFS)"
git lfs track 只是在 .gitattributes 加了一行,告訴 Git 這類檔案要經 LFS 處理:
*.psd filter=lfs diff=lfs merge=lfs -text
提交後,倉庫裏的 design.psd 其實只是一個三行的指標檔(用 git show HEAD:design.psd 可以看到);真正的內容按 oid 存在 LFS 伺服器:
version https://git-lfs.github.com/spec/v1
oid sha256:d7eac9f7…(內容的 SHA-256)
size 200000
- 已經提交了才想起:
git lfs track只影響之後的提交,舊 commit 裏的大檔仍在。要把歷史裏的檔案也換成指標,用git lfs migrate import --include="*.psd" --everything;這會改寫歷史(M7),所有人都要重新 clone。 - LFS 有配額:GitHub、GitLab 等託管服務對 LFS 的儲存量和每月下載流量都有限額,超出要付費或會被暫停,大量影片仍然應放 NAS 或雲端硬碟。
5Obsidian 筆記庫+Git An Obsidian vault in Git
Obsidian 的筆記庫(vault)就是一個裝滿 Markdown 檔的資料夾,所以可以直接 git init。幾個要點:
- .gitignore 個人設定:
.obsidian/workspace.json和手機版的.obsidian/workspace-mobile.json(視窗佈局)每次開啟都會變,應忽略;外掛設定可以提交,讓幾部電腦一致(M2)。但要小心:有些外掛把 API 密鑰或登入資料存在.obsidian/plugins/外掛名/data.json,提交.obsidian/之前逐個看一遍,含密鑰的和.env一樣寫進.gitignore(見互動 3)。 - 改名和搬檔:在 Obsidian 裏改名,Obsidian 會更新所有
[[連結]];Git 只看到「舊檔不見了、多了一個新檔」,用git add -A把兩邊一起暫存。Git 並不記錄「改名」:commit 存的只是快照,git diff、git log --follow等命令在事後比較內容,相似度夠高(預設 50%)才判斷為改名。所以改名和大幅修改分開提交:同一個 commit 裏又改名又重寫大半內容,Git 只會看到刪除加新增,--follow也追不回舊歷史。 - 附件:截圖和 PDF 累積得很快,按第 4 節的原則決定哪些進倉庫。
- 提交節奏:寫完一個段落、整理完一個主題就提交一次,說明寫「整理了甚麼」(M2);可以用 obsidian-git 外掛定時提交和推送,但自動提交的說明往往空泛。
- 多部電腦:每部電腦 clone 同一個遠端,開始寫之前先
git pull,寫完git push(M9)。
6Git 與雲端同步 Git vs file sync
| Dropbox、iCloud、Syncthing | Git | |
|---|---|---|
| 何時同步 | 自動、即時 | 你決定(commit、push、pull) |
| 歷史 | 有限的舊版本,沒有說明 | 完整歷史,每次改動有說明 |
| 兩人同時改同一檔 | 產生「衝突副本」(如 .sync-conflict 檔),要人手比對 | 逐行合併,衝突處清楚標示 |
| 適合 | 個人檔案在幾部電腦間同步、大型媒體、暫存區 | 需要追溯和協作的文字知識 |
.git 裏好幾個檔案(index、物件、分支指標)。同步工具不知道 Git 正在寫,可能把寫到一半的檔案傳到另一部電腦;兩部電腦都動過時,還會為分支指標等內部檔案產生 .sync-conflict 副本。結果是另一邊的倉庫缺物件、分支指向不存在的 commit,也就是壞了。實務上的分工是:Git 管知識庫,同步工具管其他資料夾;兩部電腦之間的知識庫靠 push/pull 傳遞。
7容易混淆的對照 Easily confused
- 「Git 只適合程式員。」它管理的是文字,任何以文字為主的工作都適用;介面可以用 GitHub Desktop、VS Code 或 Obsidian 外掛,不一定要命令列。
- 「刪掉大檔再提交,倉庫就會變小。」不會;舊 commit 仍保存着它。要真正移除須改寫整段歷史(例如 git filter-repo),而且所有人都要重新 clone。
- 「一句一行會讓文章變得零碎。」只影響原始檔;顯示和輸出時通常仍合成同一段,但中文之間會否多出空格、會否變成真換行,視乎工具和設定(第 3 節)。
- 「一句一行就永遠不會衝突。」改相鄰兩句仍會衝突;只是衝突範圍縮小到那幾句。
- 「Git 和雲端同步是同一回事。」同步工具自動把最新版複製到每部電腦,兩人同時改只會得到衝突副本;Git 由你決定何時記錄,保證每一版都找得到、知道為甚麼改,並能逐行合併(第 6 節)。兩者分工,不要管同一個資料夾。
- 「
git mv會把改名記錄下來。」Git 只存快照,改名是事後按內容相似度推斷的(第 5 節)。
8速查 Cheat sheet
| 命令 | 作用 |
|---|---|
git diff --word-diff-regex=. | 逐字顯示改動(中文要加 =.,每個字元算一個詞;適合整段一行的文字) |
git log --follow 筆記.md | 追蹤檔案改名前後的歷史 |
git mv 舊名.md 新名.md | 改名並暫存(等於搬檔後 git add 兩邊;Git 不另記「改名」) |
git lfs track "*.psd" | 指定由 LFS 管理的檔案類型(寫進 .gitattributes) |
git lfs migrate import --include="*.psd" --everything | 把已提交的檔案改由 LFS 管理(改寫歷史) |
git count-objects -vH | 查看倉庫佔用的空間 |
pandoc 報告.md -o 報告.docx | 從 Markdown 輸出 Word(另需安裝 Pandoc) |
9小測 Quiz
本單元重點 Summary
- 寫作用純文字(Markdown),交付時才輸出成 Word/PDF。
- 一句一行:diff 只標出改了的句子;兩人改的句子之間隔着未改動的一行,就能自動合併,改相鄰兩句仍會衝突。顯示效果視乎編輯器設定。
- 文字進 Git,媒體放儲存,倉庫留指標;需要版本的大檔用 Git LFS。
- Obsidian vault 就是一個資料夾:忽略個人設定、檢查外掛設定有沒有密鑰、改名和大幅修改分開提交、寫完一段就提交。
- Git 管知識庫,同步工具管其他東西,不要兩者管同一個資料夾。
延伸閱讀
- Semantic Line Breaks
- Git LFS
- Pro Git 8.2 Git Attributes(含讓 git diff 讀懂 Word 檔的方法)
- obsidian-git 外掛
- Pandoc