Shortcut for adding SSH keys

If you’ve dabbled with SSH much, for example by following the excellent suso.org tutorial a few years ago, you’ll know about adding keys to allow passwordless login (or, if you prefer, a passphrase) using public key authentication. Specifically, you copy the public key ~/.ssh/id_rsa.pub or ~/.ssh/id_dsa.pub off the machine from which you wish to connect into the /.ssh/authorized_keys file on the target machine. That will allow you to open an SSH session with the machine from the user account on the local machine to the one on the remote machine, without having to type in a password.

tom@conan:~$ scp ~/.ssh/id_rsa.pub crom:.ssh/conan.pubkey
tom@conan:~$ ssh crom
Password:
tom@crom:~$ cd .ssh
tom@crom:~$ cat .ssh/conan.pubkey >>~/.ssh/authorized_keys

However, there’s a nice shortcut that I didn’t know about when I first learned how to do this, which has since been added to that tutorial too — specifically, the ssh-copy-id tool, which is available in most modern OpenSSH distributions and combines this all into one less error-prone step. If you have it available to you, it’s definitely a much better way to add authorized keys onto a remote machine.

tom@conan:~$ ssh-copy-id crom

Incidentally, this isn’t just good for convenience or for automated processes; strong security policies for publically accessible servers might disallow logging in via passwords completely, as usernames and passwords can be guessed. It’s a lot harder to guess an entire SSH key, so forcing this login method will reduce your risk of script kiddies or automated attacks brute-forcing your OpenSSH server to zero. You can arrange this by setting ChallengeResponseAuthentication to no in your sshd_config, but if that’s a remote server, be careful not to lock yourself out!

Buffers, windows, and tabs

If you’ve moved to Vim from an editor like Notepad++ or TextMate, you’ll be used to working with the idea of tabs in a text editor in a certain way. Specifically, a tab represents an open file; while the tab’s there, you’ve got an open file, as soon as you close it, it goes away. This one-to-one correspondence is pretty straightforward and is analogous to using tabs in a web browser; while the page is open, there’s a tab there, and you have one page per tab, and one tab per page.

Vim has a system for tabs as well, but it works in a completely different way from how text editors and browsers do. Beginners with Vim are often frustrated by this because the model for tabs is used so consistently in pretty much every other program they’re accustomed to, and they might end up spending a lot of fruitless time trying to force Vim to follow the same model through its configuration.

I think a good way to understand the differences in concept and usage between Vim’s three levels of view abstraction — buffers, windows, and tabs — is to learn how to use each from the ground up. So let’s start with buffers.

Buffers

A buffer in Vim is probably best thought of for most users as an open instance of a file, that may not necessarily be visible on the current screen. There’s probably a more accurate technical definition as a buffer need not actually correspond to a file on your disk, but it’ll work for our purposes.

When you open a single file in Vim, that file gets put into a buffer, and it’s the only buffer there on startup. If this buffer is unmodified (or if you’re using hidden), and you open another file with :edit, that buffer goes into the background and you start working with the new file. The previous buffer only goes away when you explicitly delete it with a call to :quit or :bdelete, and even then it’s still recoverable unless you do :bwipe.

By default, a buffer can only be in the background like this if it’s unmodified. You can remove this restriction if you like with the :set hidden option.

You can get a quick list of the buffers open in a Vim session with :ls. This all means that when most people think of tabs in more familiar text editors, the idea of a buffer in Vim is actually closest to what they’re thinking.

Windows

A window in Vim is a viewport onto a single buffer. When you open a new window with :split or :vsplit, you can include a filename in the call. This opens the file in a new buffer, and then opens a new window as a view onto it. A buffer can be viewed in multiple windows within a single Vim session; you could have a hundred windows editing the same buffer if you wanted to.

Windows in Vim

Vim Session with multiple windows open, both horizontally and vertically.

Tabs

Finally, a tab in Vim is a collection of one or more windows. The best way to think of tabs, therefore, is as enabling you to group windows usefully. For example, if you were working on code for both a client program and a server program, you could have the files for the client program open in the first tab, perhaps in three or four windows, and the files for the server program open in another, which could be, say, seven windows. You can then flick back and forth between the tabs using :tabn and :tabp.

