Commits

ZyX_I committed 119ca13 Merge

Merge
Fixes #2, closes #1

  • Participants
  • Parent commits 1a25445, 4b3af04

Comments (0)

Files changed (25)

File README.markdown

 This plugin provides a vim <--> VCS (currently mercurial, git and subversion) 
 integration for your projects. Features:
 
-  - Partially committing changes ([:AuRecord](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line353-0)).
+  - 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#line584-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#line588-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#line393-0)). It is also possible to open multiple 
+    specific revisions ([:AuVimDiff](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line397-0)). It is also possible to open multiple 
     tabs with all changes to all files viewed as side-by-side diffs.
 
   - Viewing revisions log ([:AuLog](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line239-0)). Output is highly customizable.
 
-  - Viewing working directory status ([:AuStatus](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line357-0)).
+  - 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#line829-0)).
+    rollback ([g:aurum_remembermsg](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line843-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 
     attached: useful for sharing) ([:AuHyperlink](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line189-0)).
 
-  - [aurum#changeset()](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line441-0), [aurum#repository()](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line437-0) and [aurum#status()](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line445-0) functions 
+  - [aurum#changeset()](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line445-0), [aurum#repository()](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line441-0) and [aurum#status()](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line449-0) functions 
     that are to be used from modeline.
 
   - 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#line726-0)), 
+Most commands can be reached with a set of mappings (see [aurum-mappings](http://vimpluginloader.sourceforge.net/doc/aurum.txt.html#line730-0)), 
 all mappings are customizable.
 
 

File autoload/aurum.vim

 "▶1 
-execute frawor#Setup('0.0', {'@aurum/repo': '1.0',
+execute frawor#Setup('0.0', {'@aurum/repo': '2.0',
             \               '@aurum/cache': '0.0',})
 "▶1 aurum#repository
 function aurum#repository()

File doc/aurum.txt

                 Flag. Determines whether copies should be shown.
     [no]showrenames
                 Flag. Determines whether renames should be shown.
+    [no]procinput
+                Flag. Overrides |g:aurum_procinput|: determines whether user 
+                input will be processed while generating log.
+                Note If enabled, acts like g:aurum_procinput=2.
 
                                                                      *:AuMove*
 AuMove [copy] [rightrepl] [leftpattern] [pretend] [repo {repo}] [args ...]
 closewindow                                              *g:aurum_closewindow*
     Bool. Determines whether log window should be closed when executing 
     mappings (except mappings that add a filter and exit mapping).
-    Default:
+    Default: 1.
+
+procinput                                                  *g:aurum_procinput*
+    0, 1 or 2. If not zero, consumes and passes user input to |:normal| while 
+    generating a log thus allowing you to navigate it while it is being 
+    created. Value 1 will make logger additionally check whether there were 
+    some characters in input buffer before creation of log started. If there 
+    were characters, then processing user input will be disabled (because in 
+    other case it will possibly break mappings that do not expect such 
+    behavior from |:AuLog|).
+    Default value: 1.
 
 recheight                                                  *g:aurum_recheight*
     VimL |expression| that evaluates to unsigned integer or unsigned integer. 
     List of |aurum-changeset| objects. It is empty by default and may be 
     populated only when you call |aurum-rf-getchangesets|. See also 
     |aurum-cs.rev|.
-    Obsolete, use |aurum-rf-revrange|.
+    Obsolete, use |aurum-rf-getchangesets|.
 changesets :: {hex : cs}                               *aurum-repo.changesets*
     Dictionary containing all |aurum-changeset| objects that are associated 
     with the repository. For each changeset cs is repo.changesets[cs.hex].
     List of label types supported by |aurum-rf-label|. First type will be 
     default used by |:AuName|.
 requires_sort :: Bool                               *aurum-repo.requires_sort*
-    Determines whether it is necessary to run sort_in_topological_order 
-    function on commit list returned by |aurum-rf-revrange| when it is about 
-    to be displayed in |aurum://log| buffer. If it is false, then this list 
-    will only be reversed (|reverse()|).
+    Determines whether it is necessary to run sort_in_topological_order function 
+    on commit lists returned by |aurum-rf-revrange| and |aurum-rf-getchangesets| 
+    when it is about to be displayed in |aurum://log| buffer. If it is false, 
+    then this list will only be reversed (|reverse()|).
+    See |aurum-rf-getchangesets| for the description of what “topological order” 
+    is.
     This key is optional, default value: true.
 has_octopus_merges :: Bool                     *aurum-repo.has_octopus_merges*
     Determines whether repository has merges containing more then two parents. 
     Pull all changesets contained in repository into |aurum-repo.changesets| 
     dictionary. If this function is not called, then |aurum-repo.changesets| 
     dictionary contains only changesets that were used at least ones. Returns 
-    list of changesets as contained in |aurum-repo.cslist|. Return value is 
-    ignored (|aurum-rf-revrange| function is now used instead).
+    list of pulled changesets. This list should be sorted in topological order 
+    (thus meaning that every changeset appears before every its parent) or 
+    |aurum-repo.requires_sort| should be set to true.
   revrange :: hex, hex -> [ cs ]                           *aurum-rf-revrange*
-    Get revision range (inclusive). Numbers are to be treated specially, like 
-    it is done by |list-index|, but it is up to driver what it thinks is “last 
-    revision” (-1) or “first revision” (0).
+    Get revision range (inclusive).
   getcs :: hex -> cs                                          *aurum-rf-getcs*
     Returns |aurum-changeset| object which has given hash. See also 
     |aurum-cs.hex|.
 ==============================================================================
 11. Changelog                                                *aurum-changelog*
 
-@aurum/repo:
+@aurum/repo (reflects all drivers’ API changes):
     1.0: Instead of depending on drivers, make drivers depend on @aurum/repo
     1.1: Added repo.branch and repo.label
     1.2: Added setlines function, default implementations of |aurum-rf-copy|, 
          |aurum-rf-getstats|, |aurum-rf-getcsprop| and |aurum-rf-diffname|.
     1.3: Added default implementation of |aurum-rf-revrange|.
+    2.0: Purged support of numeric ranges out of |aurum-rf-revrange|, returned 
+         value of |aurum-rf-getchangesets| is not stated as ignored.
 @aurum:
     0.1: Added :AuBranch and :AuName
 @aurum/edit:
     0.1: Added |aurum-style-git| and |aurum-style-gitoneline| log styles, 
          added skipping of $rev if |aurum-repo.hasrevisions| is false, made it 
          separate graph from content using non-breaking spaces
+    0.2: Added |:AuLog| procinput option
 
 vim: ft=help:tw=78

File ftplugin/aurumannotate.vim

 endif
 setlocal noswapfile
 setlocal nomodeline
-execute frawor#Setup('0.0', {'@aurum/repo': '1.0',
+execute frawor#Setup('0.0', {'@aurum/repo': '2.0',
             \             '@aurum/bufvars': '0.0',
             \             '@aurum/vimdiff': '0.0',
             \            '@aurum/annotate': '0.0',

File ftplugin/aurumlog.vim

 setlocal nomodeline
 execute frawor#Setup('0.0', {'@aurum/cmdutils': '0.0',
             \                 '@aurum/bufvars': '0.0',
-            \                    '@aurum/repo': '1.0',
+            \                    '@aurum/repo': '2.0',
             \                    '@aurum/edit': '1.0',
             \                           '@/os': '0.0',
             \                     '@/mappings': '0.0',})

File plugin/aurum.vim

                 \             '@aurum/status': '0.0',
                 \                '@aurum/log': '0.0',
                 \             '@aurum/commit': '0.0',
-                \               '@aurum/repo': '1.1',
+                \               '@aurum/repo': '2.0',
                 \               '@aurum/edit': '1.0',
                 \            '@aurum/bufvars': '0.0',
                 \            '@aurum/vimdiff': '0.0',}, 0)

