A Better Way to Write Commit Messages in VS Code
Visual Studio Code is my favorite software application. (Is that weird?)
Here are some things it’s got going for it:
- It’s extensible!
- It’s cross-platform!
- It’s open source!
- It’s got amazing release notes! (Seriously—reading the notes for each release is a highlight of every month. Is that weird?)
It also has the best Git GUI that I’ve used.1 Being, as I am, a staunch advocate of the command line,2 I was surprised to find myself more and more consistently opening VS Code to review and stage non-trivial commits, even when I had done the actual editing in a different application. And sure, it’s all well and good to leverage the strengths of multiple applications in concert; but the trouble was, as much as I appreciated VS Code for preparing commits, I found the experience of actually writing commits to be an exercise in frustration.
It’s fine if you’re writing a one-line message. It even tells you if you exceed the traditional 50 character limit! But for anything longer than that—especially when adhering to the tenets of conventional formatting—it starts to feel substantially inferior to good ol’ Vim; and if my changes are substantial enough that I’m using VS Code to review them, odds are I’m going to have a few things to say. I may be willing to use one application to write code and another to stage commits, but firing up a third just to write commit messages is crossing a line.
I’m clearly not alone in this opinion, as there’s been a Github issue open since 2017 requesting the ability to write commits with a full editor tab, like any other file. It finally saw some momentum in April with the appearance of a pull request; and, indeed, I’ve held off writing on this topic in the expectation that it would soon be merged and such a post would be immediately redundant. Alas, four months (and four releases) have now elapsed, with no indication that the feature will be integrated anytime soon, so I have decided at last to share my workaround.
In January, VS Code v1.42
introduced user-level tasks; in March, I realized I could use one to
invoke git commit
using VS Code as the editor:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "git commit --verbose",
"type": "shell",
"command": "GIT_EDITOR=\"code --wait\" git commit --verbose",
"presentation": {
"echo": false,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": false
},
"problemMatcher": []
}
]
}
The GIT_EDITOR=\"code --wait\"
bit
temporarily sets an environment variable which tells Git to use VS
Code as its default editor, rather than my actual default
(which is Vim). Unfortunately, this is a Bash convention that
doesn’t work in Windows, which I am forced to use
at work, and so for the next five months I patiently continued to
execute my awkward dance between Visual Studio, VS Code, and the
terminal. Then finally, in July, I chanced upon the realization that
I could use a process
task rather than a
shell
task, and pass the GIT_EDITOR
variable in a shell-agnostic fashion:
{
"label": "Git: Commit",
"detail": "git commit --verbose",
"type": "process",
"command": "git",
"args": [
"commit",
"--verbose"
],
"options": {
"env": {
"GIT_EDITOR": "code --wait"
}
},
"presentation": {
"echo": false,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": false
},
"problemMatcher": []
}
And there was much rejoicing.
The really neat thing about this approach—and the one
reason why it might stay relevant if and when that pull
request finally merges—is that you can change up the
args
for different scenarios. For instance, I added
another task (otherwise identical) to amend my last commit:
"args": [
"commit",
"--verbose",
"--amend"
],
Occasionally, I’ll receive some updated code,
documentation, or configuration files from a coworker or contractor
unfamiliar with Git, so I made another task that prompts me for an
author. This one is paired with an inputs
array
elsewhere in the file, outside of the
tasks
array:
"tasks": [
... other tasks ...
{
"label": "Git: Commit as Different Author",
... same content as above ...
"args": [
"commit",
"--verbose",
"--author",
"${input:gitAuthor}"
],
... same content as above ...
}
],
"inputs": [
{
"id": "gitAuthor",
"description": "Commit Author:",
"type": "promptString"
}
]
When I invoke this task, an input box appears at the top of the window, allowing me to enter the name of whomever actually wrote the changes which I’m committing on their behalf.
For the best experience, you can add a custom keybinding to invoke a specific task, or just to pull up the list of tasks. I repurposed Ctrl+Shift+T to open the task list (T for task), as running tasks is something I do far more often than reopening closed editors.
I hope that someday, the option to use a full editor tab for commit messages will indeed be part of VS Code’s native Git experience. In the meantime, however, my humble tasks are working great!
Footnotes
Caveat: the only other Git GUI I’ve used is Visual Studio’s. Also, very briefly, the GUI that comes with Git for Windows. ↩︎
I really really like typing, okay? ↩︎