changeset 758:d591d4ceeaee

updated for version 7.0224
author vimboss
date Tue, 14 Mar 2006 23:00:46 +0000
parents 8b0484fd9119
children a0b5eaa101c0
files runtime/autoload/netrw.vim runtime/doc/diff.txt runtime/doc/pi_netrw.txt runtime/doc/tags runtime/doc/undo.txt src/os_unix.c src/popupmenu.c src/screen.c src/undo.c
diffstat 9 files changed, 358 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/autoload/netrw.vim
+++ b/runtime/autoload/netrw.vim
@@ -1,7 +1,7 @@
 " netrw.vim: Handles file transfer and remote directory listing across a network
 "            AUTOLOAD PORTION
-" Date:		Mar 09, 2006
-" Version:	79
+" Date:		Mar 13, 2006
+" Version:	80
 " Maintainer:	Charles E Campbell, Jr <drchipNOSPAM at campbellfamily dot biz>
 " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
 " Copyright:    Copyright (C) 1999-2005 Charles E. Campbell, Jr. {{{1
@@ -23,7 +23,7 @@
 if &cp || exists("g:loaded_netrw")
   finish
 endif
-let g:loaded_netrw = "v79"
+let g:loaded_netrw = "v80"
 if v:version < 700
  echohl WarningMsg | echo "***netrw*** you need vim version 7.0 or later for version ".g:loaded_netrw." of netrw" | echohl None
  finish
@@ -2802,6 +2802,23 @@ fun! netrw#DirBrowse(dirname)
   endif
   let s:last_sort_by= g:netrw_sort_by
 
+  " set up ShellCmdPost handling.  Append current buffer to browselist
+  if !exists("s:netrw_browselist")
+   let s:netrw_browselist= []
+  endif
+  if g:netrw_fastbrowse <= 1 && (empty(s:netrw_browselist) || bufnr("%") > s:netrw_browselist[-1])
+   call add(s:netrw_browselist,bufnr("%"))
+"   call Decho("browselist=".string(s:netrw_browselist))
+  endif
+  if !exists("s:netrw_browser_shellcmd") && g:netrw_fastbrowse <= 1
+"   call Decho("setting up local-browser shell command refresh")
+   let s:netrw_browser_shellcmd= 1
+   augroup AuNetrwShellCmd
+    au!
+    au ShellCmdPost *	call s:LocalBrowseShellCmdRefresh()
+   augroup END
+  endif
+
   " get the new directory name
   if has("win32") || has("win95") || has("win64") || has("win16")
    let b:netrw_curdir= substitute(a:dirname,'\\','/','ge')