File plugin/aurum/cmdutils.vim

 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.0', {'@/resources': '0.0',
                 \                       '@/os': '0.0',
-                \                '@aurum/repo': '1.2',
+                \                '@aurum/repo': '2.0',
                 \                '@aurum/edit': '1.0',
                 \               '@aurum/cache': '0.0',
                 \             '@aurum/bufvars': '0.0',}, 0)

File plugin/aurum/commit.vim

                 \              '@aurum/status': '0.0',
                 \            '@aurum/cmdutils': '0.0',
                 \             '@aurum/bufvars': '0.0',
-                \                '@aurum/repo': '1.0',
+                \                '@aurum/repo': '2.0',
                 \                '@aurum/edit': '1.0',
                 \                      '@/fwc': '0.3',
                 \                 '@/commands': '0.0',

File plugin/aurum/diff.vim

 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.0', {'@aurum/cmdutils': '0.0',
                 \                 '@aurum/bufvars': '0.0',
-                \                    '@aurum/repo': '1.2',
+                \                    '@aurum/repo': '2.0',
                 \                    '@aurum/edit': '1.0',
                 \                           '@/os': '0.0',
                 \                          '@/fwc': '0.0',

File plugin/aurum/drivers/git.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('0.1', {   '@aurum/repo': '1.0',
+    execute frawor#Setup('0.1', {   '@aurum/repo': '2.0',
                 \                          '@/os': '0.0',
                 \   '@aurum/drivers/common/utils': '0.0',
                 \'@aurum/drivers/common/hypsites': '0.0',}, 0)
     return [cs, line]
 endfunction
 "▶1 git.getchangesets :: repo → []
-"▶2 rerev :: Either rev num → rev
-function s:F.rerev(rev)
-    if type(a:rev)==type(0)
-        if a:rev is 0
-            " XXX Ranges like ..HEAD are not working, though it is unlikely that
-            "     revrange will receive ranges like ?..0 or 0..? except 0..-1, 
-            "     thus 0..? is always taken as 0..-1
-            return ''
-        elseif a:rev is -1
-            return 'HEAD'
-        elseif a:rev
-            return 'HEAD~'.(-1-a:rev)
-        endif
-    else
-        return a:rev
-    endif
-endfunction
-"▲2
 function s:git.getchangesets(repo, ...)
     "▶2 Prepare s:F.git arguments
     let args=[]
     let kwargs=copy(s:logkwargs)
     if a:0
-        if a:1 is 0
-        elseif a:1<0 && a:2 is -1
-            let args+=['-n'.(-a:1), 'HEAD']
-        else
-            let args+=[s:F.rerev(a:1).'^..'.s:F.rerev(a:2)]
-        endif
+        let args+=[a:1.'^..'.a:2]
     else
         let kwargs.all=1
         let kwargs['full-history']=1

File plugin/aurum/drivers/mercurial.vim

 scriptencoding utf-8
 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.2', {      '@/python': '0.0',
-                \                   '@aurum/repo': '1.0',
+                \                   '@aurum/repo': '2.0',
                 \                          '@/os': '0.0',
                 \                     '@/options': '0.0',
                 \   '@aurum/drivers/common/utils': '0.0',
 "▶1 hg.getchangesets :: repo → changesets + repo.changesets
 function s:hg.getchangesets(repo)
     call a:repo.functions.updatechangesets(a:repo)
-    return a:repo.cslist
+    return a:repo.cslist[:-2]
 endfunction
 "▶1 hg.revrange :: repo, rev, rev → [cs]
-function s:F.getrev(repo, rev, cslist)
-    if type(a:rev)==type(0)
-        if a:rev<0
-            return len(a:cslist)+a:rev-1
-        else
-            return a:rev
-        endif
-    else
-        return a:repo.functions.getcs(a:repo, a:rev).rev
-    endif
-endfunction
 function s:hg.revrange(repo, rev1, rev2)
     if empty(a:repo.cslist)
         let cslist=a:repo.functions.getchangesets(a:repo)
     else
         let cslist=a:repo.cslist
     endif
-    let rev1=s:F.getrev(a:repo, a:rev1, cslist)
-    let rev2=s:F.getrev(a:repo, a:rev2, cslist)
+    let rev1=a:repo.functions.getcs(a:repo, a:rev1).rev
+    let rev2=a:repo.functions.getcs(a:repo, a:rev2).rev
     if rev1>rev2
         let [rev1, rev2]=[rev2, rev1]
     endif

File plugin/aurum/drivers/subversion.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('0.1', {   '@aurum/repo': '1.0',
+    execute frawor#Setup('0.1', {   '@aurum/repo': '2.0',
                 \                          '@/os': '0.0',
                 \   '@aurum/drivers/common/utils': '0.0',
                 \'@aurum/drivers/common/hypsites': '0.0',}, 0)
 endfunction
 "▶1 svn.revrange :: repo, rev1, rev2 → [cs]
 function s:svn.revrange(repo, rev1, rev2)
-    if a:rev1 is 0 && a:rev2 is -1
-        return a:repo.functions.getchangesets(a:repo)
-    elseif a:rev2 is -1 && type(a:rev1)==type(0) && a:rev1<0
-        if empty(a:repo.cslist)
-            return s:F.getchangesets(a:repo, -a:rev1)
-        else
-            return a:repo.functions.getchangesets(a:repo)[(a:rev1):]
+    " call a:repo.functions.updatechangesets(a:repo)
+    let hex1=a:repo.functions.getrevhex(a:repo, a:rev1)
+    let hex2=a:repo.functions.getrevhex(a:repo, a:rev2)
+    let i=len(a:repo.cslist)-1
+    while i>=0 && a:repo.cslist[i].hex isnot# hex1
+        if a:repo.cslist[i].hex is# hex2
+            let r2i=i
         endif
-    elseif a:rev1 is 0
-        if type(a:rev2)==type(0)
-            return a:repo.functions.getchangesets(a:repo)[:(a:rev2)]
-        elseif empty(a:repo.cslist)
-            return s:F.getchangesets(a:repo, a:rev2)
-        else
-            call a:repo.functions.updatechangesets(a:repo)
-            let rev2=a:repo.functions.getrevhex(a:repo, a:rev2)
-            let i=len(a:repo.cslist)-1
-            while i>=0 && a:repo.cslist[i].hex isnot# rev2
-                let i-=1
-            endwhile
-            return a:repo.cslist[:(i)]
-        endif
-    elseif empty(a:repo.cslist)
-        return s:F.getchangesets(a:repo, a:rev1, a:rev2)
-    else
-        call a:repo.functions.updatechangesets(a:repo)
-        let rev1=a:repo.functions.getrevhex(a:repo, a:rev1)
-        let rev2=a:repo.functions.getrevhex(a:repo, a:rev2)
-        let i=len(a:repo.cslist)-1
-        while i>=0 && a:repo.cslist[i].hex isnot# rev1
-            if a:repo.cslist[i].hex is# rev2
-                let r2i=i
-            endif
-            let i-=1
-        endwhile
-        if !exists('r2i')
-            call s:_f.throw('r2fst', a:rev2, a:rev1)
-        endif
-        return a:repo.cslist[(i):(r2i)]
+        let i-=1
+    endwhile
+    if !exists('r2i')
+        call s:_f.throw('r2fst', a:rev2, a:rev1)
     endif
+    return a:repo.cslist[(i):(r2i)]
 endfunction
 "▶1 svn.getcs :: repo, rev → cs
 function s:svn.getcs(repo, rev)

