跳至主要內容
技術

VS Code 只 commit 當下這個檔案:兩個指令就搞定

VS Code 只 commit 當下這個檔案:兩個指令就搞定
VS Code 開發環境 第 1 / 1 篇 ,前往系列總覽

本篇是「VS Code 開發環境」系列的第 1 / 1 篇。你可以從系列總覽開始閱讀,也可以直接接著看本文。

有時候改好一個檔案,想要馬上把開著的這一份檔案 commit 掉。

但又不想移開鍵盤,去 Source Control 面板裡用滑鼠找檔案、點按鈕。

只 commit 當下開啟的檔案

命令面板兩個指令就夠:

⌘⇧P → Git: Stage Changes     # 只 stage 目前作用中的這個檔案
⌘⇧P → Git: Commit Staged     # 只 commit 已 stage 的內容

先講結果:第一步沒 stage 到東西時,第二步會做什麼,取決於兩個設定。

git.enableSmartCommitgit.suggestSmartCommitstage 區是空的,執行 Commit Staged
false(預設)true(預設)跳對話框,問要不要把全部變更一起 commit
falsefalse什麼都不做,也不提示
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 });
}

CommitCommit 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

按下 YesAlways 之後,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 才是承諾本身。

留言討論

esc
輸入關鍵字搜尋文章...
查看收藏 →