@@ -3162,6 +3179,36 @@ fun! s:LocalBrowseChgDir(dirname,newdir,
 endfun
 
 " ---------------------------------------------------------------------
+" LocalBrowseShellCmdRefresh: this function is called after a user has {{{2
+" performed any shell command.  The idea is to cause all local-browsing
+" buffers to be refreshed after a user has executed some shell command,
+" on the chance that s/he removed/created a file/directory with it.
+fun! s:LocalBrowseShellCmdRefresh()
+"  call Dfunc("LocalBrowseShellCmdRefresh() browselist=".string(s:netrw_browselist))
+  "  go through all buffers,
+  "  including unlisted (which is why I can't use bufdo)
+  let curwin = winnr()
+  let ibl    = 0
+  for ibuf in s:netrw_browselist
+   if bufwinnr(ibuf) == -1
+"    call Decho("wiping  buf#".ibuf)
+    exe "bw ".ibuf
+    call remove(s:netrw_browselist,ibl)
+"    call Decho("browselist=".string(s:netrw_browselist))
+    continue
+   else
+"    call Decho("refresh buf#".ibuf.'-> win#'.bufwinnr(ibuf))
+    exe bufwinnr(ibuf)."wincmd w"
+    call s:NetRefresh(s:LocalBrowseChgDir(b:netrw_curdir,'./'),1)
+   endif
+   let ibl= ibl + 1
+  endfor
+  exe curwin."wincmd w"
+
+"  call Dret("LocalBrowseShellCmdRefresh")
+endfun
+
+" ---------------------------------------------------------------------
 " LocalBrowseRm: {{{2
 fun! s:LocalBrowseRm(path) range
 "  call Dfunc("LocalBrowseRm(path<".a:path.">)")
@@ -3465,7 +3512,7 @@ fun! netrw#Explore(indx,dosplit,style,..
      call search('\<'.substitute(dirfile,"^.*/","","").'\>',"w")
     endif
     let w:netrw_explore_mtchcnt = indx + 1
-    let w:netrw_explore_bufnr   = bufnr(".")
+    let w:netrw_explore_bufnr   = bufnr("%")
     let w:netrw_explore_line    = line(".")
     call s:SetupNetrwStatusLine('%f %h%m%r%=%9*%{NetrwStatusLine()}')
 "    call Decho("explore: mtchcnt=".w:netrw_explore_mtchcnt." bufnr=".w:netrw_explore_bufnr." line#".w:netrw_explore_line)
@@ -3537,8 +3584,8 @@ fun! NetrwStatusLine()
 "  let g:stlmsg=""
 "  if !exists("w:netrw_explore_bufnr")
 "   let g:stlmsg="!X<explore_bufnr>"
-"  elseif w:netrw_explore_bufnr != bufnr(".")
-"   let g:stlmsg="explore_bufnr!=".bufnr(".")
+"  elseif w:netrw_explore_bufnr != bufnr("%")
+"   let g:stlmsg="explore_bufnr!=".bufnr("%")
 "  endif
 "  if !exists("w:netrw_explore_line")
 "   let g:stlmsg=" !X<explore_line>"
@@ -3550,7 +3597,7 @@ fun! NetrwStatusLine()
 "  endif
   " ^^^ NetrwStatusLine() debugging ^^^
 
-  if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr(".") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list")
+  if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list")
    " restore user's status line
    let &stl        = s:netrw_users_stl
    let &laststatus = s:netrw_users_ls
--- a/runtime/doc/diff.txt
+++ b/runtime/doc/diff.txt
@@ -1,4 +1,4 @@
-*diff.txt*      For Vim version 7.0aa.  Last change: 2006 Feb 18
+*diff.txt*      For Vim version 7.0aa.  Last change: 2006 Mar 14
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -378,7 +378,7 @@ will have the same effect.  These variab
 
 Example (this does the same as 'patchexpr' being empty): >
 
-	let patchexpr=MyPatch
+	let patchexpr=MyPatch()
 	function MyPatch
 	   :call system("patch -o " . v:fname_out . " " . v:fname_in .
 	   \  " < " . v:fname_diff)
--- a/runtime/doc/pi_netrw.txt
+++ b/runtime/doc/pi_netrw.txt
@@ -1,4 +1,4 @@
-*pi_netrw.txt*  For Vim version 7.0.  Last change: Mar 09, 2006
+*pi_netrw.txt*  For Vim version 7.0.  Last change: Mar 10, 2006
 
 		VIM REFERENCE MANUAL    by Charles E. Campbell, Jr.
 
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -1965,6 +1965,8 @@ 90.5	usr_90.txt	/*90.5*
 :dsp	tagsrch.txt	/*:dsp*
 :dsplit	tagsrch.txt	/*:dsplit*
 :e	editing.txt	/*:e*
+:ea	undo.txt	/*:ea*
+:earlier	undo.txt	/*:earlier*
 :ec	eval.txt	/*:ec*
 :echo	eval.txt	/*:echo*
 :echoe	eval.txt	/*:echoe*
@@ -2142,6 +2144,8 @@ 90.5	usr_90.txt	/*90.5*
 :lang	mlang.txt	/*:lang*
 :language	mlang.txt	/*:language*
 :last	editing.txt	/*:last*
+:lat	undo.txt	/*:lat*
+:later	undo.txt	/*:later*
 :lb	quickfix.txt	/*:lb*
 :lbuffer	quickfix.txt	/*:lbuffer*
 :lc	editing.txt	/*:lc*
@@ -5281,7 +5285,9 @@ g$	motion.txt	/*g$*
 g&	change.txt	/*g&*
 g'	motion.txt	/*g'*
 g'a	motion.txt	/*g'a*
+g+	undo.txt	/*g+*
 g,	motion.txt	/*g,*
+g-	undo.txt	/*g-*
 g0	motion.txt	/*g0*
 g8	various.txt	/*g8*
 g:DrChipTopLvlMenu	pi_netrw.txt	/*g:DrChipTopLvlMenu*
@@ -6254,6 +6260,7 @@ new-searchpat	version6.txt	/*new-searchp
 new-session-files	version5.txt	/*new-session-files*
 new-spell	version7.txt	/*new-spell*
 new-tab-pages	version7.txt	/*new-tab-pages*
+new-undo-branches	version7.txt	/*new-undo-branches*
 new-unlisted-buffers	version6.txt	/*new-unlisted-buffers*
 new-user-defined	version5.txt	/*new-user-defined*
 new-user-manual	version6.txt	/*new-user-manual*
@@ -7212,6 +7219,7 @@ undercurl	syntax.txt	/*undercurl*
 underline	syntax.txt	/*underline*
 undo	undo.txt	/*undo*
 undo-blocks	undo.txt	/*undo-blocks*
+undo-branches	undo.txt	/*undo-branches*
 undo-commands	undo.txt	/*undo-commands*
 undo-redo	undo.txt	/*undo-redo*
 undo-remarks	undo.txt	/*undo-remarks*
--- a/runtime/doc/undo.txt
+++ b/runtime/doc/undo.txt
@@ -1,4 +1,4 @@
-*undo.txt*      For Vim version 7.0aa.  Last change: 2006 Feb 28
+*undo.txt*      For Vim version 7.0aa.  Last change: 2006 Mar 14
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -11,7 +11,8 @@ The basics are explained in section |02.
 1. Undo and redo commands	|undo-commands|
 2. Two ways of undo		|undo-two-ways|
 3. Undo blocks			|undo-blocks|
-4. Remarks about undo		|undo-remarks|
+4. Undo branches		|undo-branches|
+5. Remarks about undo		|undo-remarks|
 
 ==============================================================================
 1. Undo and redo commands				*undo-commands*
@@ -102,7 +103,88 @@ After this an "u" command will undo the 
 change.
 
 ==============================================================================
-4. Remarks about undo					*undo-remarks*
+4. Undo branches					*undo-branches*
+
+Above we only discussed one line of undo.  But it is also possible to branch
+off.  This happens when you undo a few changes and then make a new change.
+The undone changes become a branch.  You can go to that branch with the
+following commands.
+
+What matters here is the order in which the changes are made.  Undo and redo
+are not considered changes in this context.  After each change you have a new
+state of the text.
+
+							*g-*
+g-			Go to older text state.  With a count repeat that many
+			times.  {not in Vi}
+							*:ea* *:earlier*
+:earlier {count}	Go to older text state {count} times.
+:earlier {N}s		Go to older text state about {N} seconds before.
+:earlier {N}m		Go to older text state about {N} minutes before.
+:earlier {N}h		Go to older text state about {N} hours before.
+
+							*g+*
+g+			Go to newer text state.  With a count repeat that many
+			times.  {not in Vi}
+							*:lat* *:later*
+:later {count}		Go to newer text state {count} times.
+:later {N}s		Go to newer text state about {N} seconds later.
+:later {N}m		Go to newer text state about {N} minutes later.
+:later {N}h		Go to newer text state about {N} hours later.
+
+Note that text states will become unreachable when undo information is cleared
+for 'undolevels'.
+
+Don't be surprised when moving through time shows multiple changes to take
+place at a time.  This happens when moving through the undo tree and then
+making a new change.
+
+EXAMPLE
+
+Start with this text:
+	one two three ~
+
+Delete the first word by pressing "x" three times:
+	ne two three ~
+	e two three ~
+	 two three ~
+
+Now undo that by pressing "u" three times:
+	e two three ~
+	ne two three ~
+	one two three ~
+
+Delete the second word by pressing "x" three times:
+	one wo three ~
+	one o three ~
+	one  three ~
+
+Now undo that by using "g-" three times:
+	one o three ~
+	one wo three ~
+	one two three ~
+
+Continue going back in time by pressing "g-" one more time:
+	 two three ~
+
+You are now back in the first undo branch, after deleting "one".  Repeating
+"g-" will now bring you back to the original text:
+	e two three ~
+	ne two three ~
+	one two three ~
+
+Jump to the last change with ":later 1h":
+	one  three ~
+
+And back to the start again with ":earlier 1h":
+	one two three ~
+
+
+Note that using "u" and CTRL-R will not get you to all possible text states
+while repeating "g-" and "g+" does.
+
+==============================================================================
+5. Remarks about undo					*undo-remarks*
 
 The number of changes that are remembered is set with the 'undolevels' option.
 If it is zero, the Vi-compatible way is always used.  If it is negative no
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -931,6 +931,29 @@ deathtrap SIGDEFARG(sigarg)
     get_stack_limit();
 #endif
 
+#if 0
+    /* This is for opening gdb the moment Vim crashes.
+     * You need to manually adjust the file name and Vim executable name.
+     * Suggested by SungHyun Nam. */
+    {
+# define VI_GDB_FILE "/tmp/vimgdb"
+# define VIM_NAME "/usr/bin/vim"
+	FILE *fp = fopen(VI_GDB_FILE, "w");
+	if (fp)
+	{
+	    fprintf(fp,
+		    "file %s\n"
+		    "attach %d\n"
+		    "set height 1000\n"
+		    "bt full\n"
+		    , VIM_NAME, getpid());
+	    fclose(fp);
+	    system("xterm -e gdb -x "VI_GDB_FILE);
+	    unlink(VI_GDB_FILE);
+	}
+    }
+#endif
+
 #ifdef SIGHASARG
     /* try to find the name of this signal */
     for (i = 0; signal_info[i].sig != -1; i++)
--- a/src/popupmenu.c
+++ b/src/popupmenu.c
@@ -504,6 +504,7 @@ pum_undisplay()
 {
     pum_array = NULL;
     redraw_all_later(SOME_VALID);
+    status_redraw_all();
 }
 
 /*
--- a/src/screen.c
+++ b/src/screen.c
@@ -4273,9 +4273,15 @@ win_line(wp, lnum, startrow, endrow, noc
 
 #ifdef FEAT_SYN_HL
 	    /* Highlight 'cursorcolumn' past end of the line. */
+	    if (wp->w_p_wrap)
+		v = wp->w_skipcol;
+	    else
+		v = wp->w_leftcol;
+	    if (vcol < v)	/* line ends before left margin */
+		vcol = v;
 	    if (wp->w_p_cuc
 		    && (int)wp->w_virtcol >= vcol
-		    && (int)wp->w_virtcol < W_WIDTH(wp)
+		    && (int)wp->w_virtcol < W_WIDTH(wp) + v
 		    && lnum != wp->w_cursor.lnum
 # ifdef FEAT_RIGHTLEFT
 		    && !wp->w_p_rl
--- a/src/undo.c
+++ b/src/undo.c
@@ -39,13 +39,39 @@
  *
  * Each u_entry list contains the information for one undo or redo.
  * curbuf->b_u_curhead points to the header of the last undo (the next redo),
- * or is NULL if nothing has been undone.
+ * or is NULL if nothing has been undone (end of the branch).
  *
  * For keeping alternate undo/redo branches the uh_alt field is used.  Thus at
  * each point in the list a branch may appear for an alternate to redo.  The
  * uh_seq field is numbered sequentially to be able to find a newer or older
  * branch.
  *
+ *		   +---------------+	+---------------+
+ * b_u_oldhead --->| u_header	   |	| u_header	|
+ *		   |   uh_alt_next ---->|   uh_alt_next ----> NULL
+ *	   NULL <----- uh_alt_prev |<------ uh_alt_prev |
+ *		   |   uh_prev	   |	|   uh_prev	|
+ *		   +-----|---------+	+-----|---------+
+ *			 |		      |
+ *			 V		      V
+ *		   +---------------+	+---------------+
+ *		   | u_header	   |	| u_header	|
+ *		   |   uh_alt_next |	|   uh_alt_next |
+ * b_u_newhead --->|   uh_alt_prev |	|   uh_alt_prev |
+ *		   |   uh_prev	   |	|   uh_prev	|
+ *		   +-----|---------+	+-----|---------+
+ *			 |		      |
+ *			 V		      V
+ *		       NULL		+---------------+    +---------------+
+ *					| u_header	|    | u_header      |
+ *					|   uh_alt_next ---->|	 uh_alt_next |
+ *					|   uh_alt_prev |<------ uh_alt_prev |
+ *					|   uh_prev	|    |	 uh_prev     |
+ *					+-----|---------+    +-----|---------+
+ *					      |			   |
+ *					     etc.		  etc.
+ *
+ *
  * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the
  * buffer is unloaded.
  */
@@ -55,6 +81,7 @@
 /* See below: use malloc()/free() for memory management. */
 #define U_USE_MALLOC 1
 
+static void u_unch_branch __ARGS((u_header_T *uhp));
 static u_entry_T *u_get_headentry __ARGS((void));
 static void u_getbot __ARGS((void));
 static int undo_allowed __ARGS((void));
@@ -62,7 +89,7 @@ static int u_savecommon __ARGS((linenr_T
 static void u_doit __ARGS((int count));
 static void u_undoredo __ARGS((void));
 static void u_undo_end __ARGS((void));
-static void u_freelist __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
+static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
 static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
 static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
 static void u_freeentry __ARGS((u_entry_T *, long));
@@ -270,8 +297,8 @@ u_savecommon(top, bot, newbot)
 	}
 
 	/*
-	 * If we undid more than we redid, remove the entry lists before and
-	 * including curbuf->b_u_curhead to the alternate branch.
+	 * If we undid more than we redid, move the entry lists before and
+	 * including curbuf->b_u_curhead to an alternate branch.
 	 */
 	old_curhead = curbuf->b_u_curhead;
 	if (old_curhead != NULL)
@@ -285,17 +312,17 @@ u_savecommon(top, bot, newbot)
 	 */
 	while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
 	{
-	    u_header_T	    *upfree = curbuf->b_u_oldhead;
+	    u_header_T	    *uhfree = curbuf->b_u_oldhead;
 
 	    /* If there is no branch only free one header. */
-	    if (upfree->uh_alt_next == NULL)
-		u_freelist(curbuf, upfree, &old_curhead);
+	    if (uhfree->uh_alt_next == NULL)
+		u_freeheader(curbuf, uhfree, &old_curhead);
 	    else
 	    {
 		/* Free the oldest alternate branch as a whole. */
-		while (upfree->uh_alt_next != NULL)
-		    upfree = upfree->uh_alt_next;
-		u_freebranch(curbuf, upfree, &old_curhead);
+		while (uhfree->uh_alt_next != NULL)
+		    uhfree = uhfree->uh_alt_next;
+		u_freebranch(curbuf, uhfree, &old_curhead);
 	    }
 	}
 
@@ -317,10 +344,14 @@ u_savecommon(top, bot, newbot)
 		curbuf->b_u_oldhead = uhp;
 	}
 	uhp->uh_alt_prev = NULL;
+	if (curbuf->b_u_newhead != NULL)
+	    curbuf->b_u_newhead->uh_prev = uhp;
+
 	uhp->uh_seq = curbuf->b_u_seq_last++;
 	curbuf->b_u_seq_cur = curbuf->b_u_seq_last;
-	if (curbuf->b_u_newhead != NULL)
-	    curbuf->b_u_newhead->uh_prev = uhp;
+	uhp->uh_time = time(NULL);
+	curbuf->b_u_seq_time = uhp->uh_time + 1;
+
 	uhp->uh_walk = 0;
 	uhp->uh_entry = NULL;
 	uhp->uh_getbot_entry = NULL;
@@ -331,7 +362,6 @@ u_savecommon(top, bot, newbot)
 	else
 	    uhp->uh_cursor_vcol = -1;
 #endif
-	uhp->uh_time = time(NULL);
 
 	/* save changed and buffer empty flag for undo */
 	uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
@@ -575,7 +605,6 @@ u_doit(count)
 	    }
 
 	    u_undoredo();
-	    curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_seq;
 	}
 	else
 	{
@@ -586,7 +615,8 @@ u_doit(count)
 	    }
 
 	    u_undoredo();