File plugin/aurum/edit.vim

     execute frawor#Setup('1.0', {'@/autocommands': '0.0',
                 \                   '@/functions': '0.0',
                 \                   '@/resources': '0.0',
-                \                   '@aurum/repo': '1.2',
+                \                   '@aurum/repo': '2.0',
                 \                '@aurum/bufvars': '0.0',}, 0)
     call FraworLoad('@/autocommands')
     call FraworLoad('@/functions')

File plugin/aurum/file.vim

     execute frawor#Setup('0.0', {'@aurum/cmdutils': '0.0',
                 \                 '@aurum/bufvars': '0.0',
                 \                 '@aurum/vimdiff': '0.0',
-                \                    '@aurum/repo': '1.2',
+                \                    '@aurum/repo': '2.0',
                 \                    '@aurum/edit': '1.0',
                 \                           '@/os': '0.0',
                 \                          '@/fwc': '0.0',

File plugin/aurum/log.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('0.1', {'@/table': '0.1',
+    execute frawor#Setup('0.2', {'@/table': '0.1',
                 \        '@aurum/cmdutils': '0.0',
                 \         '@aurum/bufvars': '0.0',
                 \            '@aurum/edit': '1.0',
                 \                  '@/fwc': '0.3',
-                \            '@aurum/repo': '1.3',
+                \            '@aurum/repo': '2.0',
                 \             '@/commands': '0.0',
                 \            '@/functions': '0.0',
                 \              '@/options': '0.0',}, 0)
             \'ignorefiles': {'default': [],
             \                'checker': 'list in [patch renames copies files]'},
             \'closewindow': {'default': 1, 'filter': 'bool'},
+            \'procinput':   {'default': 1, 'checker': 'range 0 2'},
         \}
 let s:_messages={
             \'2multl': 'Two multiline statements on one line',
             \'argmis': 'Missing argument #%u for keyword %s',
+            \  'ebuf': 'Exiting: buffer changed, old buffer was wiped out',
         \}
+" iterfunc :: {fname: { "start": startfunc, "next": nextfunc }}
+" startfunc (always) :: repo, opts, * → d
+let s:iterfuncs={}
 "▶1 graph
 "▶2 graph.update_state :: graph, gstate → + graph
 function s:F.graph.update_state(s)
         let self.new_mapping+=plist
     endif
 endfunction
-"▶2 graph.insert_into_new_columns :: graph, cs, mapindex → mapindex + graph
-function s:F.graph.insert_into_new_columns(cs, mapindex)
+"▶2 graph.insert_into_new_columns :: graph, hex, mapindex → mapindex + graph
+function s:F.graph.insert_into_new_columns(hex, mapindex)
     let i=0
     for hex in self.new_columns
-        if hex is# a:cs.hex
+        if hex is# a:hex
             let self.mapping[a:mapindex]=i
             return a:mapindex+2
         endif
         let i+=1
     endfor
     let self.mapping[a:mapindex]=len(self.new_columns)
-    call add(self.new_columns, a:cs.hex)
+    call add(self.new_columns, a:hex)
     return a:mapindex+2
 endfunction
 "▶2 graph.update_columns :: graph → + graph
             let seen=1
             let self.commit_index=i
             for parent in self.interesting_parents
-                let midx=self.insert_into_new_columns(
-                            \                self.repo.changesets[parent], midx)
+                let midx=self.insert_into_new_columns(parent, midx)
             endfor
             if midx==oldmidx
                 let midx+=2
             endif
         else
-            let midx=self.insert_into_new_columns(self.repo.changesets[ccshex],
-                        \                         midx)
+            let midx=self.insert_into_new_columns(ccshex, midx)
         endif
     endfor
     while self.mapping_size>1 && self.mapping[self.mapping_size-1]==-1
 function s:F.graph.update(cs)
     let self.cs=a:cs
     let self.interesting_parents=filter(copy(a:cs.parents),
-                \                       'has_key(self.addchangesets, v:val)')
+                \                       '!has_key(self.skipchangesets, v:val)')
     let self.num_parents=len(self.interesting_parents)
     let self.prev_commit_index=self.commit_index
     call self.update_columns()
     call map(a:text.text, 'lines[v:key].v:val')
     return a:text
 endfunction
-"▶2 glog.generate
-function s:F.glog.generate(css, showparents, opts)
-    let seen=[]
-    let state=[0, 0]
-    let r=      {'text': [],
-                \'specials': {},
-                \'rectangles': [],
-                \'csstarts': {},}
-    for cs in a:css
-        let char=((index(a:showparents, cs.hex)==-1)?('o'):('@'))
-        if has_key(a:opts.skipchangesets, cs.hex)
-            let text={'skip': 1}
-            let skip=1
-        else
-            let text=a:opts.templatefunc(cs, a:opts)
-            let skip=0
-        endif
-        call s:F.glog.utf(state, 'C', char, text,
-                    \     s:F.glog.utfedges(seen, cs.hex, cs.parents))
-        if !has_key(text, 'text') || empty(text.text)
-            continue
-        endif
-        if !skip
-            let text.block_r[0][0]+=len(r.text)
-            let text.block_r[1][0]+=len(r.text)
-            let r.specials[cs.hex]=text.special
-            let r.rectangles+=[text.block_r+[cs.hex]]
-            let r.csstarts[cs.hex]=text.block_r[0][0]
-        endif
-        let r.text+=text.text
-    endfor
-    return r
-endfunction
 "▶2 glog.graph_init :: repo, [cs] → graph
 let s:defgraph={
             \'cs':                0,
             \'mapping':           [],
             \'new_mapping':       [],
             \'mapping_size':      0,
-            \'addchangesets':     {},
+            \'skipchangesets':    {},
         \}
-function s:F.glog.graph_init(css, showparents, opts)
+function s:F.glog.graph_init(showparents, opts)
     let graph=deepcopy(s:defgraph)
     let graph.repo=a:opts.repo
     let graph.workcss=a:showparents
-    call map(copy(a:css),
-                \'((has_key(a:opts.skipchangesets, v:val.hex))?(0):'.
-                \   '(extend(graph.addchangesets, {v:val.hex : v:val})))')
+    let graph.skipchangesets=a:opts.skipchangesets
     call extend(graph, s:F.graph)
     return graph
 endfunction
     let collen=len(lines[-1])
     let a:text.block_r[0][1]+=collen
     let a:text.block_r[1][1]+=collen
+    call s:F.glog.addcols(a:text.special, collen)
     let lines[-1]=lines[-1][:-2].' '.get(a:text.text, 0, '')
     let cchar=a:graph.output_commit_char()
     let bidx=stridx(lines[-1], cchar)
     let a:text.text=lines
     return a:text
 endfunction
-"▶2 glog.log_show_all :: [cs], showparents, opts → Log
-function s:F.glog.log_show_all(css, showparents, opts)
-    let graph=s:F.glog.graph_init(a:css, a:showparents, a:opts)
-    let show_header=1
-    let r={'text': [], 'specials': {}, 'rectangles': [], 'csstarts': {}}
-    for cs in filter(copy(a:css), 'has_key(graph.addchangesets, v:val.hex)')
-        let text=a:opts.templatefunc(cs, a:opts)
-        let text.block_r=[[0, 0],
-                    \     [len(text.text)-1,
-                    \      max(map(copy(text.text), 'len(v:val)'))]]
-        let text=s:F.glog.show_log(graph, cs, text)
-        let r.text+=text.text
-        let text.block_r[0][0]+=len(r.text)
-        let text.block_r[1][0]+=len(r.text)
-        let r.specials[cs.hex]=text.special
-        let r.rectangles+=[text.block_r+[cs.hex]]
-        let r.csstarts[cs.hex]=text.block_r[0][0]
-    endfor
-    return r
-endfunction
 "▶2 s:DateCmp :: cs, cs → -1|0|1
 function s:DateCmp(a, b)
     let a=a:a.time
     return ((a==b)?(0):((a>b)?(-1):(1)))
 endfunction
 let s:_functions+=['s:DateCmp']
