Commits

ZyX_I committed 24f22e7

@aurum/repo, @aurum/drivers/*, @aurum/cmdutils:
Finally removed repo.cslist
Added repo.mutable
Changed repo.iterfuncs.* format
Made rf-updatechangesets() be called any time repo object is obtained (except for aurum#repo() cache, of course)

Comments (0)

Files changed (18)

autoload/aurum.vim

 "▶1 
-execute frawor#Setup('0.0', {'@aurum/repo': '3.0',
+execute frawor#Setup('0.0', {'@aurum/repo': '4.0',
             \               '@aurum/cache': '0.0',})
 "▶1 aurum#repository
 function aurum#repository()
 
 Repository is a dictionary containing at least the following keys:
 
-cslist :: [ cs ]                                           *aurum-repo.cslist*
-    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-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].
+mutable :: a                                              *aurum-repo.mutable*
+    Any mutable data needed by repository functions. This and previous keys 
+    are the only ones that are not locked after repository object was created.
 csnum :: UInt                                               *aurum-repo.csnum*
     Total number of revisions in repository. For any changeset 
     cs.rev<repo.csnum. See also |aurum-cs.rev|.
         startfunc :: repo, opts -> d
          nextfunc :: d -> cs
     Dictionary containing keys "ancestors", "revrange" and "changesets" (all 
-    are optional). Each key’s value should in turn contain dictionary 
-    containing two keys: "start" and "next", both corresponding values have to 
-    be function references:
-    1. "start" function must accept |aurum-repo| dictionary and |:AuLog| 
-       options dictionary and return something that will be saved (for 
-       example, |Dictionary| containing a counter).
-       Note due to |E704| error returned value must not be a function 
-            reference.
-       Note 2: try to omit processsing options dictionary more then it is 
-               required to do the job (see below). It will be done in any case 
-               by log generator function.
-    2. "next" function must accept value returned by "start" function and 
-       return |aurum-changeset| object. Returned objects must be sorted in 
-       topological order like described under |aurum-rf-getchangesets|.
+    are optional). Each key’s value should be a reference to a function that 
+    accepts |aurum-repo| dictionary and |:AuLog| options dictionary and 
+    returns a dictionary containing at least the “next” key. The “next” value 
+    is a function reference that has previously returned dictionary in |self| 
+    variable and must return either a |aurum-changeset| or zero indicating 
+    that there are no changesets left. Returned changeset objects must be 
+    sorted in topological order like described under |aurum-rf-getchangesets|.
+    Note: try to omit processsing options dictionary more then it is required 
+          to do the job (see below). It will be done in any case by log 
+          generator function.
 
     Value of "changesets" key will be used to view full repository history.
     Value of "revrange" key will be used to view history between specified 
   revrange :: hex, hex -> [ cs ]                           *aurum-rf-revrange*
     Get revision range (inclusive).
   updatechangesets :: () -> _                      *aurum-rf-updatechangesets*
-    Updates data stored in repository. Is called automatically if 
-    |aurum-repo.cslist| is not empty when you get the repository object unless 
-    this object is obtained from cash used by |aurum#repository()|.
+    Updates data stored in repository. Is called each time when you get the 
+    repository object unless this object is obtained from cash used by 
+    |aurum#repository()|.
   getrevhex :: csdescr -> hex                             *aurum-rf-getrevhex*
     Given some changeset description (it may be tag, bookmark, branch name, 
     revision number, ... - whatever VCS supports) get changeset hash.
 |aurum-cs.tags| contains list of all references pointing to given commit, not 
     just tags.
 |aurum-cs.rev| contains truncated hash (for use in |aurum://annotate|).
-|aurum-repo.cslist| is always empty
 |aurum-rf-status|: if at least one of first two optional arguments is 
     non-zero, then then unknown and deleted files won’t be shown (if any). 
     Ignored files are not shown in any case.
     conflicts”: if file A was modified in both parents, then it is to be 
     listed in merge_cs.changes only if git was unable to merge changes and had 
     to ask user to do this).
-|aurum-rf-getchangesets| is not modifying |aurum-repo.cslist|.
 |aurum-rf-grep| will either search working directory (if no revisions were 
     given) or given revisions (mercurial driver is searching all revisions if 
     no revisions are given). Unlike mercurial where matching line will be 
 |aurum-cs.time| is always 0 if “date” programm from coreutils is missing.
 |aurum-cs.hex| contains the stringified value of |aurum-cs.rev| and thus is 
     not fixed-length.
-|aurum-repo.cslist|: repo.cslist[cs.rev] is any revision with .rev≥cs.rev, not 
-    necessary cs itself.
 |aurum-rf-diff|: Options "git", "iblanks", "dates", "alltext" are not 
     supported, option "reverse" is supported only if two revisions are given.
 |aurum-rf-status|: if at least one of first two optional arguments is 
     2.5: Added parsecmdarg and crnl resources
     3.0: Moved setlines and some other functions to @aurum/lineutils
     3.1: Added |aurum-rf-push| and |aurum-rf-pull|
+    4.0: Made |aurum-rf-updatechangesets| be called regardless of 
+         aurum-repo.cslist being empty, changed format of 
+         |aurum-repo.iterfuncs|, removed repo.cslist, added 
+         |aurum-repo.mutable|.
 @aurum:
     0.1: Added |:AuBranch| and |:AuName|.
     0.2: Added |:AuOther|.

ftplugin/aurumannotate.vim

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

ftplugin/aurumlog.vim

 setlocal nomodeline
 execute frawor#Setup('0.0', {'@aurum/cmdutils': '1.0',
             \                 '@aurum/bufvars': '0.0',
-            \                    '@aurum/repo': '3.0',
+            \                    '@aurum/repo': '4.0',
             \                    '@aurum/edit': '1.0',
             \                           '@/os': '0.0',
             \                 '@aurum/vimdiff': '1.0',
                 \                      '@/os': '0.1',
                 \           '@aurum/cmdutils': '1.0',
                 \                     '@/fwc': '0.2',
-                \               '@aurum/repo': '3.1',
+                \               '@aurum/repo': '4.0',
                 \               '@aurum/edit': '1.0',
                 \            '@aurum/bufvars': '0.0',}, 0)
     "▶2 Команды

plugin/aurum/cmdutils.vim

 if !exists('s:_pluginloaded')
     execute frawor#Setup('1.0', {'@/resources': '0.0',
                 \                       '@/os': '0.0',
-                \                '@aurum/repo': '3.0',
+                \                '@aurum/repo': '4.0',
                 \                '@aurum/edit': '1.0',
                 \               '@aurum/cache': '0.0',
                 \             '@aurum/bufvars': '0.0',}, 0)
         call s:_f.throw(a:failmsg)
     endif
     "▶2 Update repository if appropriate
-    if exists('repo') && exists('bvar.repo') &&
-                \repo is bvar.repo && !empty(repo.cslist)
+    if exists('repo') && exists('bvar.repo') && repo is bvar.repo
         call repo.functions.updatechangesets(repo)
     endif
     "▲2

plugin/aurum/commit.vim

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

plugin/aurum/diff.vim

                 \                 '@aurum/bufvars': '0.0',
                 \               '@aurum/lineutils': '0.0',
                 \                 '@aurum/vimdiff': '1.0',
-                \                    '@aurum/repo': '3.0',
+                \                    '@aurum/repo': '4.0',
                 \                    '@aurum/edit': '1.2',
                 \                           '@/os': '0.0',
                 \                          '@/fwc': '0.0',

plugin/aurum/drivers/git.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('0.1', {   '@aurum/repo': '3.0',
+    execute frawor#Setup('0.1', {   '@aurum/repo': '4.0',
                 \                          '@/os': '0.1',
                 \   '@aurum/drivers/common/utils': '0.0',
                 \'@aurum/drivers/common/hypsites': '0.0',}, 0)
 endfunction
 "▶1 git.repo :: path → repo
 function s:git.repo(path)
-    let repo={'path': a:path, 'changesets': {}, 'cslist': [],
+    let repo={'path': a:path, 'changesets': {}, 'mutable': {},
                 \'local': (stridx(a:path, '://')==-1),
                 \'labeltypes': ['tag', 'branch'],
                 \'hasrevisions': 0, 'requires_sort': 0,

plugin/aurum/drivers/mercurial.vim

 scriptencoding utf-8
 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.2', {      '@/python': '0.0',
-                \                   '@aurum/repo': '3.0',
+                \                   '@aurum/repo': '4.0',
                 \                          '@/os': '0.0',
                 \                     '@/options': '0.0',
                 \   '@aurum/drivers/common/utils': '0.0',
 "▶1 removechangesets :: repo, start_rev_num → + repo
 function s:F.removechangesets(repo, start)
     let changesets=a:repo.changesets
-    for cs in remove(a:repo.cslist, a:start, -1)
+    for cs in remove(a:repo.mutable.cslist, a:start, -1)
         let hex=cs.hex
         for parenthex in filter(cs.parents, 'has_key(changesets, v:val)')
             call filter(changesets[parenthex].children, 'v:val isnot# cs.hex')
     call map(copy(a:css), 'extend(a:repo.changesets, {v:val.hex : v:val})')
     for cs in a:css
         call map(cs.parents, 'type(v:val)=='.type(0).' ? '.
-                    \           'a:repo.cslist[v:val].hex : '.
+                    \           'a:repo.mutable.cslist[v:val].hex : '.
                     \           'v:val')
         for parenthex in cs.parents
             call add(a:repo.changesets[parenthex].children, cs.hex)
 endif
 "▶1 hg.getcs :: repo, rev → cs
 function s:hg.getcs(repo, rev)
-    if !empty(a:repo.cslist)
+    if !empty(a:repo.mutable.cslist)
         if type(a:rev)==type('') && has_key(a:repo.changesets, a:rev)
             return a:repo.changesets[a:rev]
         elseif type(a:rev)==type(0) && a:rev<a:repo.csnum
-            return a:repo.cslist[a:rev]
+            return a:repo.mutable.cslist[a:rev]
         endif
     endif
     if a:rev=~#'\v^[0-9a-f]{40}$'
     else
         let hex=a:repo.functions.getrevhex(a:repo, a:rev)
     endif
-    if has_key(a:repo.changesets, hex) && !empty(a:repo.cslist)
+    if has_key(a:repo.changesets, hex) && !empty(a:repo.mutable.cslist)
         return a:repo.changesets[hex]
     else
         let cs=s:F.getcs(a:repo, hex)
 endfunction
 "▶1 hg.getchangesets :: repo → changesets + repo.changesets
 function s:hg.getchangesets(repo)
-    call a:repo.functions.updatechangesets(a:repo)
-    return a:repo.cslist[:-2]
+    call a:repo.functions.updatechangesets(a:repo, 1)
+    return a:repo.mutable.cslist[:-2]
 endfunction
 "▶1 hg.revrange :: repo, rev, rev → [cs]
 function s:hg.revrange(repo, rev1, rev2)
-    if empty(a:repo.cslist)
+    if empty(a:repo.mutable.cslist)
         let cslist=a:repo.functions.getchangesets(a:repo)
     else
-        let cslist=a:repo.cslist
+        let cslist=a:repo.mutable.cslist
     endif
     let rev1=a:repo.functions.getcs(a:repo, a:rev1).rev
     let rev2=a:repo.functions.getcs(a:repo, a:rev2).rev
             let list=s:F.getkeylist(a:repo, key)
             let r[key]={}
             for [name, rev] in filter(copy(list), 'v:val[1]<'.a:start)
-                let r[key][name]=a:repo.cslist[rev].hex
+                let r[key][name]=a:repo.mutable.cslist[rev].hex
             endfor
         endfor
         let a:repo.csnum=a:start+len(r.css)
 endif
 "▶2 hg.updatechangesets
 " TODO test updating in cases of rollback
-function s:hg.updatechangesets(repo)
+function s:hg.updatechangesets(repo, ...)
+    if !(a:0 && a:1) && empty(a:repo.mutable.cslist)
+        return
+    endif
     let d={}
-    let start=len(a:repo.cslist)-2
+    let start=len(a:repo.mutable.cslist)-2
     if start<0
         let start=0
     endif
     " XXX getupdates may also modify repo
     let d=s:F.getupdates(a:repo, start)
     if empty(d)
-        return a:repo
+        return
     endif
     call map(d.css, 'extend(v:val, {"children": []})')
-    if !empty(a:repo.cslist)
+    if !empty(a:repo.mutable.cslist)
         call s:F.removechangesets(a:repo, d.startrev)
     endif
     for key in ['tags', 'bookmarks']
-        call map(a:repo.cslist, 'extend(v:val, {key : []})')
+        call map(a:repo.mutable.cslist, 'extend(v:val, {key : []})')
         for [name, hex] in filter(items(d[key]),
                     \             'has_key(a:repo.changesets, v:val[1])')
             let cs=a:repo.changesets[hex]
             call sort(cs[key])
         endfor
     endfor
-    let a:repo.cslist+=d.css
+    let a:repo.mutable.cslist+=d.css
     call s:F.addchangesets(a:repo, d.css)
-    return a:repo
 endfunction
 "▶1 hg.getrevhex :: repo, rev → rev(hex)
 if s:usepythondriver "▶2
     if type(a:rev)==type('') && (has_key(a:repo.changesets, a:rev) ||
                 \                a:rev=~#'\v^[0-9a-f]{40}$')
         return a:rev
-    elseif type(a:rev)==type(0) && a:rev<len(a:repo.cslist)
-        return a:repo.cslist[a:rev].hex
+    elseif type(a:rev)==type(0) && a:rev<len(a:repo.mutable.cslist)
+        return a:repo.mutable.cslist[a:rev].hex
     endif
     let hex=get(s:F.hg(a:repo, 'log', [], {'template': s:getrevhextemplate,
                 \                               'rev': ''.a:rev}, 0, 'log'),0,0)
         let r=map(split(s:F.hg(a:repo, 'log', [],
                     \          {'rev': a:cs.hex, 'template': '{children}'},
                     \          0, 'csp', a:prop, a:cs.rev)[0]), 'str2nr(v:val)')
-        if empty(a:repo.cslist)
+        if empty(a:repo.mutable.cslist)
             call map(r, 'a:repo.functions.getrevhex(a:repo, v:val)')
         else
-            call map(r, 'a:repo.cslist[v:val].hex')
+            call map(r, 'a:repo.mutable.cslist[v:val].hex')
         endif
     endif
     let a:cs[a:prop]=r
 else "▶2
 function s:hg.repo(path)
     " TODO remove bookmark label type if it is not available
-    let repo={'path': a:path, 'changesets': {}, 'cslist': [],
+    let repo={'path': a:path, 'changesets': {}, 'mutable': {'cslist': []},
                 \'local': (stridx(a:path, '://')==-1),
                 \'labeltypes': ['tag', 'bookmark'],
                 \'has_octopus_merges': 0, 'requires_sort': 0,
 endfunction
 "▶2 getgetcsfunc
 function s:F.getgetcsfunc(repo)
-    if empty(a:repo.cslist)
+    if empty(a:repo.mutable.cslist)
         return s:F.getrev
     else
         return s:F.getfromchangesets
     endif
 endfunction
 "▶2 iterfuncs.ancestors
-let s:iterfuncs.ancestors={}
-function s:iterfuncs.ancestors.start(repo, opts)
+function s:iterfuncs.ancestors(repo, opts)
     let cs=a:repo.functions.getcs(a:repo,
                 \a:repo.functions.getrevhex(a:repo, a:opts.revision))
     return {'addrevs': [cs], 'revisions': {}, 'repo': a:repo,
                 \'hasrevisions': get(a:repo, 'hasrevisions', 1),
-                \'getcs': s:F.getgetcsfunc(a:repo)}
+                \'getcs': s:F.getgetcsfunc(a:repo),
+                \'next': s:F.ancestorsnext,}
 endfunction
 function! s:RevCmp(cs1, cs2)
     let rev1=a:cs1.rev
     return ((rev1==rev2)?(0):((rev1<rev2)?(1):(-1)))
 endfunction
 let s:_functions+=['s:RevCmp']
-function s:iterfuncs.ancestors.next(d)
-    if empty(a:d.addrevs)
+function s:F.ancestorsnext()
+    if empty(self.addrevs)
         return 0
     endif
-    let cs=remove(a:d.addrevs, 0)
-    if has_key(a:d.revisions, cs.hex)
-        return s:iterfuncs.ancestors.next(a:d)
+    let cs=remove(self.addrevs, 0)
+    if has_key(self.revisions, cs.hex)
+        return self.next()
     endif
-    let a:d.revisions[cs.hex]=cs
-    let parents=map(copy(cs.parents),'a:d.getcs(a:d.repo,v:val)')
-    call extend(a:d.addrevs, parents)
-    if a:d.hasrevisions
-        call sort(a:d.addrevs, 's:RevCmp')
+    let self.revisions[cs.hex]=cs
+    let parents=map(copy(cs.parents),'self.getcs(self.repo,v:val)')
+    call extend(self.addrevs, parents)
+    if self.hasrevisions
+        call sort(self.addrevs, 's:RevCmp')
     endif
     return cs
 endfunction

plugin/aurum/drivers/subversion.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('0.1', {   '@aurum/repo': '3.0',
+    execute frawor#Setup('0.1', {   '@aurum/repo': '4.0',
                 \                          '@/os': '0.0',
                 \   '@aurum/drivers/common/utils': '0.0',
                 \     '@aurum/drivers/common/xml': '0.0',
         let rev=a:repo.functions.getrevhex(a:repo, a:rev)
         if has_key(a:repo.changesets, rev)
             return a:repo.changesets[rev]
-        elseif !empty(a:repo.cslist)
+        elseif !empty(a:repo.mutable.cslist)
             call a:repo.functions.updatechangesets(a:repo)
             if has_key(a:repo.changesets, rev)
                 return a:repo.changesets[rev]
 endfunction
 "▶1 svn.getchangesets :: repo → [cs]
 function s:svn.getchangesets(repo)
-    if empty(a:repo.cslist)
+    if empty(a:repo.mutable.cslist)
         let cslist=s:F.getchangesets(a:repo)
         let cslist[-1].children=[]
         call map(cslist[:-2], 'extend(v:val, {"children": ["".(v:val.rev-1)]})')
-        let a:repo.cslist+=cslist
+        let a:repo.mutable.cslist+=cslist
     else
         call a:repo.functions.updatechangesets(a:repo)
     endif
-    return a:repo.cslist
+    return a:repo.mutable.cslist
 endfunction
 "▶1 svn.revrange :: repo, rev1, rev2 → [cs]
 function s:svn.revrange(repo, rev1, rev2)
-    if empty(a:repo.cslist)
+    if empty(a:repo.mutable.cslist)
         call a:repo.functions.getchangesets(a:repo)
     else
         call a:repo.functions.updatechangesets(a:repo)
     endif
     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 i=len(a:repo.mutable.cslist)-1
+    while i>=0 && a:repo.mutable.cslist[i].hex isnot# hex1
+        if a:repo.mutable.cslist[i].hex is# hex2
             let r2i=i
         endif
         let i-=1
     if !exists('r2i')
         call s:_f.throw('r2fst', a:rev2, a:rev1)
     endif
-    return a:repo.cslist[(i):(r2i)]
+    return a:repo.mutable.cslist[(i):(r2i)]
 endfunction
 "▶1 svn.updatechangesets :: repo → _
 function s:svn.updatechangesets(repo)
-    let oldtiprev=a:repo.cslist[-1].rev
+    if empty(a:repo.mutable.cslist)
+        return
+    endif
+    let oldtiprev=a:repo.mutable.cslist[-1].rev
     let tiprev=+a:repo.functions.gettiphex(a:repo)
     if tiprev<oldtiprev
-        while !empty(a:repo.cslist) && a:repo.cslist[-1].rev>tiprev
-            call remove(a:repo.cslist, -1)
+        while !empty(a:repo.mutable.cslist) &&
+                    \a:repo.mutable.cslist[-1].rev>tiprev
+            call remove(a:repo.mutable.cslist, -1)
         endwhile
     elseif tiprev>oldtiprev
         let cslist=s:F.getchangesets(a:repo, ''.oldtiprev, ''.tiprev)
         if !empty(cslist)
-            let a:repo.cslist[-1].children=''.(a:repo.cslist[-1].rev+1)
+            let a:repo.mutable.cslist[-1].children=
+                        \''.(a:repo.mutable.cslist[-1].rev+1)
             call map(cslist[:-2], 'extend(v:val, {"children": '.
                         \                                '["".(v:val.rev-1)]})')
-            let a:repo.cslist+=cslist
+            let a:repo.mutable.cslist+=cslist
         endif
     endif
-    let a:repo.cslist[-1].children=[]
+    let a:repo.mutable.cslist[-1].children=[]
 endfunction
 "▶1 svn.gettiphex :: repo → hex
 function s:svn.gettiphex(repo)
 endfunction
 "▶1 svn.repo :: path → repo
 function s:svn.repo(path)
-    let repo={'path': a:path, 'changesets': {}, 'cslist': [],
+    let repo={'path': a:path, 'changesets': {}, 'mutable': {'cslist': []},
                 \'local': (stridx(a:path, '://')==-1),
                 \'labeltypes': [], 'hasrevisions': 1,
                 \'requires_sort': 0, 'has_octopus_merges': 0,
     return a:dir
 endfunction
 "▶1 iterfuncs.ancestors
-let s:iterfuncs.ancestors={}
-function s:iterfuncs.ancestors.start(repo, opts)
+function s:iterfuncs.ancestors(repo, opts)
     let cslist=copy(a:repo.functions.revrange(a:repo, '1', a:opts.revision))
-    return {'cslist': cslist}
+    return {'cslist': cslist, 'next': s:F.ancestorsnext}
 endfunction
-function s:iterfuncs.ancestors.next(d)
-    while !empty(a:d.cslist)
-        let cs=remove(a:d.cslist, -1)
+function s:F.ancestorsnext()
+    while !empty(self.cslist)
+        let cs=remove(self.cslist, -1)
         if !empty(cs.changes)
             return cs
         endif
     return 0
 endfunction
 "▶1 iterfuncs.changesets
-let s:iterfuncs.changesets={}
-function s:iterfuncs.changesets.start(repo, opts)
-    return {'cslist': copy(a:repo.functions.getchangesets(a:repo))}
+function s:iterfuncs.changesets(repo, opts)
+    return {'cslist': copy(a:repo.functions.getchangesets(a:repo)),
+                \'next': s:F.ancestorsnext}
 endfunction
-let s:iterfuncs.changesets.next=s:iterfuncs.ancestors.next
 "▶1 iterfuncs.revrange
-let s:iterfuncs.revrange={}
-function s:iterfuncs.revrange.start(repo, opts)
+function s:iterfuncs.revrange(repo, opts)
     let cslist=copy(a:repo.functions.revrange(a:repo, a:opts.revrange[0],
                 \                                     a:opts.revrange[1]))
-    return {'cslist': cslist}
+    return {'cslist': cslist, 'next': s:F.ancestorsnext}
 endfunction
-let s:iterfuncs.revrange.next=s:iterfuncs.ancestors.next
 "▶1 Register driver
 call s:_f.regdriver('Subversion', s:svn)
 "▶1

plugin/aurum/edit.vim

     execute frawor#Setup('1.3', {'@/autocommands': '0.0',
                 \                   '@/functions': '0.0',
                 \                   '@/resources': '0.0',
-                \                   '@aurum/repo': '3.0',
+                \                   '@aurum/repo': '4.0',
                 \              '@aurum/lineutils': '0.0',
                 \                '@aurum/bufvars': '0.0',}, 0)
     call FraworLoad('@/autocommands')

plugin/aurum/file.vim

                 \                 '@aurum/bufvars': '0.0',
                 \               '@aurum/lineutils': '0.0',
                 \                 '@aurum/vimdiff': '1.0',
-                \                    '@aurum/repo': '3.0',
+                \                    '@aurum/repo': '4.0',
                 \                    '@aurum/edit': '1.2',
                 \                           '@/os': '0.0',
                 \                          '@/fwc': '0.0',

plugin/aurum/log.vim

                 \         '@aurum/bufvars': '0.0',
                 \            '@aurum/edit': '1.1',
                 \                  '@/fwc': '0.3',
-                \            '@aurum/repo': '3.0',
+                \            '@aurum/repo': '4.0',
                 \             '@/commands': '0.0',
                 \            '@/functions': '0.0',
                 \              '@/options': '0.0',}, 0)
 let s:iterfuncs.glog={}
 let s:glogcache={}
 "▶2 iterfuncs.glog.generate
-function s:iterfuncs.glog.generate(repo, opts, csiterfuncs, bvar, read)
+function s:iterfuncs.glog.generate(repo, opts, bvar, read)
     let r={}
     "▶3 Get grapher
     if get(a:repo, 'has_octopus_merges', 1)
     "▶3 Initialize iterator functions
     let r.ld=r.literfuncs.start(a:repo, a:opts,
                 \               [a:repo.functions.getworkhex(a:repo)])
-    let r.csiterfuncs=a:csiterfuncs
-    let r.csd=a:csiterfuncs.start(a:repo, a:opts)
+    let csiterfuncsname=((has_key(a:opts, 'revision'))?
+                \          ('ancestors'):
+                \       ((has_key(a:opts, 'revrange'))?
+                \          ('revrange')
+                \       :
+                \          ('changesets')))
+    let r.csd=a:repo.iterfuncs[csiterfuncsname](a:repo, a:opts)
     let r.checkd=s:iterfuncs.check.generate(a:repo, a:opts)
     let hascheck=(r.checkd isnot 0)
     if !hascheck
     "▲3
     let fflines=[]
     let function=['function r.next()',
-                \ '    let cs=self.csiterfuncs.next(self.csd)',
+                \ '    let cs=self.csd.next()',
                 \ '    if cs is 0 | return -1 | endif',]
     if hascheck
         let function+=['    let skip=!self.checkd.check(cs)',
     if has_key(gd, 'sd')
         call s:iterfuncs.csshow.finish(gd.sd)
     endif
-    let bvar=a:4
+    let bvar=a:3
     if get(bvar, 'autoaddlog', 0) && r!=-1
         let bvar.gd=gd
     endif
     let opts.ignorefiles={}
     call map(copy(ignorefiles), 'extend(opts.ignorefiles, {v:val : 1})')
     unlet ignorefiles
-    "▶2 Get cslist
-    let csiterfuncsname=((has_key(opts, 'revision'))?
-                \          ('ancestors'):
-                \       ((has_key(opts, 'revrange'))?
-                \          ('revrange')
-                \       :
-                \          ('changesets')))
-    let csiterfuncs=a:repo.iterfuncs[csiterfuncsname]
     "▶2 Get template
     call s:_r.template.gettemplatelist(bvar)
     let [opts.reqs, opts.templatefunc]=s:_r.template.compile(bvar.templatelist,
                     \           s:_f.getoption('autoaddlog')) && a:opts.limit
     endif
     let bvar.cw=s:_f.getoption('closelogwindow')
-    let text=s:iterfuncs.glog.iterate(a:repo, opts, csiterfuncs, bvar, a:read)
+    let text=s:iterfuncs.glog.iterate(a:repo, opts, bvar, a:read)
     if a:read
         call s:_r.lineutils.setlines(text, a:read)
     elseif bufnr('%')==buf

plugin/aurum/record.vim

                 \                 '@aurum/commit': '1.0',
                 \               '@aurum/cmdutils': '1.0',
                 \              '@aurum/lineutils': '0.0',
-                \                   '@aurum/repo': '3.0',
+                \                   '@aurum/repo': '4.0',
                 \                   '@aurum/edit': '1.0',
                 \                     '@/options': '0.0',}, 0)
     call FraworLoad('@/commands')

plugin/aurum/repo.vim

 "▶1
 scriptencoding utf-8
 if !exists('s:_pluginloaded')
-    execute frawor#Setup('3.1', {'@/resources': '0.0',
+    execute frawor#Setup('4.0', {'@/resources': '0.0',
                 \                       '@/os': '0.0',
                 \                  '@/options': '0.0',
                 \           '@aurum/lineutils': '0.0',
     finish
 elseif s:_pluginloaded
     finish
+elseif !exists('s:_loading')
+    call FraworLoad(s:_frawor.id)
+    finish
 endif
 let s:drivers={}
 let s:repos={}
 "▶1 iterfuncs: cs generators
 " startfunc (here)  :: repo, opts → d
 "▶2 ancestors
-let s:iterfuncs.ancestors={}
-function s:iterfuncs.ancestors.start(repo, opts)
+function s:iterfuncs.ancestors(repo, opts)
     let cs=a:repo.functions.getcs(a:repo,
                 \a:repo.functions.getrevhex(a:repo, a:opts.revision))
     let indegree={cs.hex : 1}
     endfor
     return {'addrevs': [cs], 'revisions': {}, 'repo': a:repo,
                 \'hasrevisions': get(a:repo, 'hasrevisions', 1),
-                \'indegree': indegree, 'incremented': {cs.hex : 1}}
+                \'indegree': indegree, 'incremented': {cs.hex : 1},
+                \'next': s:F.ancestorsnext}
 endfunction
 function! s:RevCmp(cs1, cs2)
     let rev1=a:cs1.rev
     return ((rev1==rev2)?(0):((rev1<rev2)?(1):(-1)))
 endfunction
 let s:_functions+=['s:RevCmp']
-function s:iterfuncs.ancestors.next(d)
-    if empty(a:d.addrevs)
+function s:F.ancestorsnext()
+    if empty(self.addrevs)
         return 0
     endif
     " XXX cs variables should be kept after cycle ends
     let i=-1
-    for cs in a:d.addrevs
+    for cs in self.addrevs
         let i+=1
-        if a:d.indegree[cs.hex]<=1
+        if self.indegree[cs.hex]<=1
             break
         endif
     endfor
-    call remove(a:d.addrevs, i)
-    let a:d.revisions[cs.hex]=cs
-    for parenthex in filter(copy(cs.parents), '!has_key(a:d.revisions, v:val)')
-        let parent=a:d.repo.functions.getcs(a:d.repo, parenthex)
-        let a:d.indegree[parenthex]=a:d.indegree[parenthex]-1
-        if !has_key(a:d.incremented, parenthex)
-            let a:d.incremented[parenthex]=1
+    call remove(self.addrevs, i)
+    let self.revisions[cs.hex]=cs
+    for parenthex in filter(copy(cs.parents), '!has_key(self.revisions, v:val)')
+        let parent=self.repo.functions.getcs(self.repo, parenthex)
+        let self.indegree[parenthex]=self.indegree[parenthex]-1
+        if !has_key(self.incremented, parenthex)
+            let self.incremented[parenthex]=1
             for pparhex in parent.parents
-                let a:d.indegree[pparhex]=get(a:d.indegree, pparhex, 1)+1
+                let self.indegree[pparhex]=get(self.indegree, pparhex, 1)+1
             endfor
-            let a:d.addrevs+=[parent]
+            let self.addrevs+=[parent]
         endif
     endfor
     return cs
 endfunction
 "▶2 revrange
-let s:iterfuncs.revrange={}
-function s:iterfuncs.revrange.start(repo, opts)
+function s:iterfuncs.revrange(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,
     else
         call reverse(cslist)
     endif
-    return {'cslist': cslist}
+    return {'cslist': cslist, 'next': s:F.revrangenext}
 endfunction
-function s:iterfuncs.revrange.next(d)
-    if empty(a:d.cslist)
+function s:F.revrangenext()
+    if empty(self.cslist)
         return 0
     endif
-    return remove(a:d.cslist, 0)
+    return remove(self.cslist, 0)
 endfunction
 "▶2 changesets
 let s:iterfuncs.changesets=s:iterfuncs.revrange
 endfunction
 "▶1 updaterepo :: repo → repo + repo
 function s:F.updaterepo(repo)
-    if !empty(a:repo.cslist)
-        call a:repo.functions.updatechangesets(a:repo)
-    endif
+    call a:repo.functions.updatechangesets(a:repo)
     return a:repo
 endfunction
 "▶1 getrepo :: path → Maybe repo
                     \       'branch', 'time', 'user', 'description']
     endif
     lockvar! repo
-    unlockvar! repo.cslist
+    unlockvar! repo.mutable
     unlockvar! repo.changesets
     unlockvar 1 repo
     let s:repos[path]=repo

plugin/aurum/status.vim

     execute frawor#Setup('1.1', {'@/resources': '0.0',
                 \            '@aurum/cmdutils': '1.0',
                 \                      '@/fwc': '0.2',
-                \                '@aurum/repo': '3.0',
+                \                '@aurum/repo': '4.0',
                 \                '@aurum/edit': '1.0',
                 \                 '@/commands': '0.0',
                 \                  '@/options': '0.0',
     vim_repo={'has_octopus_merges': 0,
                    'requires_sort': 0,
                       'changesets': {},
-                          'cslist': [],
+                         'mutable': {'cslist': []},
                            'local': 1 if repo.local() else 0,
                       'labeltypes': ['tag', 'bookmark'],
              }