Changeset 614 in tailor


Ignore:
Timestamp:
08/17/05 19:49:32 (8 years ago)
Author:
lele@…
Hash name:
20050817174932-97f81-ed13a41218cda9683aafa49551b6432786b3e1a1
Message:

Make the state file an interator, and use a journal to avoid rewrite

Location:
vcpx
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • vcpx/changes.py

    r520 r614  
    117117        self.entries = entries or [] 
    118118        self.unidiff = None        # This is the unidiff of the whole changeset 
     119 
     120    def __eq__(self, other): 
     121        return (self.revision == other.revision and 
     122                self.date == other.date and 
     123                self.author == other.author) 
     124 
     125    def __ne__(self, other): 
     126        return (self.revision <> other.revision or 
     127                self.date <> other.date or 
     128                self.author <> other.author) 
    119129 
    120130    def addEntry(self, entry, revision): 
  • vcpx/cvsps.py

    r594 r614  
    285285 
    286286        if timestamp == 'INITIAL': 
    287             timestamp = csets[0].date.isoformat(sep=' ') 
     287            cset = csets.next() 
     288            timestamp = cset.date.isoformat(sep=' ') 
     289        else: 
     290            cset = None 
    288291 
    289292        if not exists(join(self.basedir, 'CVS')): 
     
    320323 
    321324        found = False 
    322         while csets: 
    323             cset = csets.pop(0) 
     325        if cset is None: 
     326            try: 
     327                cset = csets.next() 
     328            except StopIteration: 
     329                cset = None 
     330        while cset is not None: 
    324331            for m in cset.entries: 
    325332                info = entries.getFileInfo(m.name) 
     
    333340                last = cset 
    334341                break 
     342 
     343            try: 
     344                cset = csets.next() 
     345            except StopIteration: 
     346                cset = None 
    335347 
    336348        if not found: 
  • vcpx/source.py

    r579 r614  
    8383        conflicts = [] 
    8484 
    85         if not self.pending: 
    86             return last, conflicts 
    87  
    8885        try: 
    89             while self.pending: 
    90                 c = self.pending[0] 
    91  
     86            for c in self.state_file: 
    9287                # Give the opportunity to subclasses to stop the application 
    9388                # of the queue, before the application of the patch by the 
     
    123118                        replay(c) 
    124119 
    125                 # Remove it from the queue and remember it for the finally 
    126                 # clause 
    127                 last = self.pending.pop(0) 
     120                # Remember it for the finally clause and notify the state 
     121                # file so that it gets removed from the queue 
     122                last = c 
     123                self.state_file.applied() 
    128124 
    129125                # Another hook (last==c here) 
     
    132128        finally: 
    133129            # For whatever reason we exit the loop, save the last state 
    134             if last is not None: 
    135                 self.state_file.write(last.revision, self.pending) 
     130            self.state_file.finalize() 
    136131 
    137132        return last, conflicts 
     
    181176        """ 
    182177        Load the pending changesets from the state file, or query the 
    183         upstream repository if there's none. 
    184         """ 
    185  
    186         revision, self.pending = self.state_file.load() 
    187         if not self.pending: 
    188             self.pending = self._getUpstreamChangesets(revision or sincerev) 
    189         return self.pending 
     178        upstream repository if there's none. Return an iterator over 
     179        pending changesets. 
     180        """ 
     181 
     182        pending = len(self.state_file) 
     183        if pending == 0: 
     184            last = self.state_file.lastAppliedChangeset() 
     185            if last: 
     186                revision = last.revision 
     187            else: 
     188                revision = sincerev 
     189            changesets = self._getUpstreamChangesets(revision) 
     190            self.state_file.setPendingChangesets(changesets) 
     191        return self.state_file 
    190192 
    191193    def _getUpstreamChangesets(self, sincerev): 
     
    219221 
    220222        last = self._checkoutUpstreamRevision(revision) 
    221         self.state_file.write(last.revision, getattr(self, 'pending', None)) 
     223        self.state_file.applied(last) 
     224        # Some backend may have already fetched the remaining history 
     225        pending = getattr(self, 'pending', None) 
     226        if pending: 
     227            self.state_file.setPendingChangesets(pending) 
    222228        return last 
    223229 
  • vcpx/tests/__init__.py

    r524 r614  
    1212from svn import * 
    1313from config import * 
     14from statefile import * 
    1415from tailor import * 
    1516 
  • vcpx/project.py

    r603 r614  
    1414__docformat__ = 'reStructuredText' 
    1515 
     16from cPickle import load, dump 
     17 
    1618class StateFile(object): 
    1719    """ 
    1820    State file that stores current revision and pending changesets. 
     21 
     22    It behaves as an iterator, and source backends loop over not yet 
     23    applied changesets, calling .applied() after each one: that writes 
     24    the applied changeset in a `journal` file, much more atomic than 
     25    rewriting the whole archive each time. 
     26 
     27    When the source backend finishes it's job, either because there 
     28    are no more pending changeset or stopped by an error, it calls 
     29    .finalize(), that in presence of a journal file adjust the 
     30    archive filtering out already applied changesets. 
     31 
     32    Should an hard error prevent .finalize() call, it will happen 
     33    automatically next time the state file is loaded. 
    1934    """ 
     35 
    2036    def __init__(self, fname, config): 
    2137        self.filename = fname 
     38        self.archive = None 
     39        self.last_applied = None 
     40        self.current = None 
     41        self.queuelen = 0 
     42 
     43    def _load(self): 
     44        """ 
     45        Open the pickle file and load the first two items, respectively 
     46        the revision and the number of pending changesets. 
     47        """ 
     48 
     49        # Take care of the journal file, if present. 
     50        self.finalize() 
     51 
     52        self.current = None 
     53        try: 
     54            self.archive = open(self.filename) 
     55            self.last_applied = load(self.archive) 
     56            self.queuelen = load(self.archive) 
     57        except IOError: 
     58            self.archive = None 
     59            self.last_applied = None 
     60            self.queuelen = 0 
     61 
     62    def _write(self, changesets): 
     63        """ 
     64        Write the state file. 
     65        """ 
     66 
     67        sf = open(self.filename, 'w') 
     68        dump(self.last_applied, sf) 
     69        dump(len(changesets), sf) 
     70        for cs in changesets: 
     71            dump(cs, sf) 
     72        sf.close() 
    2273 
    2374    def __str__(self): 
    2475        return self.filename 
    2576 
    26     def load(self): 
    27         """ 
    28         Read the source revision and pending changesets from the state file. 
    29         """ 
    30  
    31         from cPickle import load 
    32  
    33         try: 
    34             sf = open(self.filename) 
    35             revision, changesets = load(sf) 
     77    def __iter__(self): 
     78        return self 
     79 
     80    def next(self): 
     81        if not self.archive: 
     82            raise StopIteration 
     83        try: 
     84            self.current = load(self.archive) 
     85        except EOFError: 
     86            self.archive.close() 
     87            self.archive = None 
     88            raise StopIteration 
     89        self.queuelen -= 1 
     90        return self.current 
     91 
     92    def __len__(self): 
     93        if self.archive is None: 
     94            self._load() 
     95        return self.queuelen 
     96 
     97    def applied(self, current=None): 
     98        """ 
     99        Write the applied changeset to the journal file. 
     100        """ 
     101 
     102        self.last_applied = current or self.current 
     103        journal = open(self.filename + '.journal', 'w') 
     104        dump(self.last_applied, journal) 
     105        journal.close() 
     106 
     107    def finalize(self): 
     108        """ 
     109        If there is a journal file, adjust the archive accordingly, 
     110        dropping already applied changesets. 
     111        """ 
     112 
     113        from os.path import exists 
     114        from os import unlink, rename 
     115 
     116        if self.archive is not None: 
     117            self.archive.close() 
     118            self.archive = None 
     119 
     120        if not exists(self.filename + '.journal'): 
     121            return 
     122 
     123        # Load last applied changeset from the journal 
     124        journal = open(self.filename + '.journal') 
     125        last_applied = load(journal) 
     126        journal.close() 
     127 
     128        # If there is an actual archive (ie, this is not bootstrap time) 
     129        # load the changesets from there, skipping the changesets until 
     130        # the last_applied one, then transfer the remaining to the new 
     131        # archive. 
     132        if exists(self.filename): 
     133            old = open(self.filename) 
     134            load(old) # last applied 
     135            queuelen = load(old) 
     136            cs = load(old) 
     137 
     138            # Skip already applied changesets 
     139            while cs <> last_applied: 
     140                queuelen -= 1 
     141                cs = load(old) 
     142 
     143            sf = open(self.filename + '.new', 'w') 
     144            dump(last_applied, sf) 
     145            dump(queuelen-1, sf) 
     146 
     147            while True: 
     148                try: 
     149                    cs = load(old) 
     150                except EOFError: 
     151                    break 
     152                dump(cs, sf) 
    36153            sf.close() 
    37         except IOError: 
    38             revision = None 
    39             changesets = None 
    40  
    41         return revision, changesets 
    42  
    43     def write(self, revision, changesets): 
    44         """ 
    45         Write current source revision and pending changesets in the state file. 
    46         """ 
    47  
    48         from cPickle import dump 
    49  
    50         sf = open(self.filename, 'w') 
    51         dump((revision, changesets), sf) 
    52         sf.close() 
     154            old.close() 
     155            unlink(self.filename) 
     156            rename(sf.name, self.filename) 
     157        else: 
     158            sf = open(self.filename, 'w') 
     159            dump(last_applied, sf) 
     160            dump(0, sf) 
     161            sf.close() 
     162 
     163        unlink(journal.name) 
     164 
     165    def lastAppliedChangeset(self): 
     166        """ 
     167        Return the last applied changeset, if any, None otherwise. 
     168        """ 
     169 
     170        if self.archive is None: 
     171            self._load() 
     172        return self.last_applied 
     173 
     174    def setPendingChangesets(self, changesets): 
     175        """ 
     176        Write pending changesets to the state file. 
     177        """ 
     178 
     179        if self.archive is not None: 
     180            self.archive.close() 
     181            self.archive = None 
     182 
     183        self._write(changesets) 
     184        self._load() 
    53185 
    54186 
     
    179311        """ 
    180312        Return True if the project exists, False otherwise. 
    181  
    182         Check for the existence of the state file to decide. 
    183         """ 
    184  
    185         from os.path import exists 
    186  
    187         return exists(self.state_file.filename) 
     313        """ 
     314 
     315        return self.state_file.lastAppliedChangeset() is not None 
    188316 
    189317    def workingDir(self): 
Note: See TracChangeset for help on using the changeset viewer.