-"▶2 glog.sort_in_topological_order :: [cs] → [cs]
-function s:F.glog.sort_in_topological_order(repo, css)
-    call map(copy(a:css), 'extend(v:val, {"indegree": 1})')
-    for parents in map(copy(a:css), 'v:val.parents')
-        for parent in filter(map(filter(copy(parents),
-                    \                   'has_key(a:repo.changesets, v:val)'),
-                    \            'a:repo.changesets[v:val]'),
-                    \        'get(v:val, "indegree", 0)')
-            let parent.indegree+=1
-        endfor
-    endfor
-    let work=[]
-    call map(copy(a:css), 'v:val.indegree==1 && add(work, v:val) is 0')
-    call sort(work, 's:DateCmp')
-    let r=[]
-    while !empty(work)
-        let cs=remove(work, 0)
-        for parent in filter(map(filter(copy(cs.parents),
-                    \                   'has_key(a:repo.changesets, v:val)'),
-                    \            'a:repo.changesets[v:val]'),
-                    \        'get(v:val, "indegree", 0)')
-            let parent.indegree-=1
-            if parent.indegree==1
-                let j=0
-                let lwork=len(work)
-                while j<lwork && work[j].time<parent.time
-                    let j+=1
-                endwhile
-                call insert(work, parent, j)
+"▶2 glog.graphlog
+function s:F.glog.graphlog(repo, opts, csiterfuncs, bvar, read)
+    "▶3 Get grapher
+    if get(a:repo, 'has_octopus_merges', 1)
+        let literfuncs=s:iterfuncs.git
+    else
+        let literfuncs=s:iterfuncs.hg
+    endif
+    "▶3 Initialize variables
+    let haslimit=has_key(a:opts, 'limit')
+    if haslimit
+        let limit=a:opts.limit
+    endif
+    let foundfirst=0
+    let csbuf=[]
+    let reqprops=s:F.requiredpropslist(a:opts)
+    "▶3 Initialize variables not required for reading
+    if !a:read
+        let specials={}
+        let rectangles=[]
+        let csstarts={}
+        let r=[]
+        let a:bvar.rectangles=rectangles
+        let a:bvar.specials=specials
+        let a:bvar.csstarts=csstarts
+        let a:opts.skipchangesets={}
+        let didredraw=0
+        let procinput=a:bvar.procinput
+        let lastw0line=-1
+        let buf=bufnr('%')
+        let firstcs=1
+        let lastline=0
+    endif
+    "▶3 Initialize iterator functions
+    let ld=literfuncs.start(a:repo,a:opts,[a:repo.functions.getworkhex(a:repo)])
+    let csd=a:csiterfuncs.start(a:repo, a:opts)
+    let checkd=s:iterfuncs.check.start(a:repo, a:opts)
+    "▲3
+    while 1 && (!haslimit || limit)
+        let cs=a:csiterfuncs.next(csd)
+        if cs is 0 "▶3
+            return r
+        endif "▲3
+        let skip=!s:iterfuncs.check.check(checkd, cs)
+        "▶3 Add cs to skipchangesets or get its properties
+        if skip
+            let a:opts.skipchangesets[cs.hex]=cs
+        else
+            call map(copy(reqprops),
+                        \'a:repo.functions.getcsprop(a:repo, cs, v:val)')
+            let foundfirst=1
+            if haslimit
+                let limit-=1
             endif
-        endfor
-        let cs.indegree=0
-        call add(r, cs)
+        endif
+        "▲3
+        if foundfirst
+            let csbuf+=[cs]
+            if !skip
+                for cs in csbuf
+                    let [lines, rectangle, special]=literfuncs.proccs(ld, cs)
+                    "▶3 Add various information to bvar
+                    if !a:read && rectangle isnot 0
+                        let rectangle[0][0]=lastline
+                        let lastline+=len(lines)
+                        let rectangle[1][0]=lastline-1
+                        let rectangle+=[cs.hex]
+                        call add(rectangles, rectangle)
+                        let csstarts[cs.hex]=rectangle[0][0]
+                        if special isnot 0
+                            let specials[cs.hex]=special
+                        endif
+                    endif
+                    "▲3
+                    "▶3 Add lines to returned list if reading
+                    if a:read
+                        let r+=lines
+                    "▶3 Add lines to buffer if not, process user input
+                    else
+                        "▶4 Add lines to buffer
+                        if firstcs
+                            call setline(1, lines)
+                            let firstcs=0
+                        else
+                            call append('$', lines)
+                        endif
+                        "▶4 Process user input
+                        if didredraw
+                            if procinput && getchar(1)
+                                let input=''
+                                while getchar(1)
+                                    let char=getchar()
+                                    if type(char)==type(0)
+                                        let input.=nr2char(char)
+                                    else
+                                        let input.=char
+                                    endif
+                                endwhile
+                                execute 'normal' input
+                                if bufnr('%')!=buf
+                                    if bufexists(buf)
+                                        execute 'buffer' buf
+                                    else
+                                        call s:_f.warn('ebuf')
+                                        return []
+                                    endif
+                                endif
+                                let lw0=line('w0')
+                                if lw0!=lastw0line
+                                    redraw
+                                    let didredraw=(line('$')>=lw0+winheight(0))
+                                    let lastw0line=lw0
+                                endif
+                            endif
+                        "▶4 Redraw if necessary
+                        elseif line('$')>=line('w0')+winheight(0)
+                            redraw
+                            let didredraw=1
+                            let lastw0line=line('w0')
+                        endif
+                        "▲4
+                    endif
+                    "▲3
+                    unlet rectangle special
+                endfor
+                call remove(csbuf, 0, -1)
+            endif
+        endif
+        unlet cs
     endwhile
-    call map(copy(a:css), 'remove(v:val, "indegree")')
     return r
 endfunction
-"▶2 glog.graphlog
-function s:F.glog.graphlog(repo, opts, css)
-    if get(a:repo, 'requires_sort', 1)
-        let css=s:F.glog.sort_in_topological_order(a:repo, a:css)
+"▶1 iterfuncs: loggers
+"▶2 iterfuncs.git
+" TODO Fix skipping changesets if possible
+let s:iterfuncs.git={}
+function s:iterfuncs.git.start(repo, opts, ...)
+    let graph=s:F.glog.graph_init(get(a:000, 0, []), a:opts)
+    return {'graph': graph, 'opts': a:opts}
+endfunction
+function s:iterfuncs.git.proccs(d, cs)
+    if has_key(a:d.opts.skipchangesets, a:cs.hex)
+        return [[], 0, 0]
+    endif
+    let text=a:d.opts.templatefunc(a:cs, a:d.opts)
+    let text.block_r=[[0, 0],
+                \     [len(text.text)-1,
+                \      max(map(copy(text.text), 'len(v:val)'))]]
+    let text=s:F.glog.show_log(a:d.graph, a:cs, text)
+    return [text.text, text.block_r, text.special]
+endfunction
+"▶2 iterfuncs.hg
+let s:iterfuncs.hg={}
+function s:iterfuncs.hg.start(repo, opts, ...)
+    return {'seen': [], 'state': [0, 0], 'opts': a:opts,
+                \'showparents': get(a:000, 0, [])}
+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 text={'skip': 1}
+        let skip=1
     else
-        let css=reverse(copy(a:css))
+        let text=a:d.opts.templatefunc(a:cs, a:d.opts)
+        let skip=0
     endif
