James Dyer: Stashing a Single File, and Why I Was Too Quick to Blame vc-mode!

Wait 5 sec.

In my last post I wrote about finally caving in and adding Magit to my config after years of being a contented vc-mode loyalist (VC-Mode Meets Magit - or Why I Finally Gave In!). My conclusion then was that the two complement each other nicely, Magit for repo-level operations, vc-mode for the everyday file-level stuff, commit a single file, a blame, a quick diff, C-x v and away you go. Well, I have just had a small but instructive lesson in not assuming where the limitations actually lie, because I caught myself about to reach for Magit for something vc-mode handles perfectly well!The scenario: I had a pile of changes sitting in my working tree across a few files, and I wanted to set aside the changes in just one of them, temporarily, without touching anything else. Stash a single file, in other words.I knew that vc-mode had some stash commands through the "z" keybinding when in vc-dir, and of course you can find them through M-x by completing on vc-git-stash*, but I had only ever used it for stashing everything, straight from the worktree. So I did a quick test: in vc-mode, I moved my point over a single modified file and selected vc-git-stash, but this of course would stash everything! Well, how about a single file? I assumed that vc-mode couldn't do it and reached for magit instead!However a little niggling doubt surfaced and I decided to have a little rummage around the vc-git code:(defun vc-git-stash (name) "Create a stash given the name NAME." (interactive "sStash name: ") (let ((root (vc-git-root default-directory))) (when root (apply #'vc-git--call nil "stash" "push" "-m" name (when (derived-mode-p 'vc-dir-mode) (vc-dir-marked-files))) (vc-resynch-buffer root t t))))It runs git stash push -m NAME and, if you are inside a vc-dir buffer, it appends the list of marked files as a pathspec. Which means a single-file stash is built right in, no Magit required!Open vc-dir with C-x v d (or C-x p v for the project-wide view)Move point to the file you want to set aside and press m to mark itPress z c, type a stash name, hit returnAnd that is it, git stash push -m "your name" -- the/marked/file runs under the hood, that one file's changes are tucked away, and everything else in your working tree is left exactly as it was. Mark two files and it stashes two, mark none and it stashes the lot, the marking is the whole mechanism.To bring it back, z p pops a stash (it prompts you with a completing-read list of what is there)This is almost the inverse of my last git post. Last time I went looking for vc-rebase and there genuinely was not one, a real git-specific gap that vc-mode does not fill, and off to Magit I rightly went. This time I assumed the same shape of limitation and assumed wrong, the feature was sitting there in vc-dir the whole time (and actually was pretty obvious really),So, vc-mode really is quite capable for file-level git work, single-file stashing very much included, and I should be slower to assume "git-specific" means "Magit only". Magit is still there for the repo-level heavy lifting, but for tucking one file out of the way, C-x v d, m, z c, done, no need to leave the comfort of the built-in tooling at all.I do enjoy these little corrections, each one teaches me a bit more about where the seams in Emacs version control actually are, and occasionally, that the seam I thought I had found was never there in the first place!