VS Code 只 commit 當下這個檔案:兩個指令就搞定
本篇是「VS Code 開發環境」系列的第 1 / 1 篇。你可以從系列總覽開始閱讀,也可以直接接著看本文。
有時候改好一個檔案,想要馬上把開著的這一份檔案 commit 掉。
但又不想移開鍵盤,去 Source Control 面板裡用滑鼠找檔案、點按鈕。
只 commit 當下開啟的檔案
命令面板兩個指令就夠:
⌘⇧P → Git: Stage Changes # 只 stage 目前作用中的這個檔案
⌘⇧P → Git: Commit Staged # 只 commit 已 stage 的內容
先講結果:第一步沒 stage 到東西時,第二步會做什麼,取決於兩個設定。
git.enableSmartCommit | git.suggestSmartCommit | stage 區是空的,執行 Commit Staged |
|---|---|---|
false(預設) | true(預設) | 跳對話框,問要不要把全部變更一起 commit |
false | false | 什麼都不做,也不提示 |
true | 任意 | 不問,直接把全部變更 commit |
坑一:Stage Changes 沒抓到檔案時,不會有任何提示
Git: Stage Changes 沒帶參數時,會去問「目前作用中的編輯器是哪個檔案」:
// extensions/git/src/commands.ts(VS Code 1.136.1)
private getSCMResource(uri?: Uri): Resource | undefined {
uri = uri ?? window.activeNotebookEditor?.notebook.uri ?? window.activeTextEditor?.document.uri;
問不到,就直接 return。
我實測過:作用中的分頁是擴充套件開的面板、不是文字檔時,執行 Stage Changes,檔案沒有被 stage,畫面上也沒有任何訊息。
這類面板不是文字編輯器,activeTextEditor 拿不到檔案。照同一段程式碼推,Markdown 預覽、設定頁這類非文字分頁應該也一樣,這幾個我沒逐一試。
坑二:Commit Staged 跟 Commit 走的是同一條路
名字裡寫著 Staged,很容易以為它是「安全版的 Commit」:沒 stage 東西就不動作。
我原本也這樣以為。翻了原始碼才發現不是:
@command('git.commitStaged', { repository: true })
async commitStaged(repository: Repository): Promise<void> {
await this.commitWithAnyInput(repository, { all: false });
}
Commit 和 Commit Staged 最後都進到同一個 smartCommit()。stage 區是空的時候,判斷條件裡看的是 !opts.all,而 all: false 剛好成立:
if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.all && !opts.amend) {
所以兩個指令跳出來的是同一個對話框:
There are no staged changes to commit.
Would you like to stage all your changes and commit them directly?
Yes / Always / Never
按下 Yes,我的 repo 會多一個 74 個檔案的 commit
按下 Yes 或 Always 之後,git.smartCommitChanges 的預設值是 all,VS Code 會執行:
git add -A -- .
-A 會把 untracked 檔案一起加進去。拿我寫這篇時的 repo 用 git add -A -n -- .(dry-run,只列不加)來算:
| 狀態 | 數量 |
|---|---|
| 已追蹤、有修改 | 4 |
untracked 檔案(git status 只顯示 41 項,因為整個資料夾算一項) | 70 |
| 按下 Yes 後會進 commit 的 | 74 |
我只想 commit 1 個檔案。
Always 更麻煩。它會把 git.enableSmartCommit: true 寫進 User Settings,之後遇到同樣情況就不問了,直接全部 commit。這段是讀原始碼得出的,我沒有真的按 Always 去試。
3 分鐘上手:兩個指令,加一條退路
最小可用流程:
1. 點回要 commit 的檔案,確認作用中的是文字分頁
2. ⌘⇧P → Git: Stage Changes
3. ⌘⇧P → Git: Commit Staged
4. 開出 COMMIT_EDITMSG 分頁 → 寫 message → 按分頁裡的 Commit 按鈕
5. 如果第 3 步跳出 "There are no staged changes" → 按 Esc,回到第 1 步
幾個補充:
- Source Control 輸入框裡如果已經有字,第 4 步會直接拿那段當 message,不開分頁。
- 只想 commit 其中幾行:選取後按
⌘K ⌘⌥S(Git: Stage Selected Ranges,內建快捷鍵)。 - 以前按過 Always 的話:到 User Settings 找
git.enableSmartCommit,改回false。
寧可沒反應、也不要誤 commit 的話,可以把對話框也關掉(對應上面表格第二列):
{
"git.enableSmartCommit": false,
"git.suggestSmartCommit": false
}
常用的話,再綁兩個快捷鍵(Preferences: Open Keyboard Shortcuts (JSON)):
[
{ "key": "cmd+alt+s", "command": "git.stage", "when": "editorTextFocus" },
{ "key": "cmd+alt+c", "command": "git.commitStaged" }
]
when: editorTextFocus 讓 stage 快捷鍵只在游標停在文字編輯器時生效,剛好避開坑一。
反思
技術面
Commit Staged 這個名字描述的是正常情況下它做什麼,沒有保證 stage 區是空的時候它不做什麼。邊界行為要看那個 if,不能看指令名稱。
「沒有提示」本身就是風險。Stage Changes 失敗時不出聲,真正出聲的是下一步的對話框,而它問的是要不要把全部變更帶走。
心態面
我把 Commit Staged 當成安全版的 Commit,理由只有一個:被指令名稱給誤導了。
如果沒有要寫成文章、去翻原始碼,這個誤解會一直留著,直到哪天某個 commit 裡多出 73 個不該進去的檔案。我對工具的信任,有一部分其實是建立在命名上。
有趣發現
Git 擴充內建的快捷鍵只有三個:Stage、Unstage、Revert,而且全都是「選取範圍」版本。最常用的整檔 stage 反而沒有預設快捷鍵,得自己綁。
Always 寫入的是 User Settings,不是這個 repo 的 Workspace Settings。在一個專案按一次,所有專案都跟著生效。
指令名稱是承諾的摘要,if 才是承諾本身。