-    let d={}
-    if get(a:repo, 'has_octopus_merges', 1)
-        let d.func=s:F.glog.log_show_all
+    call s:F.glog.utf(a:d.state, 'C', char, text,
+                \     s:F.glog.utfedges(a:d.seen, a:cs.hex, a:cs.parents))
+    if !has_key(text, 'text') || empty(text.text)
+        return [[], 0, 0]
+    endif
+    if !skip
+        return [text.text, text.block_r, text.special]
     else
-        let d.func=s:F.glog.generate
+        return [text.text, 0, 0]
     endif
-    return d.func(css, [a:repo.functions.getworkhex(a:repo)], a:opts)
 endfunction
 "▶1 temp
 "▶2 s:templates
 let s:datechars='YmdHM'
 function s:F.comparedates(datesel, time)
     let j=0
-    for selnum in split(a:datesel, '\v[^0-9*.]+')
+    for selnum in a:datesel
         if selnum isnot# '*'
             let spec='%'.s:datechars[j]
             if selnum is# '.'
                 let selnum=str2nr(strftime(spec))
-            else
-                if j==0 && len(selnum)==2
-                    let y=str2nr(selnum)
-                    let cy=str2nr(strftime('%y'))
-                    let c=str2nr(strftime('%Y')[:-3])
-                    if y<=cy
-                        let selnum=((c*100)+y)
-                    else
-                        let selnum=(((c-1)*100)+y)
-                    endif
-                else
-                    let selnum=str2nr(selnum)
-                endif
             endif
             let actnum=str2nr(strftime(spec, a:time))
             if actnum!=selnum
     endfor
     return 0
 endfunction