-	    curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_seq + 1;
+	    ++curbuf->b_u_seq_cur;
+	    ++curbuf->b_u_seq_time;
 
 	    /* Advance for next redo.  Set "newhead" when at the end of the
 	     * redoable changes. */
@@ -603,18 +633,27 @@ static int lastmark = 0;
 /*
  * Undo or redo over the timeline.
  * When "step" is negative go back in time, otherwise goes forward in time.
+ * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
+ * seconds.
  */
     void
-undo_time(step)
-    int	    step;
+undo_time(step, sec)
+    long	step;
+    int		sec;
 {
     long	    target;
     long	    closest;
+    long	    closest_start;
+    long	    closest_seq = 0;
+    long	    val;
+    long	    limit;
     u_header_T	    *uhp;
     u_header_T	    *last;
     int		    mark;
     int		    nomark;
     int		    round;
+    int		    dosec = sec;
+    int		    above = FALSE;
 
     u_newcount = 0;
     u_oldcount = 0;
@@ -625,18 +664,43 @@ undo_time(step)
      * the current one also counts, thus do one less. */
     if (step < 0)
     {
-	target = curbuf->b_u_seq_cur + step;
-	closest = -1;
+	if (sec)
+	    target = (long)curbuf->b_u_seq_time + step;
+	else
+	    target = curbuf->b_u_seq_cur + step;
+	if (target < 0)
+	    target = -1;
+	closest = -2;
     }
     else
     {
-	target = curbuf->b_u_seq_cur + step - 1;
-	closest = curbuf->b_u_seq_last + 1;
+	if (sec)
+	{
+	    target = curbuf->b_u_seq_time + step - 1;
+	    closest = time(NULL) + 1;
+	}
+	else
+	{
+	    target = curbuf->b_u_seq_cur + step - 1;
+	    closest = curbuf->b_u_seq_last + 1;
+	}
+	if (target >= closest)
+	    target = closest - 1;
     }
+    closest_start = closest;
+    if (sec)
+	limit = curbuf->b_u_seq_time + (step > 0 ? -1 : 1);
+    else
+	limit = curbuf->b_u_seq_cur;
 
-    /* May do this twice:
+    /*
+     * May do this twice:
      * 1. Search for "target", update "closest" to the best match found.
-     * 2. If "target" not found search for "closest".  */
+     * 2. If "target" not found search for "closest".
+     *
+     * When using the closest time we use the sequence number in the second
+     * round, because there may be several entries with the same time.
+     */
     for (round = 1; round <= 2; ++round)
     {
 	/* Find the path from the current state to where we want to go.  The
@@ -654,13 +718,37 @@ undo_time(step)
 	while (uhp != NULL)
 	{
 	    uhp->uh_walk = mark;
-	    if (uhp->uh_seq == target)	/* found it! */
-		break;
+	    val = (dosec ? uhp->uh_time : uhp->uh_seq);
 
-	    if (round == 1 && (step < 0
-			? (uhp->uh_seq < target && uhp->uh_seq > closest)
-			: (uhp->uh_seq > target && uhp->uh_seq < closest)))
-		closest = uhp->uh_seq;
+	    if (round == 1)
+	    {
+		/* Remember the header that is closest to the target.
+		 * It must be at least in the right direction (checked with
+		 * "limit").  When the timestamp is equal find the
+		 * highest/lowest sequence number. */
+		if ((dosec && val == closest)
+			? (step < 0
+			    ? uhp->uh_seq < closest_seq
+			    : uhp->uh_seq > closest_seq)
+			: (step < 0
+			    ? (val < limit
+				&& (closest > target
+				    ? (val <= closest)
+				    : (val >= closest)))
+			    : (val > limit
+				&& (closest < target
+				    ? val >= closest
+				    : val <= closest))))
+		{
+		    closest = val;
+		    closest_seq = uhp->uh_seq;
+		}
+	    }
+
+	    /* Quit searching when we found a match.  But when searching for a
+	     * time we need to continue looking for the best uh_seq. */
+	    if (target == val && !dosec)
+		break;
 
 	    /* go down in the tree if we haven't been there */
 	    if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
@@ -693,18 +781,19 @@ undo_time(step)
 
 	if (uhp != NULL)    /* found it */
 	    break;
-	if (step < 0 && closest == -1)
+	if (closest == closest_start)
 	{
-	    MSG(_("Already at oldest change"));
-	    return;
-	}
-	if (step > 0 && closest == curbuf->b_u_seq_last + 1)
-	{
-	    MSG(_("Already at newest change"));
+	    if (step < 0)
+		MSG(_("Already at oldest change"));
+	    else
+		MSG(_("Already at newest change"));
 	    return;
 	}
 
-	target = closest;
+	target = closest_seq;
+	dosec = FALSE;
+	if (step < 0)
+	    above = TRUE;	/* stop above the header */
     }
 
     /* If we found it: Follow the path to go to where we want to be. */
@@ -720,7 +809,8 @@ undo_time(step)
 		uhp = curbuf->b_u_newhead;
 	    else
 	    {
-		while (uhp->uh_alt_prev != NULL)
+		while (uhp->uh_alt_prev != NULL
+					 && uhp->uh_alt_prev->uh_walk == mark)
 		{
 		    uhp->uh_walk = nomark;
 		    uhp = uhp->uh_alt_prev;
@@ -732,11 +822,10 @@ undo_time(step)
 	    curbuf->b_u_curhead = uhp;
 	    u_undoredo();
 	    uhp->uh_walk = nomark;	/* don't go back down here */
-	    curbuf->b_u_seq_cur = uhp->uh_seq;
 	}
 
 	/*
-	 * And now go down the tree, branching off where needed.
+	 * And now go down the tree (redo), branching off where needed.
 	 */
 	uhp = curbuf->b_u_curhead;
 	for (;;)
@@ -759,13 +848,21 @@ undo_time(step)
 
 		uhp = last;
 	    }
+	    curbuf->b_u_curhead = uhp;
+	    curbuf->b_u_seq_cur = uhp->uh_seq;
+	    curbuf->b_u_seq_time = uhp->uh_time;
 
 	    if (uhp->uh_walk != mark)
 		break;	    /* must have reached the target */
 
-	    curbuf->b_u_curhead = uhp;
+	    /* Stop when going backwards in time and didn't find the exact
+	     * header we were looking for. */
+	    if (uhp->uh_seq == target && above)
+		break;
+
 	    u_undoredo();
-	    curbuf->b_u_seq_cur = uhp->uh_seq + 1;
+	    ++curbuf->b_u_seq_cur;
+	    ++curbuf->b_u_seq_time;
 
 	    /* Advance "curhead" to below the header we last used.  If it
 	     * becomes NULL then we need to set "newhead" to this leaf. */
@@ -813,8 +910,9 @@ u_undoredo()
     visualinfo_T visualinfo;
 #endif
     int		empty_buffer;		    /* buffer became empty */
+    u_header_T	*curhead = curbuf->b_u_curhead;
 
-    old_flags = curbuf->b_u_curhead->uh_flags;
+    old_flags = curhead->uh_flags;
     new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
 	       ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
     setpcmark();
@@ -831,7 +929,7 @@ u_undoredo()
     curbuf->b_op_end.lnum = 0;
     curbuf->b_op_end.col = 0;
 
-    for (uep = curbuf->b_u_curhead->uh_entry; uep != NULL; uep = nuep)
+    for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
     {
 	top = uep->ue_top;
 	bot = uep->ue_bot;
@@ -853,10 +951,10 @@ u_undoredo()
 	    /* If the saved cursor is somewhere in this undo block, move it to
 	     * the remembered position.  Makes "gwap" put the cursor back
 	     * where it was. */
-	    lnum = curbuf->b_u_curhead->uh_cursor.lnum;
+	    lnum = curhead->uh_cursor.lnum;
 	    if (lnum >= top && lnum <= top + newsize + 1)
 	    {
-		curwin->w_cursor = curbuf->b_u_curhead->uh_cursor;
+		curwin->w_cursor = curhead->uh_cursor;
 		newlnum = curwin->w_cursor.lnum - 1;
 	    }
 	    else
@@ -970,8 +1068,8 @@ u_undoredo()
 	newlist = uep;
     }
 
-    curbuf->b_u_curhead->uh_entry = newlist;
-    curbuf->b_u_curhead->uh_flags = new_flags;
+    curhead->uh_entry = newlist;
+    curhead->uh_flags = new_flags;
     if ((old_flags & UH_EMPTYBUF) && bufempty())
 	curbuf->b_ml.ml_flags |= ML_EMPTY;
     if (old_flags & UH_CHANGED)
@@ -987,16 +1085,16 @@ u_undoredo()
      * restore marks from before undo/redo
      */
     for (i = 0; i < NMARKS; ++i)
-	if (curbuf->b_u_curhead->uh_namedm[i].lnum != 0)
+	if (curhead->uh_namedm[i].lnum != 0)
 	{
-	    curbuf->b_namedm[i] = curbuf->b_u_curhead->uh_namedm[i];
-	    curbuf->b_u_curhead->uh_namedm[i] = namedm[i];
+	    curbuf->b_namedm[i] = curhead->uh_namedm[i];
+	    curhead->uh_namedm[i] = namedm[i];
 	}
 #ifdef FEAT_VISUAL
-    if (curbuf->b_u_curhead->uh_visual.vi_start.lnum != 0)
+    if (curhead->uh_visual.vi_start.lnum != 0)
     {
-	curbuf->b_visual = curbuf->b_u_curhead->uh_visual;
-	curbuf->b_u_curhead->uh_visual = visualinfo;
+	curbuf->b_visual = curhead->uh_visual;
+	curhead->uh_visual = visualinfo;
     }
 #endif
 
@@ -1005,15 +1103,15 @@ u_undoredo()
      * before starting the change (for the "o" command).
      * Otherwise the cursor should go to the first undone line.
      */
-    if (curbuf->b_u_curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
+    if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
 						 && curwin->w_cursor.lnum > 1)
 	--curwin->w_cursor.lnum;
-    if (curbuf->b_u_curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
+    if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
     {
-	curwin->w_cursor.col = curbuf->b_u_curhead->uh_cursor.col;
+	curwin->w_cursor.col = curhead->uh_cursor.col;
 #ifdef FEAT_VIRTUALEDIT
-	if (virtual_active() && curbuf->b_u_curhead->uh_cursor_vcol >= 0)
-	    coladvance((colnr_T)curbuf->b_u_curhead->uh_cursor_vcol);
+	if (virtual_active() && curhead->uh_cursor_vcol >= 0)
+	    coladvance((colnr_T)curhead->uh_cursor_vcol);
 	else
 	    curwin->w_cursor.coladd = 0;
 #endif
@@ -1034,6 +1132,10 @@ u_undoredo()
 
     /* Make sure the cursor is on an existing line and column. */
     check_cursor();
+
+    /* Remember where we are for "g-" and ":earlier 10s". */
+    curbuf->b_u_seq_cur = curhead->uh_seq;
+    curbuf->b_u_seq_time = curhead->uh_time;
 }
 
 /*
@@ -1136,11 +1238,22 @@ ex_undojoin(eap)
 u_unchanged(buf)
     buf_T	*buf;
 {
+    u_unch_branch(buf->b_u_oldhead);
+    buf->b_did_warn = FALSE;
+}
+
+    static void
+u_unch_branch(uhp)
+    u_header_T	*uhp;
+{
     u_header_T	*uh;
 
-    for (uh = buf->b_u_newhead; uh; uh = uh->uh_next)
+    for (uh = uhp; uh != NULL; uh = uh->uh_prev)
+    {
 	uh->uh_flags |= UH_CHANGED;
-    buf->b_did_warn = FALSE;
+	if (uh->uh_alt_next != NULL)
+	    u_unch_branch(uh->uh_alt_next);	    /* recursive */
+    }
 }
 
 /*
@@ -1201,7 +1314,7 @@ u_getbot()
  * Free one header and its entry list and adjust the pointers.
  */
     static void
-u_freelist(buf, uhp, uhpp)
+u_freeheader(buf, uhp, uhpp)
     buf_T	    *buf;
     u_header_T	    *uhp;
     u_header_T	    **uhpp;	/* if not NULL reset when freeing this header */
@@ -1229,7 +1342,7 @@ u_freelist(buf, uhp, uhpp)
 }
 
 /*
- * Free an alternate branch and all following alternate branches.
+ * Free an alternate branch and any following alternate branches.
  */
     static void
 u_freebranch(buf, uhp, uhpp)
@@ -1415,7 +1528,7 @@ u_blockfree(buf)
     buf_T	*buf;
 {
     while (buf->b_u_oldhead != NULL)
-	u_freelist(buf, buf->b_u_oldhead, NULL);
+	u_freeheader(buf, buf->b_u_oldhead, NULL);
     U_FREE_LINE(buf->b_u_line_ptr);
 }