diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index a30d3a1..0970c78 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -177,6 +177,11 @@ REORDERING TAB PAGES:
 		make the current tab page the first one.  Without N the tab
 		page is made the last one.
 
+:tabm[ove] +[N]
+:tabm[ove] -[N]
+		Move the current tab page N places to the right (with +) or to
+		the left (with -).
+
 
 LOOPING OVER TAB PAGES:
 
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index a0b6b07..8ebbda1 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -2647,8 +2647,6 @@ Tab pages:
     font, etc.)
 8   Make GUI menu in tab pages line configurable.  Like the popup menu.
 8   balloons for the tab page labels that are shortened to show the full path.
-8   :tabmove +N	 move tab page N pages forward
-8   :tabmove -N	 move tab page N pages backward
 7   :tabdup	 duplicate the tab with all its windows.
 7   Option to put tab line at the left or right?  Need an option to specify
     its width.  It's like a separate window with ":tabs" output.
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index ac61f1a..99474fc 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -944,7 +944,7 @@ EX(CMD_tabfind,		"tabfind",	ex_splitview,
 EX(CMD_tabfirst,	"tabfirst",	ex_tabnext,
 			TRLBAR),
 EX(CMD_tabmove,		"tabmove",	ex_tabmove,
-			RANGE|NOTADR|ZEROR|COUNT|TRLBAR|ZEROR),
+			EXTRA|NOSPC|TRLBAR),
 EX(CMD_tablast,		"tablast",	ex_tabnext,
 			TRLBAR),
 EX(CMD_tabnext,		"tabnext",	ex_tabnext,
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 6740a51..d99b6b9 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -7478,7 +7478,43 @@ ex_tabnext(eap)
 ex_tabmove(eap)
     exarg_T	*eap;
 {
-    tabpage_move(eap->addr_count == 0 ? 9999 : (int)eap->line2);
+    int relative_tab = 0;
+    int tab_number = 9999;
+    if (eap->arg && *eap->arg != NUL)
+    {
+	char_u *p = eap->arg;
+	int relative = 0; /* argument +X/-X means "move X places to the
+			   * right/left relative to the current place. */
+
+	if (*eap->arg == '-')
+	{
+	    relative = -1;
+	    p = eap->arg + 1;
+	}
+	else if (*eap->arg == '+')
+	{
+	    relative = 1;
+	    p = eap->arg + 1;
+	}
+	else
+	    p = eap->arg;
+
+	if (p == skipdigits(p))
+	{
+	    /* No numbers as argument. */
+	    eap->errmsg = e_invarg;
+	    return;
+	}
+	tab_number = getdigits(&p);
+
+	if (relative)
+	{
+	    relative_tab = tabpage_index(curtab) - 1;
+	    tab_number *= relative;
+	}
+    }
+
+    tabpage_move(relative_tab + tab_number);
 }
 
 /*
diff --git a/src/testdir/test62.in b/src/testdir/test62.in
index 1e514cd..1c5ce98 100644
--- a/src/testdir/test62.in
+++ b/src/testdir/test62.in
@@ -93,6 +93,37 @@ STARTTEST
 :endif
 :"
 :"
+:for i in range(9) | tabnew | endfor
+1gt
+Go=tabpagenr()
+:tabmove 5
+i=tabpagenr()
+:tabmove -2
+i=tabpagenr()
+:tabmove +4
+i=tabpagenr()
+:tabmove
+i=tabpagenr()
+:tabmove -20
+i=tabpagenr()
+:tabmove +20
+i=tabpagenr()
+:let a='No error caught.'
+:try
+:tabmove foo
+:catch E474
+:let a='E474 caught.'
+:endtry
+i=a
+:let a='No error caught.'
+:try
+:3tabmove
+:catch E481
+:let a='E481 caught.'
+:endtry
+i=a
+:"
+:"
 :/^Results/,$w! test.out
 :qa!
 ENDTEST
diff --git a/src/testdir/test62.ok b/src/testdir/test62.ok
index 7625cd2..039d397 100644
--- a/src/testdir/test62.ok
+++ b/src/testdir/test62.ok
@@ -8,3 +8,12 @@ settabvar: pass
 tab drop 1: pass
 tab drop 2: pass
 tab drop 3: pass
+1
+6
+4
+8
+10
+1
+10
+E474 caught.
+E481 caught.
diff --git a/src/window.c b/src/window.c
index cf45e90..2688f57 100644
--- a/src/window.c
+++ b/src/window.c
@@ -3929,7 +3929,7 @@ tabpage_move(nr)
     }
 
     /* Re-insert it at the specified position. */
-    if (n == 0)
+    if (n <= 0)
     {
 	curtab->tp_next = first_tabpage;
 	first_tabpage = curtab;