+"▶1 requiredpropslist :: opts → [csprop]
+function s:F.requiredpropslist(opts)
+    let r=[]
+    let r+=filter(['renames', 'copies'], 'get(a:opts, "show".v:val, 0)')
+    if           get(a:opts, 'showrenames', 0) ||
+                \get(a:opts, 'showcopies',  0) ||
+                \get(a:opts, 'showfiles',   0) ||
+                \get(a:opts, 'stat',        0)
+        let r+=['files']
+    endif
+    if get(a:opts, 'showfiles', 0)
+        let r+=['changes']
+    endif
+    return r
+endfunction
+"▶1 sort_in_topological_order :: [cs] → [cs]
+" TODO put sorting requirement on getchangesets and revrange functions.
+function s:F.sort_in_topological_order(repo, css)
+    try
+        call map(copy(a:css), 'extend(v:val, {"indegree": 1})')
+        for parents in map(copy(a:css), 'v:val.parents')
+            for parent in filter(map(filter(copy(parents),
+                        \                   'has_key(a:repo.changesets,v:val)'),
+                        \            'a:repo.changesets[v:val]'),
+                        \        'get(v:val, "indegree", 0)')
+                let parent.indegree+=1
+            endfor
+        endfor
+        let work=[]
+        call map(copy(a:css), 'v:val.indegree==1 && add(work, v:val) is 0')
+        call sort(work, 's:DateCmp')
+        let r=[]
+        while !empty(work)
+            let cs=remove(work, 0)
+            for parent in filter(map(filter(copy(cs.parents),
+                        \                   'has_key(a:repo.changesets,v:val)'),
+                        \            'a:repo.changesets[v:val]'),
+                        \        'get(v:val, "indegree", 0)')
+                let parent.indegree-=1
+                if parent.indegree==1
+                    let j=0
+                    let lwork=len(work)
+                    while j<lwork && work[j].time<parent.time
+                        let j+=1
+                    endwhile
+                    call insert(work, parent, j)
+                endif
+            endfor
+            let cs.indegree=0
+            call add(r, cs)
+        endwhile
+    finally
+        call map(copy(a:css), 'remove(v:val, "indegree")')
+    endtry
+    return r
+endfunction
+"▶1 iterfuncs: cs generators
+" startfunc (here)  :: repo, opts → d
+"▶2 ancestors
+let s:iterfuncs.ancestors={}
+function s:iterfuncs.ancestors.start(repo, opts)
+    let hex=a:repo.functions.getrevhex(a:repo, a:opts.revision)
+    return {'addrevs': [hex], 'revisions': {}, 'repo': a:repo}
+endfunction
+function s:iterfuncs.ancestors.next(d)
+    if empty(a:d.addrevs)
+        return 0
+    endif
+    let hex=remove(a:d.addrevs, 0)
+    if has_key(a:d.revisions, hex)
+        return s:iterfuncs.ancestors.next(a:d)
+    endif
+    let cs=a:d.repo.functions.getcs(a:d.repo, hex)
+    let a:d.revisions[hex]=cs
+    let a:d.addrevs+=cs.parents
+    return cs
+endfunction
+"▶2 revrange
+let s:iterfuncs.revrange={}
+function s:iterfuncs.revrange.start(repo, opts)
+    if has_key(a:opts, 'revrange')
+        call map(a:opts.revrange, 'a:repo.functions.getrevhex(a:repo, v:val)')
+        let cslist=copy(call(a:repo.functions.revrange,
+                    \   [a:repo]+a:opts.revrange, {}))
+    else
+        let cslist=copy(a:repo.functions.getchangesets(a:repo))
+    endif
+    if get(a:repo, 'requires_sort', 1)
+        let cslist=s:F.sort_in_topological_order(a:repo, cslist)
+    else
+        call reverse(cslist)
+    endif
+    return {'cslist': cslist}
+endfunction
+function s:iterfuncs.revrange.next(d)
+    if empty(a:d.cslist)
+        return 0
+    endif
+    return remove(a:d.cslist, 0)
+endfunction
+"▶2 changesets
+let s:iterfuncs.changesets=s:iterfuncs.revrange
+"▶1 iterfuncs.check
+" startfunc (here)  :: repo, opts → d
+let s:fcheckpropslist=['renames', 'copies', 'changes', 'files']
+let s:iterfuncs.check={}
+"▶2 iterfuncs.check.start
+"▶3 addcentury
+function s:F.addcentury(year)
+    if type(a:year)==type(0) && a:year<100
+        let curyear=str2nr(strftime('%y'))
+        let century=str2nr(strftime('%Y')[:-3])
+        if a:year<=curyear
+            let r=((century*100)+a:year)
+        else
+            let r=(((century-1)*100)+a:year)
+        endif
+        return r
+    endif
+    return a:year
+endfunction
+"▶3 redate
+function s:F.redate(datespec)
+    let date=map(split(a:datespec, '\v[^0-9*.]+'),
+                \'v:val=~#"\\d" ? str2nr(v:val) : v:val')
+    let date[0]=s:F.addcentury(date[0])
+    return date
+endfunction
+"▲3
+let s:keytoexpr={
+            \'branch': '"a:cs.branch isnot# ".string(v:val)',
+            \'merges': '(v:val)?("len(a:cs.parents)<=1"):'.
+            \                  '("len(a:cs.parents)>1")',
+            \'search': '"a:cs.description!~#".string(v:val)',
+            \  'user':        '"a:cs.user!~#".string(v:val)',
+        \}
+function s:iterfuncs.check.start(repo, opts)
+    let r={'repo': a:repo, 'hasfiles': 0, 'hasdaterange': 0, 'hasdate': 0}
+    "▶3 Define variables for files filtering
+    if has_key(a:opts, 'files')
+        let r.hasfiles=1
+        let r.csfiles={}
+        let r.filepats=a:opts.filepats
+        let r.tocheck={}
+        let a:opts.csfiles=r.csfiles
+    endif
+    "▶3 Define variables for date filtering
+    if has_key(a:opts, 'date')
+        let idx=match(a:opts.date, '\V<=\?>')
+        if idx==-1
+            let r.hasdate=1
+            let r.selector=(stridx('<>', a:opts.date[0])==-1)?(''):
+                        \                                     (a:opts.date[0])
+            let r.acceptexact=(empty(r.selector) || a:opts.date[1] is# '=')
+            let r.date=s:F.redate(a:opts.date[empty(r.selector)?(0):
+                        \                     (len(r.selector)+r.acceptexact):])
+        else
+            let r.hasdaterange=1
+            let r.date1=s:F.redate(a:opts.date[:(idx-1)])
+            let r.acceptexact=(a:opts.date[idx+1] is# '=')
+            let r.date2=s:F.redate(a:opts.date[(idx+2+r.acceptexact):])
+        endif
+    endif
+    "▶3 Determine other filters
+    let r.expr=join(values(map(filter(copy(a:opts),
+                \                     'has_key(s:keytoexpr, v:key)'),
+                \              'eval(s:keytoexpr[v:key])')), '||')
+    "▲3
+    return r
+endfunction
+"▶2 iterfuncs.check.check
+function s:iterfuncs.check.check(d, cs)
+    "▶3 Check simple cases
+    if !empty(a:d.expr) && eval(a:d.expr)
+        return 0
+    endif
+    "▶3 Check files
+    if a:d.hasfiles
+        let copies =a:d.repo.functions.getcsprop(a:d.repo, a:cs, 'copies' )
+        let renames=a:d.repo.functions.getcsprop(a:d.repo, a:cs, 'renames')
+        let changes=a:d.repo.functions.getcsprop(a:d.repo, a:cs, 'changes')[:]
+        let csfiles=[]
+        let tcfiles=[]
+        let a:d.csfiles[a:cs.hex]=csfiles
+        if has_key(a:d.tocheck,a:cs.hex)
+            let tc=a:d.tocheck[a:cs.hex]
+            call filter(changes, '(index(tc, v:val)==-1)?(1):'.
+                        \           '([0, add(csfiles, v:val)][0])')
+            call filter(tc, 'index(csfiles, v:val)==-1')
+            if !empty(tc)
+                let allfiles=a:d.repo.functions.getcsprop(a:d.repo, a:cs,
+                            \                             'allfiles')
+                let tcfiles+=filter(copy(allfiles), 'index(tc, v:val)!=-1')
+            endif
+        endif
+        for pattern in a:d.filepats
+            let newchanges=[]
+            call map(copy(changes), 'add(((v:val=~#'.string(pattern).')?'.
+                        \                   '(csfiles):'.
+                        \                   '(newchanges)), v:val)')
+            if empty(newchanges)
+                break
+            endif
+            let changes=newchanges
+        endfor
+        for file in csfiles
+            let tcfiles+=map(filter(['renames', 'copies'],
+                        \           'has_key({v:val}, file) && '.
+                        \           '{v:val}[file] isnot 0'),
+                        \    '{v:val}[file]')
+        endfor
+        if !empty(tcfiles)
+            call map(copy(a:cs.parents), 'extend(a:d.tocheck, '.
+                        \'{v:val : get(a:d.tocheck, v:val, [])+tcfiles})')
+        endif
+        if empty(csfiles)
+            return 0
+        endif
+    endif
+    "▶3 Check date
+    if a:d.hasdate
+        let cmpresult=s:F.comparedates(a:d.date, a:cs.time)
+        if !((a:d.acceptexact && cmpresult==0) ||
+                    \(a:d.selector is# '<' && cmpresult==-1) ||
+                    \(a:d.selector is# '>' && cmpresult==1))
+            return 0
+        endif
+    elseif a:d.hasdaterange
+        let cmp1result=s:F.comparedates(a:d.date1, a:cs.time)
+        let cmp2result=s:F.comparedates(a:d.date2, a:cs.time)
+        if !((cmp1result==1 && cmp2result==-1) ||
+                    \(a:d.acceptexact && (cmp1result==0 ||
+                    \                     cmp2result==0)))
+            return 0
+        endif
+    endif
+    "▲3
+    return 1
+endfunction
 "▶1 setup
-"▶2 trackfile :: repo, cs, file, csfiles → + csfiles
-function s:F.trackfile(repo, cs, file, csfiles)
-    let tocheck=[[a:file, a:cs]]
-    while !empty(tocheck)
-        let [file, cs]=remove(tocheck, 0)
-        if !has_key(a:csfiles, cs.hex)
-            continue
-        endif
-        let rename=get(cs.renames, file, 0)
-        if type(rename)!=type('')
-            let rename=file
-        endif
-        let copy=get(cs.copies, file, 0)
-        if type(copy)==type('')
-            if index(cs.changes, file)!=-1 && index(a:csfiles[cs.hex], file)==-1
-                let a:csfiles[cs.hex]+=[copy]
-            endif
-            let tocheck+=map(copy(cs.parents),'[copy,a:repo.changesets[v:val]]')
-        endif
-        if index(a:repo.functions.getcsprop(a:repo, cs, 'allfiles'), file)==-1
-            continue
-        endif
-        if index(cs.changes, file)!=-1 && index(a:csfiles[cs.hex], file)==-1
-            let a:csfiles[cs.hex]+=[file]
-        endif
-        let tocheck+=map(copy(cs.parents), '[rename, a:repo.changesets[v:val]]')
-    endwhile
-endfunction
 "▶2 getkwreg
 function s:F.getkwreg(kw, nextlit)
     if has_key(s:kwreg, a:kw)
 function s:F.setup(read, repo, opts)
     let opts=a:opts
     let bvar={}
-    call a:repo.functions.getchangesets(a:repo)
     "▶2 Add `ignorefiles'
     let ignorefiles=(has_key(opts, 'ignfiles')?
                 \               (opts.ignfiles):
     let opts.ignorefiles={}
     call map(copy(ignorefiles), 'extend(opts.ignorefiles, {v:val : 1})')
     unlet ignorefiles
-    "▶2 Get revision range
-    if has_key(opts, 'revrange')
-        let opts.revs=map(copy(opts.revrange),
-                    \'a:repo.changesets['.
-                    \   'a:repo.functions.getrevhex(a:repo, v:val)].hex')
-    elseif get(opts, 'limit', 0)>0
-        let opts.revs=[-opts.limit, -1]
-    else
-        let opts.revs=[0, -1]
-    endif
-    "▶2 Process `revision' option
-    if has_key(opts, 'revision')
-        let hex=a:repo.functions.getrevhex(a:repo, opts.revision)
-        let cs=a:repo.changesets[hex]
-        if type(cs.rev)==type(0) && cs.rev<opts.revs[1]
-            let opts.revs[1]=cs.hex
-        endif
-        let opts.revisions={}
-        let addrevs=[cs]
-        while !empty(addrevs)
-            let cs=remove(addrevs, 0)
-            if has_key(opts.revisions, cs.hex)
-                continue
-            endif
-            let opts.revisions[cs.hex]=1
-            let addrevs+=map(copy(cs.parents), 'a:repo.changesets[v:val]')
-        endwhile
-    endif
-    "▲2
-    let css=a:repo.functions.revrange(a:repo, opts.revs[0], opts.revs[1])
-    "▶2 Generate cs.{kw} for various options (`show{kw}'+`files')
-    for key in ['renames', 'copies']
-        if get(opts, 'show'.key, 0) || has_key(opts, 'files')
-            for cs in css
-                call a:repo.functions.getcsprop(a:repo, cs, key)
-            endfor
-        endif
-    endfor
-    "▶2 Generate cs.files for several options
-    if has_key(opts, 'files') || get(opts, 'showrenames', 0) ||
-                \                get(opts, 'showcopies',  0) ||
-                \                get(opts, 'showfiles',   0) ||
-                \                get(opts, 'stat',        0)
-        for cs in css
-            call a:repo.functions.getcsprop(a:repo, cs, 'files')
-        endfor
-    endif
-    "▶2 Generate cs.changes for showfiles option
-    if has_key(opts, 'files') || get(opts, 'showfiles', 0)
-        for cs in css
-            call a:repo.functions.getcsprop(a:repo, cs, 'changes')
-        endfor
-    endif
-    "▶2 Generate file lists for `files' option
-    if has_key(opts, 'files')
-        let opts.csfiles={}
-        for cs in css
-            let changes=copy(cs.changes)
-            let csfiles=[]
-            let opts.csfiles[cs.hex]=csfiles
-            for pattern in opts.filepats
-                let newfiles=filter(copy(changes), 'v:val=~#pattern')
-                call filter(changes, 'index(newfiles, v:val)==-1')
-                let csfiles+=newfiles
-                if empty(changes)
-                    break
-                endif
-            endfor
-            call map(copy(csfiles), 's:F.trackfile(a:repo, cs, v:val, '.
-                        \                         'opts.csfiles)')
-        endfor
-        let opts.totrack={}
-    endif
-    "▶2 Narrow changeset range
-    let opts.skipchangesets={}
-    let firstnoskip=-1
-    let foundfirst=0
-    let lastnoskip=-1
-    let i=opts.revs[0]
-    let requires_sort=get(a:repo, 'requires_sort', 1)
-    for cs in css
-        let skip=0
-        "▶3 `branch', `merges', `search', `user', `revision'
-        if (has_key(opts, 'branch') && cs.branch isnot# opts.branch)||
-                    \(has_key(opts, 'merges') &&
-                    \   ((opts.merges)?(len(cs.parents)<=1):
-                    \                  (len(cs.parents)>1))) ||
-                    \(has_key(opts, 'search') &&
-                    \   cs.description!~#opts.search) ||
-                    \(has_key(opts, 'user') && cs.user!~#opts.user) ||
-                    \(has_key(opts, 'revision') &&
-                    \   !has_key(opts.revisions, cs.hex))
-            let skip=1
-        "▶3 `date'
-        elseif has_key(opts, 'date')
-            if match(opts.date, '\V<=\?>')!=-1
-                let [date1, date2]=split(opts.date, '\V<=\?>')
-                let acceptexact=(stridx(opts.date, '<=>', len(date1))!=-1)
-                let cmp1result=s:F.comparedates(date1, cs.time)
-                let cmp2result=s:F.comparedates(date2, cs.time)
-                if !((cmp1result==1 && cmp2result==-1) ||
-                            \(acceptexact && (cmp1result==0 ||
-                            \                 cmp2result==0)))
-                    let skip=1
-                endif
-            else
-                let selector=opts.date[0]
-                let acceptexact=(stridx('<>', selector)==-1 || opts.date[1] is# '=')
-                let cmpresult=s:F.comparedates(opts.date, cs.time)
-                if !((acceptexact && cmpresult==0) ||
-                            \(selector is# '<' && cmpresult==-1) ||
-                            \(selector is# '>' && cmpresult==1))
-                    let skip=1
-                endif
-            endif
-        endif
-        "▶3 `files'
-        if !skip && has_key(opts, 'files')
-            let files=opts.csfiles[cs.hex]
-            if empty(files)
-                let skip=1
-            endif
-        endif
-        "▲3
-        if requires_sort
-            if skip
-                let opts.skipchangesets[cs.hex]=cs
-            endif
-        elseif skip
-            if foundfirst
-                let opts.skipchangesets[cs.hex]=cs
-            endif
-        else
-            if foundfirst
-                let lastnoskip=cs.hex
-            else
-                let foundfirst=1
-                let firstnoskip=cs.hex
-            endif
-        endif
-        let i+=1
-    endfor
-    if firstnoskip!=-1
-        let opts.revs[0]=firstnoskip
-    endif
-    if lastnoskip!=-1
-        let opts.revs[1]=lastnoskip
-    endif
+    "▶2 Get cslist
+    let iterfuncs=s:iterfuncs[((has_key(opts, 'revision'))?
+                \                ('ancestors'):
+                \             ((has_key(opts, 'revrange'))?
+                \                ('revrange')
+                \             :
+                \                ('changesets')))]
     "▶2 Get template
     if has_key(opts, 'template')
         let template=eval(opts.template)
     let bvar.templatelist=s:F.temp.parse(template)
     let opts.templatefunc=s:F.temp.compile(bvar.templatelist, opts)
     "▲2
-    let css=a:repo.functions.revrange(a:repo, opts.revs[0], opts.revs[1])
-    let text=s:F.glog.graphlog(a:repo, opts, css)
-    let bvar.specials=text.specials
-    let bvar.rectangles=text.rectangles
-    let bvar.csstarts=text.csstarts
-    let bvar.cw=s:_f.getoption('closewindow')
     if !a:read
-        setlocal noreadonly modifiable
-    endif
-    call s:_r.setlines(text.text, a:read)
-    if !a:read
-        setlocal readonly nomodifiable buftype=nofile
+        "▶ Required for setting syntax definitions and also for maps to work
+        " Is normally done by edit.vim, but in our case it is needed earlier
+        let bvar.opts=a:opts
+        let bvar.repo=a:repo
+        let s:_r.bufvars[bufnr('%')]=bvar
+        "▲
+        let bvar.procinput=(has_key(a:opts, 'procinput')?
+                    \           (2*a:opts.procinput):
+                    \           s:_f.getoption('procinput'))
+        if bvar.procinput==1 && getchar(1)
+            let bvar.procinput=0
+        endif
+        setlocal buftype=nofile filetype=aurumlog
         augroup AuLogNoInsert
             autocmd InsertEnter <buffer> :call feedkeys("\e", "n")
         augroup END
     endif
+    let bvar.cw=s:_f.getoption('closewindow')
+    let text=s:F.glog.graphlog(a:repo, opts, iterfuncs, bvar, a:read)
+    if a:read
+        call s:_r.setlines(text, a:read)
+    else
+        setlocal readonly nomodifiable
+    endif
     return bvar
 endfunction
 let s:_augroups+=['AuLogNoInsert']
             \          '  !?showfiles'.
             \          '  !?showrenames'.
             \          '  !?showcopies'.
+            \          '  !?procinput'.
             \          s:_r.repo.diffoptsstr.
             \          '   ?cmd      type ""'.
             \          '}', 'filter']
             \'function': s:F.setup,
             \ 'options': {'list': ['files', 'revrange', 'ignfiles'],
             \             'bool': ['merges', 'patch', 'stat', 'showfiles',
-            \                      'showrenames', 'showcopies'],
+            \                      'showrenames', 'showcopies', 'procinput'],
             \              'num': ['limit']+s:_r.repo.diffoptslst,
             \              'str': ['date', 'search', 'user', 'branch',
             \                      'revision', 'style', 'template',

File plugin/aurum/record.vim

                 \                   '@/functions': '0.0',
                 \                 '@aurum/commit': '0.0',
                 \               '@aurum/cmdutils': '0.0',
-                \                   '@aurum/repo': '1.2',
+                \                   '@aurum/repo': '2.0',
                 \                   '@aurum/edit': '1.0',
                 \                     '@/options': '0.0',}, 0)
     call FraworLoad('@/commands')

File plugin/aurum/repo.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('1.3', {'@/resources': '0.0',
+    execute frawor#Setup('2.0', {'@/resources': '0.0',
                 \                       '@/os': '0.0',
                 \                  '@/options': '0.0',
                 \             '@aurum/bufvars': '0.0',}, 0)

File plugin/aurum/status.vim

     execute frawor#Setup('0.0', {'@/resources': '0.0',
                 \            '@aurum/cmdutils': '0.0',
                 \                      '@/fwc': '0.2',
-                \                '@aurum/repo': '1.0',
+                \                '@aurum/repo': '2.0',
                 \                '@aurum/edit': '1.0',
                 \                 '@/commands': '0.0',
                 \                  '@/options': '0.0',
 :let g:curtest='gittest'
+:let g:tipname='HEAD'
 :W{{{1 Annotate
 :Run! AuAnnotate file ./gittestrepo/* | wincmd w
 :bwipeout!

File test/git.ok

Binary file modified.

File test/log-styles.ok

 |  :+    echo 'Abc' > ignoredabc
 |  :+)
 |  :+tar cJf testrepo.tar.xz testrepo
-|  :
+|  :

File test/log-templates.ok

 o  
 |  
 o  
-|  
+|  
 {{{1 date
 {{{2 years
 {{{3 99
-|\ 
-|/ 
-|\ 
-|\ \ 
-| | |\ 
-| | |\ \ 
-| | | | |/ 
-| +-----+ 
-| +-----+ 
-|\ \ \ \ 
-| |/ / / 
-| +---+ 
-|  / / 
-| |/ 
-|/ 
 o  Changeset 0:99ea42d70fbe32af955de907352999b1d94bef5f
 |  Commited 02 Янв 1999 05:20 by A <a@example.com>
 |  @ Added «createrepo.zsh»
 |  @ Added «createrepo.zsh»
 |  
 {{{3 05
-|\ 
-|/ 
-|\ 
-|\ \ 
-| | |\ 
-| | |\ \ 
-| | | | |/ 
-| +-----+ 
-| +-----+ 
-|\ \ \ \ 
-| |/ / / 
-| +---+ 
-|  / / 
-| |/ 
-|/ 
 o  Changeset 4:7e16e7cbaf9ec5b0e65832bc6f057ba825c38cac
 |  Commited 10 Май 2000 04:23 by A <a@example.com>
 |  @ Added «hglines.lst» and «datelines.lst»
 |  @     }
 |  
 {{{3 01
-|\ 
-|/ 
 o  Changeset 20:67cf252de26b12da6769114cc89b793357f5a46a
 |  Commited 10 Фев 2002 16:01 by B <b@example.org>
 |  @ Added nohglinesrev.lst
 | | | | |  @ Merge from B
 | | | | |  
 {{{2 <date
-|\ 
-|/ 
-|\ 
-|\ \ 
-| | |\ 
-| | |\ \ 
-| | | | |/ 
-| +-----+ 
-| +-----+ 
-|\ \ \ \ 
-| |/ / / 
-| +---+ 
-|  / / 
-| |/ 
-|/ 
 o  Changeset 0:99ea42d70fbe32af955de907352999b1d94bef5f
 |  Commited 02 Янв 1999 05:20 by A <a@example.com>
 |  @ Added «createrepo.zsh»
 |  
 {{{2 <=date
-|\ 
-|/ 
-|\ 
-|\ \ 
-| | |\ 
-| | |\ \ 
-| | | | |/ 
-| +-----+ 
-| +-----+ 
-|\ \ \ \ 
-| |/ / / 
-| +---+ 
-|  / / 
-| |/ 
-|/ 
 o  Changeset 0:99ea42d70fbe32af955de907352999b1d94bef5f
 |  Commited 02 Янв 1999 05:20 by A <a@example.com>
 |  @ Added «createrepo.zsh»
 |  Tags: clines, oldtag
 |  @ Added clines.lst
 |  
-|/ 
 o  Changeset 10:c4110a066208167dae46b08f4e0d8d2b37e842f3 (branch C)
 |  Commited 01 Фев 2001 05:18 by A <a@example.com>
 |  Tags: blines
 |  @ Added blines.lst
 |  
-| |\ 
-o---+  Changeset 8:250ba7cd831ee91c3ba5bef1faebc38c5dbca5f8 (branch C)
-  | |  Commited 02 Ноя 2000 14:14 by B <b@example.org>
- / /   @ Added tag ablines for changeset c06107b7bcbe
-| |    
-| o  Changeset 7:c06107b7bcbe751d2dd40faf4179ee7d6aac44ac (branch C)
-| |  Commited 02 Ноя 2000 14:13 by B <b@example.org>
-| |  Tags: ablines
-| |  @ Added ablines.lst
-| |  
-| o  Changeset 6:9fe0c28c3e1dd175e8372b742f13cbb7bd6799fe (branch B)
-| |  Commited 02 Ноя 2000 05:44 by A <a@example.com>
-| |  @ Added alines.lst
-| |  
-| o  Changeset 5:d760b0fe4fe45adf585f5030181b8081899e26a9 (branch A)
-|/   Commited 01 Ноя 2000 13:24 by B <b@example.org>
-|    @ Added dlines.lst
-|    
+o  Changeset 8:250ba7cd831ee91c3ba5bef1faebc38c5dbca5f8 (branch C)
+|  Commited 02 Ноя 2000 14:14 by B <b@example.org>
+|  @ Added tag ablines for changeset c06107b7bcbe
+|  
+o  Changeset 7:c06107b7bcbe751d2dd40faf4179ee7d6aac44ac (branch C)
+|  Commited 02 Ноя 2000 14:13 by B <b@example.org>
+|  Tags: ablines
+|  @ Added ablines.lst
+|  
+o  Changeset 6:9fe0c28c3e1dd175e8372b742f13cbb7bd6799fe (branch B)
+|  Commited 02 Ноя 2000 05:44 by A <a@example.com>
+|  @ Added alines.lst
+|  
+o  Changeset 5:d760b0fe4fe45adf585f5030181b8081899e26a9 (branch A)
+|  Commited 01 Ноя 2000 13:24 by B <b@example.org>
+|  @ Added dlines.lst
+|  
 o  Changeset 4:7e16e7cbaf9ec5b0e65832bc6f057ba825c38cac
 |  Commited 10 Май 2000 04:23 by A <a@example.com>
 |  @ Added «hglines.lst» and «datelines.lst»
 |  Commited 02 Янв 1999 05:20 by A <a@example.com>
 |  @ Added «createrepo.zsh»
 |  
+o  Changeset -1:0000000000000000000000000000000000000000
+/  Commited 01 Янв 1970 00:00 by 
+   
 {{{1 revrange
 o  Changeset 18:f44a21859e57e4abc6155da804d08dd5069e798e
 |  Commited 10 Фев 2002 05:00 by A <a@example.com>
 o | | |    Changeset 9:269399222040415b3928a316f5d28792cc0be4dd
 |\ \ \ \   Commited 01 Янв 2001 00:00 by C <c@example.gov>
 | | | | |  @ Merge from C
-| | | | |  
+| | | | |  

File test/logmaps.ok

 {{{1 changeset <CR>
 aurum://log:%ETMPDIR%%-test%-logmapsrepo:crrestrict:revision,revision:504f74154456dbb0e9441326514e42ce66279e62,
 {{{1 gF
-aurum://log:%ETMPDIR%%-test%-logmapsrepo:showfiles:1,files:directory%-file,crrestrict:files,
+aurum://log:%ETMPDIR%%-test%-logmapsrepo:files:directory%-file,crrestrict:files,showfiles:1,
 {{{1 gF+files
-aurum://log:%ETMPDIR%%-test%-logmapsrepo:showfiles:1,files:.hgignore,crrestrict:files,
+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
 endfunction
 command -nargs=1 -bar W call WriteFile(<q-args>)
 function s:WriteTip(writecom, cmd)
-    R AuLog limit 1
+    let tipname=get(g:, 'tipname', 'tip')
+    execute 'R AuLog limit 1 revision '.tipname
     setlocal modifiable noreadonly
     if !a:writecom
         g/Commited/delete _