Tab usage in Vim

Two tabs open in Vim, each with multiple windows. Move between two tabs with :tabn and :tabp.

This model of buffers, windows, and tabs is quite a bit more intricate than in other editors, and the terms are sometimes confusing. However, if you know a bit about how they’re supposed to work, you get a great deal more control over the layout of your editing sessions as a result. Personally, I find I rarely need tabs as with a monitor of sufficient size it suffices to keep two or three windows open at a time when working on sets of files, but on the odd occasion I do need tabs, I do find them tremendously useful.

Thanks to Hacker News users anonymous and m0shen for pointing out potential confusion with the :set hidden option and the :bdelete command.

Committing part of a file

One of the advantages that Git has over Subversion and CVS is the use of its index as a staging area, which turns out to be a much more flexible model than Subversion. One of the things that always annoyed me about Subversion was that there seemed to be no elegant way to only commit only some of your changes to a particular tracked file. Subversion deals only in files in the working copy, and if you want to commit changes to a file, you have to commit all the changes in that file, even if they’re not related.

Where Subversion falls short

As an example, suppose you’re making changes to a working copy of a Subversion repository called myproject, and you’ve made a few changes to the main file, myproject.php; on one line, you’ve fixed a bug caused by getting the parameters for htmlentities() in the wrong order. On another, near the head of the file, you’ve changed a php.ini setting to allow the script to run for a long time. Here’s what the output of svn status and svn diff might look like in this case:

$ svn status
M myproject.php

$ svn diff
Index: myproject.php
================================================================
--- myproject.php (revision 2)
+++ myproject.php (working copy)
@@ -1,5 +1,7 @@
 <?php
+ini_set("max_execution_time", 300);
+
 /**
  * Open main class.
  */
 @@ -120,7 +122,7 @@
 public function dumpvalue($value)
 {
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
 }

Under Subversion, unless you move files around, you can’t commit only one of these changes; you need to commit both. This isn’t really the end of the world, since you could include a commit message describing both things you changed:

$ svn commit -m "Allowed longer runtime, fixed parameter order bug"
Transmitting file data .
Committed revision 3.

But if you’re finicky like me, and you’d prefer to think of commits as grouping semantically related changes as much as possible, it would be much better to be able to commit these two changes separately, and this is where Git’s use of an index shines.

Git’s method

Let’s work with the same project again, but this time as a Git repository. We’ll make the same changes again, and view the output of git status and git diff:

$ git status
# On branch master
# Changes not staged for commit:
#
# modified: myproject.php
#
no changes added to commit

$ git diff
diff --git a/myproject.php b/myproject.php
index 7c20f21..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -1,5 +1,7 @@
 <?php
+ini_set("max_execution_time", 300);
+
 /**
 * Open main class.
 */
 @@ -120,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
 {
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
 }

So far, so good. Now when we run git add myproject.php to stage the changes in the index ready for commit, by default it does the same thing Subversion does, putting all of the changes in that file into the staging area. That’s probably fine in most cases, but today we want to commit one change, and then the other. The most basic way to do this is using Git’s --patch option.

The --patch option can be added to git add, and to some other Git commands concerned with manipulating the index as well, to explicitly prompt you about staging or not staging different sections of the file, that it terms hunks. In our case, the process of including only the first change would look something like this:

$ git add --patch myproject.php
diff --git a/myproject.php b/myproject.php
index 7c20f21..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -1,5 +1,7 @@
 <?php
+ini_set("max_execution_time", 300);
+
 /**
 * Open main class.
 */
 Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
 @@ -120,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
 {
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
 }
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n

This done, if you compare the output of git diff --staged and git diff, you’ll notice that there are changes staged ready for commit in the file, and also changes that are not staged that we can commit separately later:

$ git diff --staged
diff --git a/myproject.php b/myproject.php
index 7c20f21..4bb2362 100644
--- a/myproject.php
+++ b/myproject.php
@@ -1,5 +1,7 @@
 <?php
