ZyX_I avatar ZyX_I committed dbb1e40 Merge

Merge

Comments (0)

Files changed (25)

 55a792dca0fa1cf28aa8179d43e67f1d25570d25 release-1.2.1
 9de855630429bd49dbc3cc2a55538be5e48200ca release-1.2.2
 54238c7fc6f69fde14ea7955c71b760e6ca9bac2 release-1.3
+bdc3f787b35cd00f6e41439c3f3768664ddb7329 release-1.4
 
   - Partially committing changes ([:AuRecord](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line357-0)).
 
-  - Viewing file state at particular revision ([aurum://file](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line588-0), [:AuFile](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line158-0)).
+  - Viewing file state at particular revision ([aurum://file](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line596-0), [:AuFile](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line158-0)).
 
   - Viewing uncommited changes in a vimdiff, as well as changes between 
     specific revisions ([:AuVimDiff](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line397-0)). It is also possible to open multiple 
   - Viewing working directory status ([:AuStatus](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line361-0)).
 
   - Commiting changes ([:AuCommit](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line99-0)), commit messages are remembered in case of 
-    rollback ([g:aurum_remembermsg](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line846-0)).
+    rollback ([g:aurum_remembermsg](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line867-0)).
 
   - Obtaining various URL’s out of remote repository URL (like URL of the HTML 
     version of the current file with URL fragment pointing to the current line 
 
   - Frontends for various other VCS commands.
 
-Most commands can be reached with a set of mappings (see [aurum-mappings](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line730-0)), 
+Most commands can be reached with a set of mappings (see [aurum-mappings](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line751-0)), 
 all mappings are customizable.
 
 

aurum-addon-info.txt

 {
     "name": "aurum",
-    "version": "1.3",
+    "version": "1.4",
     "author": "ZyX <kp-pav@yandex.ru>",
     "maintainer": "ZyX <kp-pav@yandex.ru>",
     "description": "Plugin for dealing with source files under various VCS control",
     Local mappings (mgid=AuAnnotate, without leader by default) (current 
     revision=revision under cursor):                  *g:frawormap_AuAnnotate*
              *g:frawormap_AuAnnotate_Enter*  *g:frawormap_AuAnnotate_Fdiff*
-             *g:frawormap_AuAnnotate_RFdiff* *g:frawormap_AuAnnotate_Diff*
+             *g:frawormap_AuAnnotate_RFdiff* *g:frawormap_AuAnnotate_FVdiff*
+             *g:frawormap_AuAnnotate_Diff*   *g:frawormap_AuAnnotate_RFVdiff*
              *g:frawormap_AuAnnotate_Rdiff*  *g:frawormap_AuAnnotate_Annotate*
              *g:frawormap_AuAnnotate_RVdiff* *g:frawormap_AuAnnotate_Open*
              *g:frawormap_AuAnnotate_Update* *g:frawormap_AuAnnotate_Next*
              *g:frawormap_AuAnnotate_Prev*       *aurum-m-AuAnnotate_Prev*
                  *aurum-m-AuAnnotate_Enter*      *aurum-m-AuAnnotate_Fdiff*
-                 *aurum-m-AuAnnotate_RFdiff*     *aurum-m-AuAnnotate_Diff*
+                 *aurum-m-AuAnnotate_RFdiff*     *aurum-m-AuAnnotate_FVdiff*
+                 *aurum-m-AuAnnotate_Diff*       *aurum-m-AuAnnotate_RFVdiff*
                  *aurum-m-AuAnnotate_Rdiff*      *aurum-m-AuAnnotate_Annotate*
                  *aurum-m-AuAnnotate_RVdiff*     *aurum-m-AuAnnotate_Open*
                  *aurum-m-AuAnnotate_Update*     *aurum-m-AuAnnotate_Next*
     Mapname  LHS  Description ~
     Enter    <CR> View changes between file and current revision (|vimdiff|)
+    FVdiff   gD   View diff between working directory and current revision 
+                  (using |vimdiff| in multiple tabs)
+    RFVdiff  gC   View diff between current revision and its first parent 
+                  (using |vimdiff| in multiple tabs)
     Fdiff    gd   View diff between working directory and current revision
-    RFdiff   gc   View diff between current revision and its parent
+    RFdiff   gc   View diff between current revision and its first parent
     Diff      d   View diff between file and current revision
     Rdiff     c   Like Fdiff, but view only changes made to annotated file
     Vdiff     D   Same as Enter
     all changes.
     Local mappings (mgid=AuDiff, without leader by default):
                                                           *g:frawormap_AuDiff*
-                         *g:frawormap_AuDiff_Next* *g:frawormap_AuDiff_Prev*
+                         *g:frawormap_AuDiff_Next* *g:frawormap_AuDiff_FVdiff*
+                         *g:frawormap_AuDiff_Prev* *g:frawormap_AuDiff_Vdiff*
                          *g:frawormap_AuDiff_Open* *g:frawormap_AuDiff_Update*
-                         *g:frawormap_AuDiff_Vdiff*    *aurum-m-AuDiff_Vdiff*
-                             *aurum-m-AuDiff_Next*     *aurum-m-AuDiff_Prev*
+                             *aurum-m-AuDiff_Next*     *aurum-m-AuDiff_FVdiff*
+                             *aurum-m-AuDiff_Prev*     *aurum-m-AuDiff_Vdiff*
                              *aurum-m-AuDiff_Open*     *aurum-m-AuDiff_Update*
     Mapname  LHS  Description ~
     Next      K   Change all revisions specified to aurum://diff to their 
                   and fallback to {rev2} version. If {rev1} is not empty, it 
                   will open {rev1} version.
     Vdiff     D   View diff section under cursor using |vimdiff|.
+    FVdiff    gD  View the whole diff in a multiple tabs using |vimdiff|.
 
     Note These mappings are defined only when buffer is created using 
          |:AuDiff| command or Prev/Next above mappings.
     Open log for given repo. {opts} are the same as used by |:AuLog|.
     Local mappings (mgid=AuLog, current revision=revision under cursor):
                                                            *g:frawormap_AuLog*
-                         *g:frawormap_AuLog_File* *g:frawormap_AuLog_Enter*
-                         *g:frawormap_AuLog_User* *g:frawormap_AuLog_Update*
-                         *g:frawormap_AuLog_Rev*  *g:frawormap_AuLog_Branch*
-                         *g:frawormap_AuLog_Diff* *g:frawormap_AuLog_Fdiff*
-                         *g:frawormap_AuLog_Next* *g:frawormap_AuLog_RFdiff*
-                         *g:frawormap_AuLog_Prev* *g:frawormap_AuLog_Rdiff*
-                         *g:frawormap_AuLog_Open* *g:frawormap_AuLog_Annotate*
-                         *g:frawormap_AuLog_Date*     *aurum-m-AuLog_Date*
-                             *aurum-m-AuLog_File*     *aurum-m-AuLog_Enter*
-                             *aurum-m-AuLog_User*     *aurum-m-AuLog_Update*
-                             *aurum-m-AuLog_Rev*      *aurum-m-AuLog_Branch*
-                             *aurum-m-AuLog_Diff*     *aurum-m-AuLog_Fdiff*
-                             *aurum-m-AuLog_Next*     *aurum-m-AuLog_RFdiff*
-                             *aurum-m-AuLog_Prev*     *aurum-m-AuLog_Rdiff*
-                             *aurum-m-AuLog_Open*     *aurum-m-AuLog_Annotate*
+                        *g:frawormap_AuLog_File*  *g:frawormap_AuLog_Enter*
+                        *g:frawormap_AuLog_User*  *g:frawormap_AuLog_Update*
+                        *g:frawormap_AuLog_Rev*   *g:frawormap_AuLog_Branch*
+                        *g:frawormap_AuLog_Diff*  *g:frawormap_AuLog_FVdiff*
+                        *g:frawormap_AuLog_Fdiff* *g:frawormap_AuLog_RFVdiff*
+                        *g:frawormap_AuLog_Next*  *g:frawormap_AuLog_RFdiff*
+                        *g:frawormap_AuLog_Prev*  *g:frawormap_AuLog_Rdiff*
+                        *g:frawormap_AuLog_Open*  *g:frawormap_AuLog_Annotate*
+                        *g:frawormap_AuLog_Date*      *aurum-m-AuLog_Date*
+                            *aurum-m-AuLog_File*      *aurum-m-AuLog_Enter*
+                            *aurum-m-AuLog_User*      *aurum-m-AuLog_Update*
+                            *aurum-m-AuLog_Rev*       *aurum-m-AuLog_Branch*
+                            *aurum-m-AuLog_Diff*      *aurum-m-AuLog_Fdiff*
+                            *aurum-m-AuLog_Next*      *aurum-m-AuLog_RFdiff*
+                            *aurum-m-AuLog_Prev*      *aurum-m-AuLog_Rdiff*
+                            *aurum-m-AuLog_Open*      *aurum-m-AuLog_Annotate*
     Mapname  LHS  Description ~
     Enter    <CR> Depending on element under cursor:
                   Element          Action ~
                   used)
     User     gu   View only revisions that were added by the user who added 
                   current one
-    Date     gD   View only revisions that were added in the same month as 
+    Date     gM   View only revisions that were added in the same month as 
                   current one
     Branch   gb   View only revisions that are in the same branch as current
     Rev      gr   View only ancestors of current revision
-    Fdiff    gd   View changes between repository state at current revision 
-                  and working directory state
+    FVdiff   gD   View changes between current revision and working directory 
+                  state in a multiple tabs each containing |vimdiff|
+    RFVdiff  gC   View all changes introduced by current revision as 
+                  a |vimdiff| in a multiple tabs
+    Fdiff    gd   View changes between current revision and working directory 
+                  state
     RFdiff   gc   View changes introduced by current revision
     Diff      d   View diff between working directory and current revision 
                   versions of one of the files
                                                         *g:frawormap_AuStatus*
                  *g:frawormap_AuStatus_Open*   *g:frawormap_AuStatus_ROpen*
                  *g:frawormap_AuStatus_RFdiff* *g:frawormap_AuStatus_Fdiff*
+                 *g:frawormap_AuStatus_FVdiff* *g:frawormap_AuStatus_RFVdiff*
                  *g:frawormap_AuStatus_Diff*   *g:frawormap_AuStatus_vDiff*
                  *g:frawormap_AuStatus_Rdiff*  *g:frawormap_AuStatus_vRdiff*
                  *g:frawormap_AuStatus_Vdiff*  *g:frawormap_AuStatus_RVdiff*
+                 *g:frawormap_AuStatus_vVdiff* *g:frawormap_AuStatus_vRVdiff*
                  *g:frawormap_AuStatus_Commit* *g:frawormap_AuStatus_Annotate*
                  *g:frawormap_AuStatus_Track*  *g:frawormap_AuStatus_vCommit*
                  *g:frawormap_AuStatus_vTrack* *g:frawormap_AuStatus_Forget*
                  *g:frawormap_AuStatus_vForget*    *aurum-m-AuStatus_vForget*
                      *aurum-m-AuStatus_Open*       *aurum-m-AuStatus_ROpen*
                      *aurum-m-AuStatus_RFdiff*     *aurum-m-AuStatus_Fdiff*
+                     *aurum-m-AuStatus_FVdiff*     *aurum-m-AuStatus_RFVdiff*
                      *aurum-m-AuStatus_Diff*       *aurum-m-AuStatus_vDiff*
                      *aurum-m-AuStatus_Rdiff*      *aurum-m-AuStatus_vRdiff*
                      *aurum-m-AuStatus_Vdiff*      *aurum-m-AuStatus_RVdiff*
+                     *aurum-m-AuStatus_vVdiff*     *aurum-m-AuStatus_vRVdiff*
                      *aurum-m-AuStatus_Commit*     *aurum-m-AuStatus_Annotate*
                      *aurum-m-AuStatus_Track*      *aurum-m-AuStatus_vCommit*
                      *aurum-m-AuStatus_vTrack*     *aurum-m-AuStatus_Forget*
     RFdiff   gd   View changes introduced by first revision
     Fdiff    gc   View diff between two revisions (or working directory state 
                   and first revision)
+    RFVdiff  gD   Like RFdiff, but show them in multiple tabs using |vimdiff|
+    FVdiff   gC   Like Fdiff,  but show them in multiple tabs using |vimdiff|
     Diff      d   Like RFdiff, but show only current file
     vDiff     d   Like RFdiff, but shows files selected in visual mode
     Rdiff     c   Like Fdiff, but show only current file
     vRdiff    c   Like Fdiff, but shows files selected in visual mode
     Vdiff     D   Like Diff, but use |vimdiff| to view changes
+    vVdiff    D   Like FVdiff, but limit files only to selected ones
     RVdiff    C   Like Rdiff, but use |vimdiff| to view changes
+    vRVdiff   C   Like RFVdiff, but limit files only to selected ones
     Annotate  a   Open file under cursor and annotate it
     Commit    i   Commit changes made to file under cursor
     vCommit   i   Commit changes made to selected files
     1.1: Added “requiresbvar” _f.newcommand option
 @aurum/vimdiff:
     0.1: Added full, untracked, onlymodified and files options to |:AuVimDiff|
+    0.2: Added _r.vimdiff.full
 @aurum/log:
     0.1: Added |aurum-style-git| and |aurum-style-gitoneline| log styles, 
          added skipping of $rev if |aurum-repo.hasrevisions| is false, made it 

ftplugin/aurumannotate.vim

 setlocal nomodeline
 execute frawor#Setup('0.0', {'@aurum/repo': '2.0',
             \             '@aurum/bufvars': '0.0',
-            \             '@aurum/vimdiff': '0.0',
+            \             '@aurum/vimdiff': '0.2',
             \            '@aurum/annotate': '0.0',
             \                '@aurum/edit': '1.0',
             \                 '@/mappings': '0.0',
     "▶2 Various *diff actions
     if a:action[-4:] is# 'diff'
         if a:action[:2] is# 'rev'
-            let rev1=get(bvar.repo.functions.getcs(bvar.repo, hex).parents,0,'')
+            let cs1=bvar.repo.functions.getcs(bvar.repo, hex)
+            if empty(cs1.parents)
+                return
+            endif
+            let rev1=cs1.parents[0]
         elseif bvar.rev isnot# bvar.repo.functions.getworkhex(bvar.repo)
             let rev1=bvar.rev
         else
             setlocal noscrollbind
         endif
         if a:action[-7:-5] is# 'vim'
-            if empty(rev1)
-                let file1=s:_r.os.path.join(bvar.repo.path, bvar.file)
-                let existed=bufexists(file1)
-                if filereadable(file1)
-                    execute 'silent edit' fnameescape(file1)
+            if a:0 && a:1
+                return s:_r.vimdiff.full(bvar.repo,
+                            \            [(empty(rev1)?(0):(rev1)), rev2],
+                            \            0, [], 0)
+            else
+                if empty(rev1)
+                    let file1=s:_r.os.path.join(bvar.repo.path, bvar.file)
+                    let existed=bufexists(file1)
+                    if filereadable(file1)
+                        execute 'silent edit' fnameescape(file1)
+                    else
+                        call s:_f.throw('norfile', file1)
+                    endif
                 else
-                    call s:_f.throw('norfile', file1)
+                    try
+                        let existed=s:_r.run('silent edit', 'file', bvar.repo,
+                                    \        rev1, file)
+                    catch /\V\^Frawor:\[^:]\+:nofile:/
+                        call s:_f.throw('nofile', file, rev1)
+                    endtry
                 endif
-            else
-                try
-                    let existed=s:_r.run('silent edit', 'file', bvar.repo, rev1,
-                                \        file)
-                catch /\V\^Frawor:\[^:]\+:nofile:/
-                    call s:_f.throw('nofile', file, rev1)
-                endtry
-            endif
-            if existed
-                setlocal bufhidden=wipe
-                unlet existed
-            endif
-            call s:_r.vimdiff.split(s:_r.fname('file',bvar.repo,rev2,file), -1)
-            if empty(rev1)
-                wincmd p
+                if existed
+                    setlocal bufhidden=wipe
+                    unlet existed
+                endif
+                call s:_r.vimdiff.split(s:_r.fname('file',bvar.repo,rev2,file),
+                            \           -1)
+                if empty(rev1)
+                    wincmd p
+                endif
             endif
         else
             if empty(rev1)
 "▲2
 call s:_f.mapgroup.add('AuAnnotate', {
             \    'Enter': {'lhs': '<CR>', 'rhs': s:F.getrhs(   'vimdiff'   )},
+            \   'FVdiff': {'lhs': 'gD',   'rhs': s:F.getrhs(   'vimdiff', 1)},
+            \  'RFVdiff': {'lhs': 'gC',   'rhs': s:F.getrhs('revvimdiff', 1)},
             \    'Fdiff': {'lhs': 'gd',   'rhs': s:F.getrhs(      'diff', 1)},
             \   'RFdiff': {'lhs': 'gc',   'rhs': s:F.getrhs('rev'.'diff', 1)},
             \     'Diff': {'lhs':  'd',   'rhs': s:F.getrhs(      'diff'   )},

ftplugin/aurumlog.vim

             \'nocontents': 'Log is empty',
         \}
 let s:ignkeys=['crrestrict', 'filepats', 'revs', 'cmd', 'repo']
-"▶1 bisect :: [a], function + self → a
-function s:F.bisect(list, function)
-    let llist=len(a:list)
-    let lborder=0
-    let rborder=llist-1
-    let lres=call(a:function, [a:list[lborder]], self)
-    if lres<=0
-        return a:list[lborder]
-    endif
-    let rres=call(a:function, [a:list[rborder]], self)
-    if rres>=0
-        return a:list[rborder]
-    endif
-    let totest='r'
-    let cur=(((rborder+1)/2)-1)
-    while lborder!=rborder
-        let res=call(a:function, [a:list[cur]], self)
-        if res==0
-            return a:list[cur]
-        else
-            let shift=((rborder-lborder)/2)
-            if shift==0
-                let shift=1
-            endif
-            let {(res>0)?('l'):('r')}border=cur
-            let cur=lborder+shift
-        endif
-    endwhile
-    return a:list[lborder]
-endfunction
-"▶1 checkinblock :: block → -1|0|1
-function s:F.checkinblock(block)
-    let curline=line('.')-1
-    return       ((curline<a:block[0][0])?(-1):
-                \((curline>a:block[1][0])?( 1):
-                \                         ( 0)))
-endfunction
-"▶1 getblock :: bvar + cursor, bvar → block
-function s:F.getblock(bvar)
-    if empty(a:bvar.rectangles)
-        call s:_f.throw('nocontents')
-    endif
-    return s:F.bisect(a:bvar.rectangles, s:F.checkinblock)
-endfunction
 "▶1 findCurSpecial :: bvar, hex, blockstart + cursor → special
 "▶2 s:spSort :: special, special → -1|0|1
 let s:sufweights={'-': 3, 'r': 2, 'l': 1, 'R': 0,}
 function s:F.cr(...)
     "▶2 Get changeset, current special, encode options
     let bvar=s:_r.bufvars[bufnr('%')]
-    let [blockstart, blockend, hex]=s:F.getblock(bvar)
+    let [blockstart, blockend, hex]=bvar.getblock(bvar)
     if a:0
         let spname=a:1
     else
     "▲3
     return s:F.cwin(bvar).":silent ".cmd."\n"
 endfunction
+"▶1 fvdiff
+function s:F.fvdiff(...)
+    let bvar=s:_r.bufvars[bufnr('%')]
+    let hex=bvar.getblock(bvar)[2]
+    let cmd=':AuVimDiff full noonlymodified '
+    if !a:0
+        return cmd.'curfile '.hex."\n"
+    else
+        let cs=bvar.repo.changesets[hex]
+        if !empty(cs.parents)
+            return cmd.hex.' '.cs.parents[0]."\n"
+        endif
+    endif
+    return ''
+endfunction
 "▶1 gethexfile
 function s:F.gethexfile()
     let bvar=s:_r.bufvars[bufnr('%')]
-    let [blockstart, blockend, hex]=s:F.getblock(bvar)
+    let [blockstart, blockend, hex]=bvar.getblock(bvar)
     let spname=s:F.findCurSpecial(bvar, hex, blockstart[0])
     let cs=bvar.repo.changesets[hex]
     let file=0
 function s:F.findfirstvisible(n)
     let bvar=s:_r.bufvars[bufnr('%')]
     let repo=bvar.repo
-    let [blockstart, blockend, hex]=s:F.getblock(bvar)
+    let hex=bvar.getblock(bvar)[2]
     let n=abs(a:n)
     let direction=((a:n>0)?('parents'):('children'))
     let tocheck=[]
 "▶1 update
 function s:F.update()
     let bvar=s:_r.bufvars[bufnr('%')]
-    let [blockstart, blockend, hex]=s:F.getblock(bvar)
+    let hex=bvar.getblock(bvar)[2]
     call s:_r.repo.update(bvar.repo, hex, v:count)
     return "\<C-\>\<C-n>:silent edit\n"
 endfunction
             \   'Enter': {'lhs': "\n", 'rhs': [],                             },
             \    'File': {'lhs': 'gF', 'rhs': s:F.filehistory                 },
             \    'User': {'lhs': 'gu', 'rhs': ['user']                        },
-            \    'Date': {'lhs': 'gD', 'rhs': ['time']                        },
+            \    'Date': {'lhs': 'gM', 'rhs': ['time']                        },
             \  'Branch': {'lhs': 'gb', 'rhs': ['branch']                      },
             \     'Rev': {'lhs': 'gr', 'rhs': ['rev']                         },
+            \  'FVdiff': {'lhs': 'gD', 'rhs': s:F.fvdiff                      },
+            \ 'RFVdiff': {'lhs': 'gC', 'rhs': [1],         'func': s:F.fvdiff },
             \   'Fdiff': {'lhs': 'gd', 'rhs': ['curdiff']                     },
             \  'RFdiff': {'lhs': 'gc', 'rhs': ['revdiff']                     },
             \    'Diff': {'lhs':  'd', 'rhs': [1],         'func': s:F.diff   },

ftplugin/aurumstatus.vim

 setlocal noswapfile
 setlocal nomodeline
 execute frawor#Setup('0.0', {'@aurum/bufvars': '0.0',
-            \                '@aurum/vimdiff': '0.0',
+            \                '@aurum/vimdiff': '0.2',
+            \               '@aurum/cmdutils': '0.0',
             \                   '@aurum/edit': '1.0',
             \                 '@aurum/commit': '0.0',
             \                    '@/mappings': '0.0',
     let vline1=line("'<")
     let vline2=line("'>")
     let file=bvar.files[curline]
+    let manyfiles=(visual || v:count1>1)
     if has_key(s:noacttypes, a:action) &&
+                \!(a:action[-7:] is# 'vimdiff' && manyfiles) &&
                 \index(s:noacttypes[a:action], bvar.types[curline])!=-1
         return ''
     endif
-    if !(a:action is# 'commit' || a:action is# 'track' || a:action is# 'forget')
+    if !(a:action is# 'commit' || a:action is# 'track' || a:action is# 'forget'
+                \|| a:action[-11:] is# 'fullvimdiff' ||
+                \(a:action[-7:] is# 'vimdiff' && manyfiles))
         if isrecord
             let [lwnr, rwnr, swnr]=bvar.getwnrs()
             execute lwnr.'wincmd w'
         call s:_r.run('silent edit', 'diff', bvar.repo, rev1, rev2, [], {})
     elseif a:action is# 'revfulldiff'
         call s:_r.run('silent edit', 'diff', bvar.repo, rev1,  '',  [], {})
-    elseif a:action is# 'revvimdiff' || a:action is# 'vimdiff'
+    elseif a:action is# 'fullvimdiff'
+        execute 'AuVimDiff full '.((empty(rev2) || empty(rev1))?
+                    \                   ('curfile '):
+                    \                   ('')).rev1.' '.rev2
+    elseif a:action is# 'revfullvimdiff'
+        let cs1=bvar.repo.functions.getcs(bvar.repo, rev1)
+        if !empty(cs1.parents)
+            call s:_r.vimdiff.full(bvar.repo, [rev1, cs1.parents[0]], 1, [], 0)
+        endif
+    elseif !manyfiles && (a:action is# 'revvimdiff' || a:action is# 'vimdiff')
         let file1=s:_r.fname('file', bvar.repo, rev1, file)
-        if empty(rev2) || a:action is# 'vimdiff'
+        if a:action is# 'revvimdiff'
+            let cs1=bvar.repo.functions.getcs(bvar.repo, rev1)
+            if empty(cs1.parents)
+                return
+            else
+                let file2=s:_r.fname('file', bvar.repo, cs1.parents[0], file)
+            endif
+        elseif empty(rev2)
             let file2=s:_r.os.path.join(bvar.repo.path, file)
         else
             let file2=s:_r.fname('file', bvar.repo, rev2, file)
         endif
         let range=range(curline, rborder)
     endif
-    if has_key(s:noacttypes, a:action)
+    if has_key(s:noacttypes, a:action) && a:action[-7:] isnot# 'vimdiff'
         call filter(range,
                     \'index(s:noacttypes[a:action], bvar.types[v:val])==-1')
     endif
                             \   'execute "autocmd AuStatusCommit '.
                             \                    'BufEnter <buffer='.buf.'> '.
                             \                    'nested '.
-                            \               'execute \"autocmd! AuStatusCommit '.
-                            \                           'BufEnter '
+                            \              'execute \"autocmd! AuStatusCommit '.
+                            \                          'BufEnter '
                             \                           '<buffer='.buf.'>\" | '.
-                            \                   'silent edit!" | '.
+                            \              'silent edit!" | '.
                             \'endif'
             augroup END
         endif
         call s:_r.run('silent edit', 'diff', bvar.repo, rev1,  '',  files, {})
     elseif a:action is# 'revdiff'
         call s:_r.run('silent edit', 'diff', bvar.repo, rev1, rev2, files, {})
+    elseif a:action is# 'revvimdiff' || a:action is# 'vimdiff'
+        let args=[bvar.repo]
+        if a:action is# 'revvimdiff'
+            let cs1=bvar.repo.functions.getcs(bvar.repo, rev1)
+            if empty(cs1.parents)
+                return
+            endif
+            let args+=[[cs1.parents[0], rev1]]
+        else
+            if empty(rev2)
+                let args+=[[0, rev1]]
+            else
+                let args+=[[rev1, rev2]]
+            endif
+        endif
+        let args+=[2, files, 0]
+        return call(s:_r.vimdiff.full, args, {})
     endif
 endfunction
 let s:_augroups+=['AuStatusCommit']
 endfunction
 "▲2
 call s:_f.mapgroup.add('AuStatus', {
-            \    'Exit': {'lhs':  'X',   'rhs': ':<C-u>bwipeout!<CR>'    },
-            \    'Open': {'lhs': '<CR>', 'rhs': s:F.getrhs(       'open')},
-            \   'ROpen': {'lhs':  'o',   'rhs': s:F.getrhs(    'revopen')},
-            \  'RFdiff': {'lhs': 'gd',   'rhs': s:F.getrhs('revfulldiff')},
-            \   'Fdiff': {'lhs': 'gc',   'rhs': s:F.getrhs(   'fulldiff')},
-            \    'Diff': {'lhs':  'd',   'rhs': s:F.getrhs(       'diff')},
-            \   'vDiff': {'lhs':  'd',   'rhs': s:F.getrhs(       'diff', 1),
+            \    'Exit': {'lhs':  'X',   'rhs': ':<C-u>bwipeout!<CR>'       },
+            \    'Open': {'lhs': '<CR>', 'rhs': s:F.getrhs(          'open')},
+            \   'ROpen': {'lhs':  'o',   'rhs': s:F.getrhs(       'revopen')},
+            \ 'RFVdiff': {'lhs': 'gD',   'rhs': s:F.getrhs('revfullvimdiff')},
+            \  'FVdiff': {'lhs': 'gC',   'rhs': s:F.getrhs(   'fullvimdiff')},
+            \  'RFdiff': {'lhs': 'gd',   'rhs': s:F.getrhs(   'revfulldiff')},
+            \   'Fdiff': {'lhs': 'gc',   'rhs': s:F.getrhs(      'fulldiff')},
+            \    'Diff': {'lhs':  'd',   'rhs': s:F.getrhs(          'diff')},
+            \   'vDiff': {'lhs':  'd',   'rhs': s:F.getrhs(          'diff', 1),
+            \             'mode': 'x'},
+            \   'Rdiff': {'lhs':  'c',   'rhs': s:F.getrhs(       'revdiff')},
+            \  'vRdiff': {'lhs':  'c',   'rhs': s:F.getrhs(       'revdiff', 1),
+            \             'mode': 'x'},
+            \   'Vdiff': {'lhs':  'D',   'rhs': s:F.getrhs(       'vimdiff')},
+            \  'vVdiff': {'lhs':  'D',   'rhs': s:F.getrhs(       'vimdiff', 1),
+            \             'mode': 'x'},
+            \  'RVdiff': {'lhs':  'C',   'rhs': s:F.getrhs(    'revvimdiff')},
+            \ 'vRVdiff': {'lhs':  'C',   'rhs': s:F.getrhs(    'revvimdiff', 1),
+            \             'mode': 'x'},
+            \'Annotate': {'lhs':  'a',   'rhs': s:F.getrhs(      'annotate')},
+            \  'Commit': {'lhs':  'i',   'rhs': s:F.getrhs(        'commit')},
+            \ 'vCommit': {'lhs':  'i',   'rhs': s:F.getrhs(        'commit', 1),
             \             'mode': 'v'},
-            \   'Rdiff': {'lhs':  'c',   'rhs': s:F.getrhs(    'revdiff')},
-            \  'vRdiff': {'lhs':  'c',   'rhs': s:F.getrhs(    'revdiff', 1),
+            \   'Track': {'lhs':  'A',   'rhs': s:F.getrhs(         'track')},
+            \  'vTrack': {'lhs':  'A',   'rhs': s:F.getrhs(         'track', 1),
             \             'mode': 'v'},
-            \   'Vdiff': {'lhs':  'D',   'rhs': s:F.getrhs(    'vimdiff')},
-            \  'RVdiff': {'lhs':  'C',   'rhs': s:F.getrhs( 'revvimdiff')},
-            \'Annotate': {'lhs':  'a',   'rhs': s:F.getrhs(   'annotate')},
-            \  'Commit': {'lhs':  'i',   'rhs': s:F.getrhs(     'commit')},
-            \ 'vCommit': {'lhs':  'i',   'rhs': s:F.getrhs(     'commit', 1),
-            \             'mode': 'v'},
-            \   'Track': {'lhs':  'A',   'rhs': s:F.getrhs(      'track')},
-            \  'vTrack': {'lhs':  'A',   'rhs': s:F.getrhs(      'track', 1),
-            \             'mode': 'v'},
-            \  'Forget': {'lhs':  'R',   'rhs': s:F.getrhs(     'forget')},
-            \ 'vForget': {'lhs':  'R',   'rhs': s:F.getrhs(     'forget', 1),
+            \  'Forget': {'lhs':  'R',   'rhs': s:F.getrhs(        'forget')},
+            \ 'vForget': {'lhs':  'R',   'rhs': s:F.getrhs(        'forget', 1),
             \             'mode': 'v'},
             \}, {'func': s:F.runmap, 'silent': 1, 'mode': 'n'})
 "▶1
     if a:0==0
         let target='.'
         let files=[repo.functions.reltorepo(repo,
-                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', -1)[3])]
+                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', 'getfile')[3])]
     elseif a:0==1 && isdirectory(a:1)
         let target=a:1
         let files=[repo.functions.reltorepo(repo,
-                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', -1)[3])]
+                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', 'getfile')[3])]
     elseif a:0>1 && get(a:opts, 'rightrepl', 0)
         let patterns=map(a:000[:-2], 's:_r.globtopat('.
                     \                'repo.functions.reltorepo(repo,v:val), 1)')
     elseif a:0==2 && !isdirectory(a:2) && filewritable(a:1)
         let fst=a:1
         if fst is# ':'
-            let fst=s:_r.cmdutils.getrrf(rrfopts, 'nocurf', -1)[3]
+            let fst=s:_r.cmdutils.getrrf(rrfopts, 'nocurf', 'getfile')[3]
         endif
         let moves = {repo.functions.reltorepo(repo, fst):
                     \repo.functions.reltorepo(repo, a:2)}
         endif
         let files=s:F.filterfiles(repo, globs, allfiles)
         if hascur
-            let files+=[s:_r.cmdutils.getrrf(rrfopts, 'nocurf', -1)[3]]
+            let files+=[s:_r.cmdutils.getrrf(rrfopts, 'nocurf', 'getfile')[3]]
         endif
     endif
     if exists('files')
     if hascur
         let rrfopts={'repo': repo.path}
         let files+=[repo.functions.reltorepo(repo,
-                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', -1)[3])]
+                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', 'getfile')[3])]
     endif
     for key in filter(['forget', 'remove', 'ignore'], 'eval(v:val)')
         call map(copy(files), 'repo.functions[key](repo, v:val)')
     if hascur
         let rrfopts={'repo': repo.path}
         let files+=[repo.functions.reltorepo(repo,
-                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', -1)[3])]
+                    \s:_r.cmdutils.getrrf(rrfopts, 'nocurf', 'getfile')[3])]
     endif
     call map(copy(files), 'repo.functions.add(repo, v:val)')
 endfunction
     let utype=get(opts, 'url', 'html')
     if utype is# 'html' || utype is# 'annotate' || utype is# 'raw'
                 \       || utype is# 'filehist'
-        let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 'nocurf', 0)
+        let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 'nocurf',
+                    \                                      'get')
         call s:_r.cmdutils.checkrepo(repo)
         let file=s:F.urlescape(file)
         if rev is 0

plugin/aurum/annotate.vim

 " TODO Investigate why wiping out annotate buffer causes consumption of next
 "      character under wine
 function s:annfunc.function(opts)
-    let [hasannbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 'noafile', 1)
+    let [hasannbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 'noafile',
+                \                                         'annotate')
     if repo is 0
         return
     endif

plugin/aurum/cmdutils.vim

     endif
     return file
 endfunction
-"▶1 rrf buffer functions :: bvar, opts, ann, failmsg → scope
+"▶1 rrf buffer functions :: bvar, opts, act, failmsg → scope
 let s:rrf={}
 "▶2 rrf.file : bvar → (repo, rev, file)
-function s:rrf.file(bvar, opts, ann, failmsg)
+function s:rrf.file(bvar, opts, act, failmsg)
     return {'hasbuf': 1,
            \  'repo': a:bvar.repo,
            \   'rev': a:bvar.rev,
            \  'file': a:bvar.file,}
 endfunction
 "▶2 rrf.copy : bvar → (file), file → (repo), 0 → (rev)
-function s:rrf.copy(bvar, opts, ann, failmsg)
+function s:rrf.copy(bvar, opts, act, failmsg)
     let r={}
-    if a:ann==-1
+    if a:act is# 'getfile'
         let r.file=a:bvar.file
     else
         let r.repo=s:_r.repo.get(s:_r.os.path.dirname(a:bvar.file))
     return r
 endfunction
 "▶2 rrf.status : bvar → (repo, rev), . → (file)
-function s:rrf.status(bvar, opts, ann, failmsg)
+function s:rrf.status(bvar, opts, act, failmsg)
     let r={}
     let r.repo=a:bvar.repo
     let  r.rev=get(a:bvar.opts, 'rev1', 0)
         if a:failmsg isnot 0
             call s:_f.throw(a:failmsg)
         endif
+    elseif a:act is# 'getfiles'
+        let r.files=a:bvar.files
     else
         let r.file=a:bvar.files[line('.')-1]
     endif
-    if a:ann>=0
+    if a:act is# 'annotate' || a:act is# 'open'
         topleft new
     endif
     return r
 endfunction
 "▶2 rrf.diff : bvar → (repo, rev, file(s))
-function s:rrf.diff(bvar, opts, ann, failmsg)
+function s:rrf.diff(bvar, opts, act, failmsg)
     let r={}
     let r.repo=a:bvar.repo
     let  r.rev=empty(a:bvar.rev2) ? a:bvar.rev1 : a:bvar.rev2
-    " XXX Maybe it should pull in all filenames instead when ann=-2?
+    " XXX Maybe it should pull in all filenames instead when act='getfiles'?
     let r.file=s:F.getdifffile(a:bvar)
     if r.file is 0 && a:failmsg isnot 0
         return 0
     endif
-    if a:ann>=0
+    if a:act is# 'annotate' || a:act is# 'open'
         leftabove vnew
     endif
     return r
 endfunction
 "▶2 rrf.commit : bvar → (repo, file(s))
-function s:rrf.commit(bvar, opts, ann, failmsg)
+function s:rrf.commit(bvar, opts, act, failmsg)
     let r={}
     let r.repo=a:bvar.repo
-    if a:ann==-2
+    if a:act is# 'getfiles'
         let r.files=a:bvar.files
     else
         let r.file=s:F.getfile(a:bvar.files)
         if r.file is 0 && a:failmsg isnot 0
             return 0
         endif
-        if a:ann>=0
+        if a:act is# 'annotate' || a:act is# 'open'
             topleft new
         endif
     endif
     return r
 endfunction
 "▶2 rrf.annotate : bvar → (repo), . → (rev, file)
-function s:rrf.annotate(bvar, opts, ann, failmsg)
+function s:rrf.annotate(bvar, opts, act, failmsg)
     let r={}
     let r.repo=a:bvar.repo
-    if a:ann==-2
+    if a:act is# 'getfiles'
         let r.file=a:bvar.file
     else
         let r.file=a:bvar.files[line('.')-1]
         if !has_key(a:opts, 'rev')
             let r.rev=a:bvar.revisions[line('.')-1]
             if r.rev is# r.repo.functions.getrevhex(r.repo, a:bvar.rev)
-                if a:ann!=1
+                if a:act isnot# 'annotate'
                     " Don't do the following if we are not annotating
                 elseif has_key(a:bvar, 'annbuf') &&
                             \bufwinnr(a:bvar.annbuf)!=-1
                 return 0
             endif
         endif
-        if a:ann>=0
+        if a:act is# 'annotate' || a:act is# 'open'
             if winnr('$')>1
-                wincmd c
+                close
             endif
             if has_key(a:bvar, 'annbuf') && bufwinnr(a:bvar.annbuf)!=-1
                 execute bufwinnr(a:bvar.annbuf).'wincmd w'
     endif
     return r
 endfunction
+"▶2 rrf.log : bvar → repo, . → (rev), 0 → (file)
+function s:rrf.log(bvar, opts, act, failmsg)
+    return {'repo': a:bvar.repo,
+           \ 'rev': a:bvar.getblock(a:bvar)[2],
+           \'file': 0,}
+endfunction
 "▲2
-"▶1 getrrf :: opts, failmsg, ann + buf → (hasbuf, repo, rev, file)
+"▶1 getrrf :: opts, failmsg, act + buf → (hasbuf, repo, rev, file)
 let s:rrffailresult=[0, 0, 0, 0]
-function s:F.getrrf(opts, failmsg, ann)
+function s:F.getrrf(opts, failmsg, act)
     let hasbuf=0
     let file=0
     "▶2 a:opts.file file → (repo?)
     if has_key(a:opts, 'file') && a:opts.file isnot# ':'
-        if a:ann!=-1 && a:opts.repo is# ':'
+        if a:act isnot# 'getfile' && a:opts.repo is# ':'
             let repo=s:_r.repo.get(s:_r.os.path.dirname(a:opts.file))
             let file=repo.functions.reltorepo(repo, a:opts.file)
         else
         if index(a:opts.files, ':')!=-1
             let newopts=copy(a:opts)
             unlet newopts.files
-            let [repo, rev, file]=s:F.getrrf(newopts, 'nocurf', -1)[1:]
+            let [repo, rev, file]=s:F.getrrf(newopts, 'nocurf', 'getfile')[1:]
             if repo is 0
                 unlet repo
                 let repo=s:_r.repo.get(file)
             endif
-            if a:ann!=-2 && repo isnot 0
+            if repo isnot 0
                 let file=repo.functions.reltorepo(repo, file)
             endif
             let files+=[file]
                 \has_key(s:_r.bufvars[bufnr('%')], 'command')
         let bvar=s:_r.bufvars[bufnr('%')]
         if has_key(s:rrf, bvar.command)
-            let res=call(s:rrf[bvar.command], [bvar,a:opts,a:ann,a:failmsg], {})
+            let res=call(s:rrf[bvar.command], [bvar,a:opts,a:act,a:failmsg], {})
             if res is 0
                 return s:rrffailresult
             else
         endif
     "▶2 buf → (repo, file), (rev=0)
     elseif filereadable(expand('%'))
-        if a:ann==-1
+        if a:act is# 'getfile'
             let file=expand('%')
         else
             let repo=s:_r.repo.get(':')
         call repo.functions.updatechangesets(repo)
     endif
     "▲2
-    if a:ann!=-1
+    if a:act isnot# 'getfile'
         "▶2 repo
         if !exists('repo')
             let repo=s:_r.repo.get(a:opts.repo)
             if type(repo)!=type({})
                 call s:_f.throw('nrepo')
             endif
-            let file=repo.functions.reltorepo(repo, file)
+            if file isnot 0
+                let file=repo.functions.reltorepo(repo, file)
+            endif
         endif
         "▶2 rev
-        if a:ann==-2
+        if a:act is# 'getfiles'
             let rev=0
         elseif has_key(a:opts, 'rev')
             let oldrev=0
         "▲2
     endif
     return [hasbuf, exists('repo') ? repo : 0, rev,
-                \((a:ann==-2)?
+                \((a:act is# 'getfiles')?
                 \   ((exists('files'))?
                 \       (files):
                 \   ((file is 0)?

plugin/aurum/commit.vim

     endif
     let [repo, rev, files]=s:_r.cmdutils.getrrf(rrfopts,
                 \                               ((a:0)?(0):('nocfile')),
-                \                               -2)[1:]
+                \                               'getfiles')[1:]
     call s:_r.cmdutils.checkrepo(repo)
     let status=repo.functions.status(repo)
     "▶2 Get file list

plugin/aurum/diff.vim

 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.0', {'@aurum/cmdutils': '0.0',
                 \                 '@aurum/bufvars': '0.0',
+                \                 '@aurum/vimdiff': '0.2',
                 \                    '@aurum/repo': '2.0',
                 \                    '@aurum/edit': '1.0',
                 \                           '@/os': '0.0',
                     \                       'index(filelist, v:val)==-1')
     endfor
     if hascur
-        let curfile=s:_r.cmdutils.getrrf({'repo': ':'}, 'nocurf', -1)[3]
+        let curfile=s:_r.cmdutils.getrrf({'repo': ':'}, 'nocurf', 'getfile')[3]
         let curfile=repo.functions.reltorepo(repo, curfile)
         if index(csfiles, curfile)!=-1 && index(filelist, curfile)==-1
             let filelist+=[curfile]
 "▶1 aurum://diff mappings
 let s:mmgroup=':call <SNR>'.s:_sid.'_Eval("s:_f.mapgroup.map(''AuDiff'', '.
             \                                               "bufnr('%'))\")\n"
+"▶2 rundiffmap
 function s:F.rundiffmap(action)
     let buf=bufnr('%')
     let bvar=s:_r.bufvars[buf]
     endif
     return cmd
 endfunction
+"▶2 fvdiff
+function s:F.fvdiff()
+    let bvar=s:_r.bufvars[bufnr('%')]
+    let args=[bvar.repo]
+    if empty(bvar.rev1)
+        let args+=[[0, bvar.rev2]]
+    elseif empty(bvar.rev2)
+        let args+=[[0, bvar.rev1]]
+    else
+        let args+=[[bvar.rev1, bvar.rev2]]
+    endif
+    let args+=[1, bvar.files, 0]
+    return call(s:_r.vimdiff.full, args, {})
+endfunction
+"▲2
 call s:_f.mapgroup.add('AuDiff', {
-            \  'Next': {'lhs': 'K', 'rhs': ['next'    ]},
-            \  'Prev': {'lhs': 'J', 'rhs': ['previous']},
-            \'Update': {'lhs': 'U', 'rhs': ['update'  ]},
-            \  'Exit': {'lhs': 'X', 'rhs': ['exit'    ]},
-            \  'Open': {'lhs': 'o', 'rhs': ['open'    ]},
-            \ 'Vdiff': {'lhs': 'D', 'rhs': ['vimdiff' ]},
+            \  'Next': {'lhs':  'K', 'rhs': ['next'       ]},
+            \  'Prev': {'lhs':  'J', 'rhs': ['previous'   ]},
+            \'Update': {'lhs':  'U', 'rhs': ['update'     ]},
+            \  'Exit': {'lhs':  'X', 'rhs': ['exit'       ]},
+            \  'Open': {'lhs':  'o', 'rhs': ['open'       ]},
+            \ 'Vdiff': {'lhs':  'D', 'rhs': ['vimdiff'    ]},
+            \'FVdiff': {'lhs': 'gD', 'rhs': ':call <SNR>'.s:_sid.'_Eval('.
+            \                                            '"s:F.fvdiff()")<CR>'},
         \}, {'func': s:F.rundiffmap, 'silent': 1, 'mode': 'n', 'dontmap': 1,})
 "▶1 diff resource
 let s:diff= {'arguments': 2,

plugin/aurum/file.vim

     if a:file isnot 0 && a:file isnot# ':'
         let opts.file=a:file
     endif
-    let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf(opts, 'noffile', 0)
+    let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf(opts, 'noffile', 'open')
     if repo is 0
         return
     endif

plugin/aurum/log.vim

 "▶2 graph.update :: graph, cs → + graph
 function s:F.graph.update(cs)
     let self.cs=a:cs
-    let self.interesting_parents=filter(copy(a:cs.parents),
-                \                       '!has_key(self.skipchangesets, v:val)')
+    let self.interesting_parents=copy(a:cs.parents)
     let self.num_parents=len(self.interesting_parents)
     let self.prev_commit_index=self.commit_index
     call self.update_columns()
 endfunction
 "▶2 graph.output_commit_char :: graph → String
 function s:F.graph.output_commit_char()
+    if has_key(self.skipchangesets, self.cs.hex)
+        return '*'
+    endif
     return '@o'[index(self.workcss, self.cs.hex)==-1]
 endfunction
 "▶2 graph.draw_octopus_merge :: graph → String
     endif
     if has_key(a:text, 'skip')
         let joined_sil=join(shift_interline, '')
-        let joined_nl=substitute(join(nodeline, ''), '\V'.a:char, '|', '')
+        let joined_nl=join(nodeline, '')
         let a:text.text=[]
-        if joined_nl!~#'\v^[o| ]+$'
-            let a:text.text+=[substitute(joined_nl,'\v\-@<=\||\|\-@=','+','')]
+        if joined_nl!~#'\v^[*| ]+$'
+            let a:text.text+=[joined_nl]
         endif
-        if joined_sil!~#'\v^[| ]+$' && joined_sil isnot# joined_nl
+        if joined_sil!~#'\v^[| ]+$' &&
+                    \joined_sil isnot# tr(joined_nl, a:char, '|')
             let a:text.text+=[joined_sil]
         endif
         return a:text
     let lines=((a:graph.cs is 0)?([]):(a:graph.show_remainder()))
     call a:graph.update(a:cs)
     let lines+=a:graph.show_commit()
+    let skip=has_key(a:text, 'skip')
+    if skip && len(lines)==1 && lines[0]!~#'[^|* ]'
+        return a:text
+    endif
     let collen=len(lines[-1])
     let a:text.block_r[0][1]+=collen
     let a:text.block_r[1][1]+=collen
 endfunction
 function s:iterfuncs.git.proccs(d, cs)
     if has_key(a:d.opts.skipchangesets, a:cs.hex)
-        return [[], 0, 0]
+        let text={'skip': 1, 'text': [], 'special': {}}
+    else
+        let text=a:d.opts.templatefunc(a:cs, a:d.opts, a:d.repo)
     endif
-    let text=a:d.opts.templatefunc(a:cs, a:d.opts, a:d.repo)
     let text.block_r=[[0, 0],
                 \     [len(text.text)-1,
                 \      max(map(copy(text.text), 'len(v:val)'))]]
                 \'showparents': get(a:000, 0, []), 'repo': a:repo}
 endfunction
 function s:iterfuncs.hg.proccs(d, cs)
-    let char=((index(a:d.showparents, a:cs.hex)==-1)?('o'):('@'))
     if has_key(a:d.opts.skipchangesets, a:cs.hex)
+        let char='*'
         let text={'skip': 1}
         let skip=1
     else
+        let char=((index(a:d.showparents, a:cs.hex)==-1)?('o'):('@'))
         let text=a:d.opts.templatefunc(a:cs, a:d.opts, a:d.repo)
         let skip=0
     endif
     endif
     let a:bvar.templatelist=s:F.temp.parse(template)
 endfunction
+"▶1 getblock :: bvar + cursor, bvar → block
+"▶2 bisect :: [a], function + self → a
+function s:F.bisect(list, function)
+    let llist=len(a:list)
+    let lborder=0
+    let rborder=llist-1
+    let lres=call(a:function, [a:list[lborder]], self)
+    if lres<=0
+        return a:list[lborder]
+    endif
+    let rres=call(a:function, [a:list[rborder]], self)
+    if rres>=0
+        return a:list[rborder]
+    endif
+    let totest='r'
+    let cur=(((rborder+1)/2)-1)
+    while lborder!=rborder
+        let res=call(a:function, [a:list[cur]], self)
+        if res==0
+            return a:list[cur]
+        else
+            let shift=((rborder-lborder)/2)
+            if shift==0
+                let shift=1
+            endif
+            let {(res>0)?('l'):('r')}border=cur
+            let cur=lborder+shift
+        endif
+    endwhile
+    return a:list[lborder]
+endfunction
+"▶2 checkinblock :: block → -1|0|1
+function s:F.checkinblock(block)
+    let curline=line('.')-1
+    return       ((curline<a:block[0][0])?(-1):
+                \((curline>a:block[1][0])?( 1):
+                \                         ( 0)))
+endfunction
+"▲2
+function s:F.getblock(bvar)
+    if empty(a:bvar.rectangles)
+        call s:_f.throw('nocontents')
+    endif
+    return s:F.bisect(a:bvar.rectangles, s:F.checkinblock)
+endfunction
 "▶1 setup
 "▶2 getkwreg
 function s:F.getkwreg(kw, nextlit)
 function s:F.setup(read, repo, opts, ...)
     let opts=a:opts
     let bvar=get(a:000, 0, {'opts': opts})
+    let bvar.getblock=s:F.getblock
     "▶2 Add `ignorefiles'
     let ignorefiles=(has_key(opts, 'ignfiles')?
                 \               (opts.ignfiles):
     let opts=copy(a:opts)
     if has_key(opts, 'files')
         if opts.files[0] is# ':'
-            let curfile=s:_r.cmdutils.getrrf(opts, 'nocurf', -1)[3]
+            let curfile=s:_r.cmdutils.getrrf(opts, 'nocurf', 'getfile')[3]
             if curfile is 0
                 call remove(opts.files, 0)
             else

plugin/aurum/vimdiff.vim

 "▶1 
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('0.1', {'@/os': '0.0',
+    execute frawor#Setup('0.2', {'@/os': '0.0',
                 \     '@aurum/cmdutils': '0.0',
                 \         '@aurum/edit': '1.0',
                 \               '@/fwc': '0.0',
         \}
 let s:_messages={
             \'nodfile': 'Failed to deduce which file to diff with',
+            \ 'cndiff': 'Can’t show diff for file %s',
             \ 'nodrev': 'Unsure what revision should be diffed with',
         \}
 let s:lastfullid=0
     1 wincmd w
     "▲2
 endfunction
+"▶1 fullvimdiff
+function s:F.fullvimdiff(repo, revs, mt, files, areglobs, ...)
+    let statuses=map(a:revs[1:],
+                \    'a:repo.functions.status(a:repo, v:val, a:revs[0])')
+    let files={}
+    let i=1
+    let stypes=['modified']
+    "▶2 Get accepted statuses list
+    if a:mt
+        let stypes+=['added', 'removed']
+        if a:mt==2
+            let stypes+=['deleted', 'unknown']
+        endif
+    endif
+    "▶2 Get file statuses
+    for status in statuses
+        for [k, fs] in filter(items(status), 'index(stypes, v:val[0])!=-1')
+            for f in fs
+                if !has_key(files, f)
+                    let files[f]={}
+                endif
+                let files[f][i]=k
+            endfor
+        endfor
+        let i+=1
+    endfor
+    "▶2 Filter out requested files
+    if !empty(a:files)
+        let files2={}
+        if a:areglobs
+            let filepats=map(filter(copy(a:files), 'v:val isnot# ":"'),
+                        \    's:_r.globtopat(v:val)')
+            "▶3 Current file
+            if a:0 && !empty(a:1)
+                for f in a:1
+                    if has_key(files, f)
+                        let files2[f]=remove(files, f)
+                    else
+                        call s:_f.throw('cndiff', f)
+                    endif
+                endfor
+            endif
+            "▲3
+            for pattern in filepats
+                call map(filter(keys(files), 'v:val=~#pattern'),
+                            \'extend(files2, {v:val : remove(files, v:val)})')
+            endfor
+        else
+            for f in filter(copy(a:files), 'has_key(files, v:val)')
+                let files2[f]=remove(files, f)
+            endfor
+        endif
+        let files=files2
+    endif
+    "▶2 Open tabs
+    let s:lastfullid+=1
+    for [f, d] in items(files)
+        call s:F.opentab(a:repo, a:revs, f, d)
+    endfor
+    "▲2
+endfunction
 "▶1 vimdfunc
 " TODO exclude binary files from full diff
 function s:vimdfunc.function(opts, ...)
     "▶2 repo and revisions
-    let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 0, 0)
+    let full=get(a:opts, 'full', 0)
+    let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 0,
+                \                                      ((full)?('getfiles'):
+                \                                              ('open')))
     call s:_r.cmdutils.checkrepo(repo)
     let revs=[]
     if rev isnot 0
     endif
     "▲2
     if get(a:opts, 'full', 0)
-        let statuses=map(revs[1:], 'repo.functions.status(repo,v:val,revs[0])')
-        let files={}
-        let i=1
-        "▶2 Get accepted statuses list
-        let stypes=['modified']
-        if get(a:opts, 'untracked', 0)
-            let stypes+=['added', 'removed', 'deleted', 'unknown']
-        elseif !get(a:opts, 'onlymodified', 1)
-            let stypes+=['added', 'removed']
-        endif
-        "▶2 Get file statuses
-        for status in statuses
-            for [k, fs] in filter(items(status), 'index(stypes, v:val[0])!=-1')
-                for f in fs
-                    if !has_key(files, f)
-                        let files[f]={}
-                    endif
-                    let files[f][i]=k
-                endfor
-            endfor
-            let i+=1
-        endfor
-        "▶2 Filter out only requested files
+        let args=[repo, revs,
+                    \((get(a:opts, 'untracked', 0))?
+                    \   (2):
+                    \   (!get(a:opts, 'onlymodified', 1)))]
         if has_key(a:opts, 'files')
-            let filepats=map(filter(copy(a:opts.files), 'v:val isnot# ":"'),
-                        \    's:_r.globtopat(repo.functions.reltorepo(repo, '.
-                        \                                            'v:val))')
-            let files2={}
-            "▶3 Current file
-            if len(filepats)!=len(a:opts.files)
-                if file isnot# 0 && has_key(files, file)
-                    let files2[file]=remove(files, file)
+            let files=map(filter(copy(a:opts.files), 'v:val isnot# ":"'),
+                        \        'repo.functions.reltorepo(repo, v:val)')
+            let args+=[files, 1]
+            if len(files)!=len(a:opts.files)
+                if empty(file)
+                    call s:_f.throw('nodfile')
                 else
-                    call s:_f.throw('nodfile')
+                    let args+=[file]
                 endif
             endif
-            "▲3
-            for pattern in filepats
-                call map(filter(keys(files), 'v:val=~#pattern'),
-                            \'extend(files2, {v:val : remove(files, v:val)})')
-            endfor
-            let files=files2
+        elseif empty(file)
+            let args+=[[], 0]
+        else
+            let args+=[file, 0]
         endif
-        "▲2
-        let s:lastfullid+=1
-        for [f, d] in items(files)
-            call s:F.opentab(repo, revs, f, d)
-        endfor
+        return call(s:F.fullvimdiff, args, {})
     else
         if file is 0
             call s:_f.throw('nodfile')
             \'\vfile\s+type\s*\V""', 'file path',        ''),
             \'\V+ type ""',          '+ '.s:_r.comp.rev, ''))
 "▶1 Post resource
-call s:_f.postresource('vimdiff', {'split': s:F.diffsplit,})
+call s:_f.postresource('vimdiff', {'split': s:F.diffsplit,
+            \                       'full': s:F.fullvimdiff,})
 "▶1
 call frawor#Lockvar(s:, '_r,_pluginloaded,lastfullid')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

test/annotatemaps.in

 :0/\v^\s*2
 c:WW
 :bwipeout!
+:W{{{1 gD
+:R silent edit .hgtags | setlocal bufhidden=wipe
+:AuAnnotate | wincmd p
+G-gD:WTabs
+,X
+:bwipeout!
+:W{{{1 gC
+:R silent edit chgrepo.zsh | setlocal bufhidden=wipe
+:AuAnnotate | wincmd p
+:0/\v^\s*2
+gC:WTabs
+,X
+:bwipeout!
 :W{{{1 gd
 :R silent edit chgrepo.zsh | setlocal bufhidden=wipe
 :AuAnnotate | wincmd p

test/annotatemaps.ok

 *1: aurum://diff:%ETMPDIR%%-test%-annotatemapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2::crepo.zsh:
 {{{1 c
 *1: aurum://diff:%ETMPDIR%%-test%-annotatemapsrepo:dfe39aa48c6ddde158e42217548b531a45ff66e9:5a6a1bf999fad1f4547eeb887af54f31d11833f2:chgrepo.zsh:
+{{{1 gD
+ 1*1: %TMPDIR%/test/annotatemapsrepo/.hgtags
+*2*1: aurum://file:%ETMPDIR%%-test%-annotatemapsrepo:250ba7cd831ee91c3ba5bef1faebc38c5dbca5f8:.hgtags
+*2#2: %TMPDIR%/test/annotatemapsrepo/.hgtags
+{{{1 gC
+ 1*1: %TMPDIR%/test/annotatemapsrepo/chgrepo.zsh
+*2*1: aurum://file:%ETMPDIR%%-test%-annotatemapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2:crepo.zsh
+*2#2: aurum://file:%ETMPDIR%%-test%-annotatemapsrepo:dfe39aa48c6ddde158e42217548b531a45ff66e9:crepo.zsh
 {{{1 gd
 *1: aurum://diff:%ETMPDIR%%-test%-annotatemapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2:::
 {{{1 gc

test/audiffmaps.in

 D:call WriteFile(winnr('$'), expand('%:p'), bufname(winbufnr(winnr('#'))))
 :bwipeout
 :bwipeout
+:R AuDiff rev1 2
+gD:WTabs

test/audiffmaps.ok

 2
 %TMPDIR%/test/audiffmapsrepo/crepo.zsh
 aurum://file:%ETMPDIR%%-test%-audiffmapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2:crepo.zsh
+ 1*1: aurum://diff:%ETMPDIR%%-test%-audiffmapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2:dfe39aa48c6ddde158e42217548b531a45ff66e9::
+*2*1: aurum://file:%ETMPDIR%%-test%-audiffmapsrepo:dfe39aa48c6ddde158e42217548b531a45ff66e9:crepo.zsh
+*2#2: aurum://file:%ETMPDIR%%-test%-audiffmapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2:crepo.zsh

Binary file modified.

 | | |/ 
 | | |/ 
 | | | |\ 
-| | +---+ 
+| | *---+ 
 | |  / / 
-| +---+ 
+| +---* 
 |/ / 
 o |  Changeset 5:d760b0fe4fe45adf585f5030181b8081899e26a9 (branch A)
 |/   Commited 01 Ноя 2000 13:24 by B <b@example.org>
 | | | |  
 |\ \ \ \ 
 | |/ / / 
-| +---+ 
+| *---+ 
 |  / / 
 | |/ 
 |/ 
 | | | | |  @ Merge from B
 | | | | |  
 | | | | |/ 
-| +-----+ 
-| +-----+ 
+| +-----* 
+| +-----* 
 o | | |    Changeset 9:269399222040415b3928a316f5d28792cc0be4dd
 |\ \ \ \   Commited 01 Янв 2001 00:00 by C <c@example.gov>
 | | | | |  @ Merge from C
 | | | | |  
 | |/ / / 
-| +---+ 
+| *---+ 
 |  / / 
 | |/ 
 |/ 
 | | |\ 
 | | |\ \ 
 | | | | |/ 
-| +-----+ 
+| +-----* 
 | +-----o  Changeset 11:0e6e0adf281b423193f9dce097ab1df4f91215f7 (branch C)
 | | | |    Commited 01 Фев 2001 05:19 by A <a@example.com>
 | | | |    @ Added tag blines for changeset c4110a066208
 | | | |  
 |\ \ \ \ 
 | |/ / / 
-| +---+ 
+| *---+ 
 |  / / 
 | | o  Changeset 6:9fe0c28c3e1dd175e8372b742f13cbb7bd6799fe (branch B)
 | |/   Commited 02 Ноя 2000 05:44 by A <a@example.com>
 | | |\ 
 | | |\ \ 
 | | | | |/ 
-| +-----+ 
-| +-----+ 
+| +-----* 
+| +-----* 
 |\ \ \ \ 
 | |/ / / 
-| +---+ 
+| *---+ 
 |  / / 
 | |/ 
 |/ 
 | | | | |  @ Merge from B
 | | | | |  
 | | | | |/ 
-| +-----+ 
-| +-----+ 
+| +-----* 
+| +-----* 
 o | | |    Changeset 9:269399222040415b3928a316f5d28792cc0be4dd
 |\ \ \ \   Commited 01 Янв 2001 00:00 by C <c@example.gov>
 | | | | |  @ Merge from C
 | | | | |  
 | |/ / / 
-| +---+ 
+| *---+ 
 |  / / 
 | |/ 
 |/ 
 | | |\ 
 | | |\ \ 
 | | | | |/ 
-| +-----+ 
-| +-----+ 
+| +-----* 
+| +-----* 
 |\ \ \ \ 
 | |/ / / 
-| +---+ 
+| *---+ 
 |  / / 
 | |/ 
 |/ 
 | | | | |  @ Merge from B
 | | | | |  
 | | | | |/ 
-| +-----+ 
-| +-----+ 
+| +-----* 
+| +-----* 
 o | | |    Changeset 9:269399222040415b3928a316f5d28792cc0be4dd
 |\ \ \ \   Commited 01 Янв 2001 00:00 by C <c@example.gov>
 | | | | |  @ Merge from C
 :R AuLog
 gu:call WriteFile(bufname('%'))
 :bwipeout!
-:W{{{1 gD
+:W{{{1 gM
 :R AuLog
-gD:call WriteFile(bufname('%'))
+gM:call WriteFile(bufname('%'))
 :bwipeout!
 :W{{{1 gb
 :R AuLog
 /(branch A)
 gr:call WriteFile(bufname('%'))
 :bwipeout!
+:W{{{1 gD
+:R AuLog
+gg/ 25:\x\x\x
+gD:WTabs
+,X
+:bwipeout!
+:W{{{1 gC
+:R AuLog
+/ 2:\x\x\x
+gC:WTabs
+,X
+:bwipeout!
 :W{{{1 gd
 :R AuLog
 / 1:\x\x\x
 aurum://log:%ETMPDIR%%-test%-logmapsrepo:files:.hgignore,crrestrict:files,showfiles:1,
 {{{1 gu
 aurum://log:%ETMPDIR%%-test%-logmapsrepo:user:%+VA <a@example.com>,crrestrict:user,
-{{{1 gD
+{{{1 gM
 aurum://log:%ETMPDIR%%-test%-logmapsrepo:date:2002-02,crrestrict:date,
 {{{1 gb
 aurum://log:%ETMPDIR%%-test%-logmapsrepo:crrestrict:branch,branch:A,
 {{{1 gr
 aurum://log:%ETMPDIR%%-test%-logmapsrepo:crrestrict:revision,revision:be92227e3e43f4e7402408afe110098d209d2568,
+{{{1 gD
+ 1*1: aurum://log:%ETMPDIR%%-test%-logmapsrepo:
+ 1#2: 
+ 2*1: 
+ 2#2: %TMPDIR%/test/logmapsrepo/.hgignore
+*3*1: 
+*3#2: %TMPDIR%/test/logmapsrepo/directory/file
+{{{1 gC
+ 1*1: aurum://log:%ETMPDIR%%-test%-logmapsrepo:
+ 1#2: 
+*2*1: aurum://file:%ETMPDIR%%-test%-logmapsrepo:dfe39aa48c6ddde158e42217548b531a45ff66e9:crepo.zsh
+*2#2: aurum://file:%ETMPDIR%%-test%-logmapsrepo:5a6a1bf999fad1f4547eeb887af54f31d11833f2:crepo.zsh
 {{{1 gd
 aurum://diff:%ETMPDIR%%-test%-logmapsrepo::dfe39aa48c6ddde158e42217548b531a45ff66e9
 {{{1 gc
 /^M
 o:WW
 :bwipeout!
+:W{{{1 gC
+:R AuStatus
+gC:WTabs
+,X:bwipeout!
 :W{{{1 gc
 :R AuStatus
 /^M
 :call WriteFile('d: '.&diff)
 :bwipeout!
 :bwipeout!
+:W{{{1 2C
+:R AuStatus rev1 tip rev2 -2
+gg2C:WTabs
+,X:bwipeout!
+:W{{{1 gD
+:R AuStatus
+gD:WTabs
+,X:bwipeout!
 :W{{{1 gd
 :R AuStatus
 /^M
 :call WriteFile('d: '.&diff)
 :bwipeout!
 :bwipeout!
+:W{{{1 2D
+:R AuStatus
+:0/\v^M/-1
+2D:WTabs
+,X:bwipeout!
 :W{{{1 i
 :R AuStatus
 gg0idd02x:silent write
 *1: %TMPDIR%/test/statmapsrepo/nohglinesrev.lst
 {{{1 o
 *1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:nohglinesrev.lst
+{{{1 gC
+ 1#1: 
+ 1*2: aurum://status:%ETMPDIR%%-test%-statmapsrepo:
+*2*1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:nohglinesrev.lst
+*2#2: %TMPDIR%/test/statmapsrepo/nohglinesrev.lst
 {{{1 gc
 *1: aurum://diff:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:::
 {{{1 c
 *1: aurum://diff:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62::nohglinesrev.lst:
 {{{1 C
 *1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:nohglinesrev.lst
-#2: %TMPDIR%/test/statmapsrepo/nohglinesrev.lst
+#2: aurum://file:%ETMPDIR%%-test%-statmapsrepo:d8140c0b7b0b670bafe0891bcf0c4582092de884:nohglinesrev.lst
 d: 1
+{{{1 2C
+ 1#1: 
+ 1*2: aurum://status:%ETMPDIR%%-test%-statmapsrepo:rev1:tip,rev2:-2,
+ 2*1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:.hgignore
+ 2#2: 
+*3*1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:directory%-file
+*3#2: 
+{{{1 gD
+ 1#1: 
+ 1*2: aurum://status:%ETMPDIR%%-test%-statmapsrepo:
+ 2*1: 
+ 2#2: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:.hgignore
+*3*1: 
+*3#2: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:directory%-file
 {{{1 gd
 *1: aurum://diff:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:::
 {{{1 d
 *1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:nohglinesrev.lst
 #2: %TMPDIR%/test/statmapsrepo/nohglinesrev.lst
 d: 1
+{{{1 2D
+ 1#1: 
+ 1*2: aurum://status:%ETMPDIR%%-test%-statmapsrepo:
+ 2*1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:nohglinesrev.lst
+ 2#2: %TMPDIR%/test/statmapsrepo/nohglinesrev.lst
+*3*1: aurum://file:%ETMPDIR%%-test%-statmapsrepo:504f74154456dbb0e9441326514e42ce66279e62:nohglines.lst
+*3#2: 
 {{{1 i
 @  Changeset 27
 |  Tags: tip
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.