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.
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.
1
:grep foo **/*.js
Then you can execute commands like this:
1
: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:
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:
12
:Ggrep foo
:Ggrep foo **/*.js
Then you can execute commands in the same manner as above:
1
: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:
I’m going to attempt to use netrw exclusively for a while instead of NERDTree.
Mainly because not everyone has NERDTree, but everyone I work with will
generally have netrw installed by default so I thought I should be become more
proficient with it.
This post should provide me something I can quickly refer back to when I get
stuck. Netrw has a lot of features and the help page is pretty comprehensive.
It’s worth a quick look, at least at the table of contents. It’s available by
typing :help netrw.
There is also a quick reference for the mapping by typing :help netrw-quickmap
and one for the commands by typing :help netrw-quickcom.
Navigation
Navigating around netrw is pretty intuitive, you use the arrows (or hjkl) to
move around and enter to open files or directories. These windows are just
like any other window in vim so most of the usual motions, etc. work except
netrw adds some default mappings. Here are the basics for navigation:
enter - Open files/directories - - Go up one directory u - Go back to previously visited directory (like <C-o> in vim) U - Go forward to subsequently visited directory (like <C-i> in vim)
enter works just fine to open files and directories, but there are some other
options available too:
o - Open file/directory in new horizontal split v - Open file/directory in new vertical split t - Open file/directory in new tab p - Preview file without (moving the cursor from netrw) x - Open the file/directory with the default system app
Appearance and Behaviour
One of the first things I noticed when making the switch was when you press
enter on a directory, instead of displaying the contents of the sub-directory
inline, it would replace the whole buffer with the contents of the
sub-directory.
This is because by default netrw doesn’t use a tree to display the
files/directories, its more like doing an ls but you can configure netrw to
print a tree and have the same behaviour as NERDTree with this mapping:
i - Cycle between different listing modes (one of them is tree mode)
In normal mode, enter will move into and show the given file/directory
In tree mode, enter will show the contents of the sub-directory in addition
While netrw doesn’t look as nice as NERDTree, it still has a lot of options to
customize the way it looks and works.
I - Toggle the banner c - Make the browsing directory the current working directory gn - Make the directory under the cursor the top of the tree gh - Toggle hidden files on or off a - Cycle between all files, not hidden files or just hidden files visible s - Cycle sort order between name, time or filesize r - Reverse sort order
File Operations
It’s also pretty easy to do basic file operations:
% - Create a new file d - Create a new directory D - Delete the file/directory under the cursor (or marked files/dirs) R - Rename/move file/directory
Copying files however is a little more involved. You need to mark the files you
want to copy, mark the destination, then execute the operation (a little
tedious):
mf - Toggle whether the file/directory is marked mt - Mark the directory under the cursor as the copy target mc - Execute the copy operation mu - Unmark all marked items
Commands
To open netrw is easy. You can use all the same commands you would use to open a
file except give them a directory. You can use the command line with vim . or
within vim with the commands below. These commands don’t even need a space
between it and its arguments, so its very few keystrokes:
:e. - Open the current directory normally :sp. - Open the current directory in a horizontal split :vs. - Open the current directory in a vertical split :tabe. - Open the current directory in a new tab
These will all work, unless you have installed an alternative explorer plugin
(like NERDTree), in which case you can explicitly open netrw with :Ntree.
Netrw also provides a lot of different ways to launch an explorer window. The
most common command is :Explore which will open netrw in the directory of the
currently open file, much like the :NERDTreeFind command. Here are some
variations of that command:
:Ex - Use current buffer if available, otherwise split horizontally :Ex! - Use current buffer if available, otherwise split vertically
:Tex - New tab, directory of currently open buffer
There is another variation that doesn’t use the directory of currently open
buffer but uses the current working directory. This is one is the most similar
to the default :NERDTree command.
:Lex - Vertical split full height, current working directory :Lex! - Vertical split, current working directory (opposite side)
All these commands can also take a directory as an argument.
Basic Configuration
I didn’t want to customize too much because that would defeat the purpose of the
learning the defaults, but I did want to turn off that banner (it was handy
having the basic operations on display, but I don’t think I need it anymore).
There is more to explore with netrw, such as working with bookmarks and pattern
listings, but this post should have enough of the basics to be productive.
Checkout :help netrw for more features.
Git v2.8 introduced a new configuration option called user.useconfigonly. This
option prevents git from trying to guess your email address to use for the
author metadata for a commit and forces you to have configured one.
This is great news because with a good workflow in place you can be sure you
always have the correct email addresses on your commits.
Global Configuration
If user.useconfigonly is set to true, you will need to configure an email
address. The simplest way is with this command:
Which will result in a configuration like this saved in ~/.gitconfig:
123
[user]
name = Steve Occhipinti
email = steve@example.com
This is great, but what if you want to work on personal projects and work
projects on the same machine?
Per Repository Configuration
Instead of using the global config in the home directory, git will first look
for a local configuration in ./.git/config. To set per repository
configuration, all you have to do is cd to the repo and run the same commands
but without the --global flag:
If you have a global configuration, all repositories will use that as a default
unless you remember to set a per-repository config. If you don’t set a global
config, git will normally guess what your email is and use that if its valid.
If you set the useconfigonly option and remove your global config, git will
not allow you to make a commit until you’ve setup your user details and this
will prevent accidentally using your work email address on a personal project or
vice-versa.
However, I do leave my name configured globally as that is not going to change
between repositories.
Visual Queues
Having that error appear when you haven’t setup an email address is great, but
rather than waiting for me to make a mistake, I added an exclamation mark
indicator to my shell prompt to remind me to set up the user details.
The basic idea is something like this:
123
local noauthor
git config user.email > /dev/null ||noauthor="!"export$PS1="$noauthor${PS1}"
Quicker Configuration
If this is something that I will have to do for every repo I create or clone, I
want to it be pain free. Git makes it really easy to create new sub commands
simply by putting a script named git-xxxx in the path, where xxxx is the
sub command name.
Now when go to a repo and I see my ! indicator, all I have to do is run git
author.
This allows me to put all my email addresses in a file and then select the one I
want from a nice menu. Note, I’m using pecorb here which is a
gem I wrote to generate selectable
menus on the command line, but there are plenty of
alternatives or you could
just use read in bash and type it in (which my script falls back to).
My Bash Prompt
The prompt I use may not be the tidiest, but its available
here.
Following on from a previous post about rewriting indentation for an entire git repo,
another reason to rewrite the history of a whole repo is to change the email
address or name mentioned in commits.
Once again, be aware that rewriting history will cause problems if other people
have clones of the repo so make sure you understand the risks before doing
this!
To change the author name and email address for every commit, you can run this:
123456
git filter-branch -f --env-filter " GIT_AUTHOR_NAME='YOUR_NAME' GIT_AUTHOR_EMAIL='YOUR_EMAIL' GIT_COMMITTER_NAME='YOUR_NAME' GIT_COMMITTER_EMAIL='YOUR_EMAIL'" HEAD
This will blindly replace all the commits with the details given, but if other
people have contributed, you may need to be a bit more careful and only replace
your own:
123456789101112
git filter-branch --commit-filter " if [ "$GIT_COMMITTER_NAME" = 'YOUR_ORIGINAL_NAME']; then GIT_AUTHOR_NAME='YOUR_NEW_NAME' GIT_AUTHOR_EMAIL='YOUR_NEW_EMAIL' GIT_COMMITTER_NAME='YOUR_NEW_NAME' GIT_COMMITTER_EMAIL='YOUR_NEW_EMAIL' git commit-tree "$@"; else git commit-tree "$@"; fi" HEAD
If you understand the risks of rewriting history for a shared repository, you
can force push with the -f flag:
1
git push -f
Then, to ensure future commits don’t use the wrong name and email, you can set
it explicity for this particular repo (instead of globally) with these
commands:
By default, the sudo command does not pass your current shell environment to
the new process.
I’ve added the function below to my bash configuration which will gives me an
esudo command. This command works much like sudo but it will pass my current
environment to the new process.
1
function esudo { sudo -E bash -c "$*";}
If you use a ruby version manager, you’ll know that they generally rely on
environment variables, so rvm provide the rvmsudo command for this purpose.
I’ve since switched to chruby and I couldn’t seem to find an equivalent, but
this has solved the issue for me.
It seems the JavaScript community is divided into two camps when it comes to
indentation conventions: two spaces or four?
I personally prefer two spaces mainly because its what I’m used to in Ruby, I
find it easier on the eyes and just that little bit easier to keep my lines
under 80 characters long (yes, I still think this is good practice).
Our team had come to a general consensus for using two spaces, but when we were
starting a new project based on an existing project that used four spaces, we
thought it would be nice to fix all the whitespace in one go.
In a rush, we made a commit that literally replaced all the whitespace with the
following command:
1
find . -type f -exec sed -i """s/ / /g"{}\;
The problem with this approach is that it makes going through commit messages
harder. It means that when you use git annotate, every line with indentation
is going to have the whitespace fix as the last commit, so then you would have
to jump back another commit to get the actual commit message for that line,
which can be fairly annoying.
Luckily, if you are forking an existing repository for a new project you have
the luxury of being able to rewrite history without causing any pain to others
as no one else would have checked it out yet.
Git provides a powerful command called filter-branch. It is designed to
rewrite large amounts of history in one go. This can be useful to purge
sensitive information from every commit or update an email address in the
commit data, etc. The only problem with this is, just like rebasing, any
existing checkouts will not be able to simply use git pull cleanly after the
history has been rewritten upstream, but this isn’t a problem for new projects.
In order to execute the command above for every commit in our repo, we can make
use of the --tree-filter option like this:
1
git filter-branch --tree-filter 'find . -type f -exec sed -i "" "s/ / /g" {} \;' HEAD
Please note, this can take quite some time, especially for large repositories.
You should also make sure there are no files that specifically need four spaces
(like markdown files, etc.), otherwise you may want to restrict the find
command to only effect files you know are safe to change (*.js for example).
For more information on rewriting history and git filter-branch, see this
article
and the
documentation.
We all know that if you are going to be rendering user provided content on a
HTML page, it should be sanitized just in case they include a nasty script tag
or something.
React
will automatically sanitize any string being rendered to the screen unless you
jump through hoops
first - which is great because making this difficult means its somewhat harder
to inadvertently create a
XSS vulnerability.
However, if you get a string from a human source and want to display that in a
div tag while maintaining its newlines, this becomes a little harder because
you don’t want to replace all \n characters with <br /> tags because this
would mean you would also need to disable the sanitizing.
There are some libraries to deal with this sort of thing that implement white
listing of tags, etc.
Another alternative would be using something like
Markdown.
Even a simple <pre> tag will get you pretty far, but today I read an article
that shows how you can achieve this with a little CSS:
123
.pre{white-space:pre-line;}
For more details, I recommend having a read of
this article.
If you ever needed to circumvent the browsers
Same Origin Policy (SOP)
so you don’t need to add
Cross Origin Resource Sharing (CORS)
headers just to test out an idea, here is a quick little hack to open a new
instance of Chrome that will not enforce these security features:
1
alias unsafe-chrome='/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=~/Library/Application\ Support/Google/UnsafeChrome --disable-web-security 2> /dev/null'
The important options here are:
--disable-web-security - This will disable the Same Origin Policy --user-data-dir=... - This launches a separate instance, with its own user data
directory.
This prevents you from needing to close any current instances of chrome,
otherwise running this command will just bring the existing instance into focus.
Another good thing about giving this instance its own user-data-dir is that
you can give this instance its own settings. For example, this instance is not
signed into my Google account and I’ve configured a bright red theme so I can
easily tell the difference between my normal Chrome and my insecure Chrome!