+ini_set("max_execution_time", 300);
+
 /**
 * Open main class.
 */

$ git diff
diff --git a/myproject.php b/myproject.php
index 4bb2362..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -122,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
 {
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
 }

So your staging area is all ready with just that one change in it, and all you need to do is type git commit with an appropriate message:

$ git commit -m "Allowed longer runtime"
[master 19d9068] Allowed longer runtime
1 files changed, 2 insertions(+), 0 deletions(-)

And the other change you made is still there, waiting to be staged and committed whenever you see fit:

$ git diff
diff --git a/myproject.php b/myproject.php
index 4bb2362..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -122,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
 {
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
 }

Other methods

Because Git’s index can be manipulated with its lower-level tools very easily, you can treat the differences between your changes and the index like any other diff task. This means more advanced tools like Fugitive for Vim can be even better for seeing changesets in individual files as you stage them for commit. Check out Drew Neil’s Vimcast series on Fugitive if you’re interested in doing this; it’s quite an in-depth series of videos, but very much worth watching if you’re a Vim user who wants to understand and use Git to its fullest, and you really value precision and clarity in your commits.

Vim search highlighting

Being able to search by regular expression is a pretty fundamental thing in a text editor. Vim takes it to the next level by making entering a search into a motion all on its own, and even a few weeks’ Vim experience and you’ll probably catch yourself using the search to navigate your buffers.

One of the things I most commonly see recommended in threads and discussions about useful Vim features includes turning on two kinds of search highlighting: first, highlighting the search result on which you’ll land as you type it, and secondly, once you’ve pressed Enter to run your search, highlighting all of the matches. You turn these on like this:

:set incsearch
:set hlsearch

This ends up being great for editing code in particular, because if you search for variable or function names, you can see at a glance where they’re declared, defined, or used in your code. Couple that with the quick forward and backward search that you can do with the * and # keys, and you have a very convenient way to flick through successive uses of an identifier.

The only snag, really, is that it doesn’t turn itself off, and it gets visually distracting when you’ve moved on from whatever task prompted the search, and because you might search to move around, you don’t always actually want the highlighting to stay there. Text editors following a Windows model would probably clear away the highlighting as soon as you close the search box, or insert some text. This isn’t the case with Vim, which can be good or bad, but most people I’ve spoken to about this feature seem to find it annoying.

Bram Moolenar, the author of Vim, addressed this very topic in a Google Tech Talk he gave about effective text editing. You can turn off the highlighting temporarily by typing:

:nohlsearch

This is different from :set nohlsearch in that it doesn’t permanently disable the highlighting. It’ll turn it on next time you search. I find this is kind of a pain to type, so I alias it to Ctrl+L in my .vimrc file, so that in addition to its default behaviour of clearing the screen it clears the search highlighting as well:

nnoremap <C-l> :nohlsearch<CR><C-l>

Additionally, I like to set leader keys to quickly turn the highlighting option on and off completely if for some particular project it’s just thoroughly unhelpful. In this example, typing \i and \h will toggle the incsearch and hlsearch settings on and off, respectively. I’m assuming you’re using the backslash key as your <leader> here.

nnoremap <leader>i :set incsearch!<CR>
nnoremap <leader>h :set hlsearch!<CR>

Finally, I find that while I’m in insert mode, I usually don’t want to see the highlighting, so I define events to turn the highlighting off temporarily in the current buffer while I’m inserting text:

autocmd InsertEnter * :setlocal nohlsearch
autocmd InsertLeave * :setlocal hlsearch

I find these shortcuts make the search highlighting behaviour much less annoying. Interestingly, according to Vim’s help, you can’t simply add an event hook to run :nohlsearch when you enter Insert mode (from :help nohlsearch):

This command doesn’t work in an autocommand, because the highlighting state is saved and restored when executing autocommands. Same thing for when invoking a user function.

You can work around this by setting the search pattern to empty instead, as suggested by this Stack Overflow answer, but I don’t really like doing that because I often like to recurse through more search results with n and N after leaving insert mode.