Search and replace, vim and git


Git provides some nice utility functionality that can make a vim development environment much nicer. With recent updates to Vim it can now execute a command for every item in the quickfix list (:cdo) and location list (:ldo). These features work together quite nicely.

This new functionality has been introduced to vim from version 7.4.858 and is therefore available in vim8 and also available in neovim.

The Old Way

Back in 2011 I wrote a post about doing search and replace across multiple files with just the basic vim functionality. It’s quite common to use the arg list because its easy to add to:

:arg *.js
:arg `git ls-files`

and it’s easy to execute commands:

:argdo %s/foo/bar/gc

The New Way

Now that we can execute a command from the quickfix and location lists it is much easier to make larger, more controlled edits with the help of other features or plugins.

If you use syntastic or neomake for linting, a compiler via the :make command, the built in :grep or :vimgrep commands or any number of other plugins that integrate with these lists, such as vim-fugitive, you can take advantage of :cdo and :ldo.

:grep foo **/*.js

Then you can execute commands like this:

:cdo s/foo/bar/gc

Git Is Awesome

Git is incredibly powerful and I often use a couple of features that are often overlooked:

  • git ls-files - recursively list all the files in the repo
  • git grep - grep through all the files in the repo

One of the reasons these commands are so good is because by default they will ignore the items in the .gitignore file.

I tend to work with javascript a fair bit and having an easy way to avoid those nasty node_modules directories readily available is great! Best of all, these tools are available on just about every machine I work, on including the machines of other peoples that I pair with.

My Workflow

I use ctrl-p in vim and rather than relying on yet another third party tool like ack or ag, I’ve configured crtl-p like this:

let g:ctrlp_user_command = [
\  '.git',
\  'cd %s && git ls-files . -co --exclude-standard',
\  'find %s -type f'
\]

It is super fast and I haven’t come across a situation where I need ctrl-p for a large enough non-git repo and find wasn’t good enough for… yet!

If you use vim-fugitive, there is also a :Ggrep command that behaves much like the builtin :grep command, but it will ignore the .gitignore items. I use this quite a lot too:

:Ggrep foo
:Ggrep foo **/*.js

Then you can execute commands in the same manner as above:

:cdo s/foo/bar/gc

Because I use this so often, I’ve added the following mapping to my config to automatically search for the token under my cursor:

map <leader>g :Ggrep!  <Bar> copen