mercurial/bundlerepo.py
changeset 12332 680fe77ab5b8
parent 12331 40935b59518b
child 12333 44c7dfc2f6a3
equal deleted inserted replaced
12331:40935b59518b 12332:680fe77ab5b8
    16 import os, struct, tempfile, shutil
    16 import os, struct, tempfile, shutil
    17 import changegroup, util, mdiff
    17 import changegroup, util, mdiff
    18 import localrepo, changelog, manifest, filelog, revlog, error
    18 import localrepo, changelog, manifest, filelog, revlog, error
    19 
    19 
    20 class bundlerevlog(revlog.revlog):
    20 class bundlerevlog(revlog.revlog):
    21     def __init__(self, opener, indexfile, bundlefile,
    21     def __init__(self, opener, indexfile, bundle,
    22                  linkmapper=None):
    22                  linkmapper=None):
    23         # How it works:
    23         # How it works:
    24         # to retrieve a revision, we need to know the offset of
    24         # to retrieve a revision, we need to know the offset of
    25         # the revision in the bundlefile (an opened file).
    25         # the revision in the bundle (an unbundle object).
    26         #
    26         #
    27         # We store this offset in the index (start), to differentiate a
    27         # We store this offset in the index (start), to differentiate a
    28         # rev in the bundle and from a rev in the revlog, we check
    28         # rev in the bundle and from a rev in the revlog, we check
    29         # len(index[r]). If the tuple is bigger than 7, it is a bundle
    29         # len(index[r]). If the tuple is bigger than 7, it is a bundle
    30         # (it is bigger since we store the node to which the delta is)
    30         # (it is bigger since we store the node to which the delta is)
    31         #
    31         #
    32         revlog.revlog.__init__(self, opener, indexfile)
    32         revlog.revlog.__init__(self, opener, indexfile)
    33         self.bundlefile = bundlefile
    33         self.bundle = bundle
    34         self.basemap = {}
    34         self.basemap = {}
    35         def chunkpositer():
    35         def chunkpositer():
    36             for chunk in changegroup.chunkiter(bundlefile):
    36             for chunk in changegroup.chunkiter(bundle):
    37                 pos = bundlefile.tell()
    37                 pos = bundle.tell()
    38                 yield chunk, pos - len(chunk)
    38                 yield chunk, pos - len(chunk)
    39         n = len(self)
    39         n = len(self)
    40         prev = None
    40         prev = None
    41         for chunk, start in chunkpositer():
    41         for chunk, start in chunkpositer():
    42             size = len(chunk)
    42             size = len(chunk)
    66             self.index.insert(-1, e)
    66             self.index.insert(-1, e)
    67             self.nodemap[node] = n
    67             self.nodemap[node] = n
    68             prev = node
    68             prev = node
    69             n += 1
    69             n += 1
    70 
    70 
    71     def bundle(self, rev):
    71     def inbundle(self, rev):
    72         """is rev from the bundle"""
    72         """is rev from the bundle"""
    73         if rev < 0:
    73         if rev < 0:
    74             return False
    74             return False
    75         return rev in self.basemap
    75         return rev in self.basemap
    76     def bundlebase(self, rev):
    76     def bundlebase(self, rev):
    77         return self.basemap[rev]
    77         return self.basemap[rev]
    78     def _chunk(self, rev):
    78     def _chunk(self, rev):
    79         # Warning: in case of bundle, the diff is against bundlebase,
    79         # Warning: in case of bundle, the diff is against bundlebase,
    80         # not against rev - 1
    80         # not against rev - 1
    81         # XXX: could use some caching
    81         # XXX: could use some caching
    82         if not self.bundle(rev):
    82         if not self.inbundle(rev):
    83             return revlog.revlog._chunk(self, rev)
    83             return revlog.revlog._chunk(self, rev)
    84         self.bundlefile.seek(self.start(rev))
    84         self.bundle.seek(self.start(rev))
    85         return self.bundlefile.read(self.length(rev))
    85         return self.bundle.read(self.length(rev))
    86 
    86 
    87     def revdiff(self, rev1, rev2):
    87     def revdiff(self, rev1, rev2):
    88         """return or calculate a delta between two revisions"""
    88         """return or calculate a delta between two revisions"""
    89         if self.bundle(rev1) and self.bundle(rev2):
    89         if self.inbundle(rev1) and self.inbundle(rev2):
    90             # hot path for bundle
    90             # hot path for bundle
    91             revb = self.rev(self.bundlebase(rev2))
    91             revb = self.rev(self.bundlebase(rev2))
    92             if revb == rev1:
    92             if revb == rev1:
    93                 return self._chunk(rev2)
    93                 return self._chunk(rev2)
    94         elif not self.bundle(rev1) and not self.bundle(rev2):
    94         elif not self.inbundle(rev1) and not self.inbundle(rev2):
    95             return revlog.revlog.revdiff(self, rev1, rev2)
    95             return revlog.revlog.revdiff(self, rev1, rev2)
    96 
    96 
    97         return mdiff.textdiff(self.revision(self.node(rev1)),
    97         return mdiff.textdiff(self.revision(self.node(rev1)),
    98                          self.revision(self.node(rev2)))
    98                          self.revision(self.node(rev2)))
    99 
    99 
   105         text = None
   105         text = None
   106         chain = []
   106         chain = []
   107         iter_node = node
   107         iter_node = node
   108         rev = self.rev(iter_node)
   108         rev = self.rev(iter_node)
   109         # reconstruct the revision if it is from a changegroup
   109         # reconstruct the revision if it is from a changegroup
   110         while self.bundle(rev):
   110         while self.inbundle(rev):
   111             if self._cache and self._cache[0] == iter_node:
   111             if self._cache and self._cache[0] == iter_node:
   112                 text = self._cache[2]
   112                 text = self._cache[2]
   113                 break
   113                 break
   114             chain.append(rev)
   114             chain.append(rev)
   115             iter_node = self.bundlebase(rev)
   115             iter_node = self.bundlebase(rev)
   137         raise NotImplementedError
   137         raise NotImplementedError
   138     def checksize(self):
   138     def checksize(self):
   139         raise NotImplementedError
   139         raise NotImplementedError
   140 
   140 
   141 class bundlechangelog(bundlerevlog, changelog.changelog):
   141 class bundlechangelog(bundlerevlog, changelog.changelog):
   142     def __init__(self, opener, bundlefile):
   142     def __init__(self, opener, bundle):
   143         changelog.changelog.__init__(self, opener)
   143         changelog.changelog.__init__(self, opener)
   144         bundlerevlog.__init__(self, opener, self.indexfile, bundlefile)
   144         bundlerevlog.__init__(self, opener, self.indexfile, bundle)
   145 
   145 
   146 class bundlemanifest(bundlerevlog, manifest.manifest):
   146 class bundlemanifest(bundlerevlog, manifest.manifest):
   147     def __init__(self, opener, bundlefile, linkmapper):
   147     def __init__(self, opener, bundle, linkmapper):
   148         manifest.manifest.__init__(self, opener)
   148         manifest.manifest.__init__(self, opener)
   149         bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
   149         bundlerevlog.__init__(self, opener, self.indexfile, bundle,
   150                               linkmapper)
   150                               linkmapper)
   151 
   151 
   152 class bundlefilelog(bundlerevlog, filelog.filelog):
   152 class bundlefilelog(bundlerevlog, filelog.filelog):
   153     def __init__(self, opener, path, bundlefile, linkmapper):
   153     def __init__(self, opener, path, bundle, linkmapper):
   154         filelog.filelog.__init__(self, opener, path)
   154         filelog.filelog.__init__(self, opener, path)
   155         bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
   155         bundlerevlog.__init__(self, opener, self.indexfile, bundle,
   156                               linkmapper)
   156                               linkmapper)
   157 
   157 
   158 class bundlerepository(localrepo.localrepository):
   158 class bundlerepository(localrepo.localrepository):
   159     def __init__(self, ui, path, bundlename):
   159     def __init__(self, ui, path, bundlename):
   160         self._tempparent = None
   160         self._tempparent = None
   169             self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
   169             self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
   170         else:
   170         else:
   171             self._url = 'bundle:' + bundlename
   171             self._url = 'bundle:' + bundlename
   172 
   172 
   173         self.tempfile = None
   173         self.tempfile = None
   174         self.bundlefile = open(bundlename, "rb")
   174         f = open(bundlename, "rb")
   175         b = changegroup.readbundle(self.bundlefile, bundlename)
   175         self.bundle = changegroup.readbundle(f, bundlename)
   176         if b.compressed():
   176         if self.bundle.compressed():
   177             # we need a seekable, decompressed bundle
   177             # we need a seekable, decompressed bundle
   178             fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
   178             fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
   179                                             suffix=".hg10un", dir=self.path)
   179                                             suffix=".hg10un", dir=self.path)
   180             self.tempfile = temp
   180             self.tempfile = temp
   181             fptemp = os.fdopen(fdtemp, 'wb')
   181             fptemp = os.fdopen(fdtemp, 'wb')
   182 
   182 
   183             try:
   183             try:
   184                 fptemp.write("HG10UN")
   184                 fptemp.write("HG10UN")
   185                 while 1:
   185                 while 1:
   186                     chunk = b.read(2**18)
   186                     chunk = self.bundle.read(2**18)
   187                     if not chunk:
   187                     if not chunk:
   188                         break
   188                         break
   189                     fptemp.write(chunk)
   189                     fptemp.write(chunk)
   190             finally:
   190             finally:
   191                 fptemp.close()
   191                 fptemp.close()
   192                 self.bundlefile.close()
   192 
   193 
   193             f = open(self.tempfile, "rb")
   194             self.bundlefile = open(self.tempfile, "rb")
   194             self.bundle = changegroup.readbundle(f, bundlename)
   195             self.bundlefile.seek(6)
       
   196 
   195 
   197         # dict with the mapping 'filename' -> position in the bundle
   196         # dict with the mapping 'filename' -> position in the bundle
   198         self.bundlefilespos = {}
   197         self.bundlefilespos = {}
   199 
   198 
   200     @util.propertycache
   199     @util.propertycache
   201     def changelog(self):
   200     def changelog(self):
   202         c = bundlechangelog(self.sopener, self.bundlefile)
   201         c = bundlechangelog(self.sopener, self.bundle)
   203         self.manstart = self.bundlefile.tell()
   202         self.manstart = self.bundle.tell()
   204         return c
   203         return c
   205 
   204 
   206     @util.propertycache
   205     @util.propertycache
   207     def manifest(self):
   206     def manifest(self):
   208         self.bundlefile.seek(self.manstart)
   207         self.bundle.seek(self.manstart)
   209         m = bundlemanifest(self.sopener, self.bundlefile, self.changelog.rev)
   208         m = bundlemanifest(self.sopener, self.bundle, self.changelog.rev)
   210         self.filestart = self.bundlefile.tell()
   209         self.filestart = self.bundle.tell()
   211         return m
   210         return m
   212 
   211 
   213     @util.propertycache
   212     @util.propertycache
   214     def manstart(self):
   213     def manstart(self):
   215         self.changelog
   214         self.changelog
   223     def url(self):
   222     def url(self):
   224         return self._url
   223         return self._url
   225 
   224 
   226     def file(self, f):
   225     def file(self, f):
   227         if not self.bundlefilespos:
   226         if not self.bundlefilespos:
   228             self.bundlefile.seek(self.filestart)
   227             self.bundle.seek(self.filestart)
   229             while 1:
   228             while 1:
   230                 chunk = changegroup.getchunk(self.bundlefile)
   229                 chunk = changegroup.getchunk(self.bundle)
   231                 if not chunk:
   230                 if not chunk:
   232                     break
   231                     break
   233                 self.bundlefilespos[chunk] = self.bundlefile.tell()
   232                 self.bundlefilespos[chunk] = self.bundle.tell()
   234                 for c in changegroup.chunkiter(self.bundlefile):
   233                 for c in changegroup.chunkiter(self.bundle):
   235                     pass
   234                     pass
   236 
   235 
   237         if f[0] == '/':
   236         if f[0] == '/':
   238             f = f[1:]
   237             f = f[1:]
   239         if f in self.bundlefilespos:
   238         if f in self.bundlefilespos:
   240             self.bundlefile.seek(self.bundlefilespos[f])
   239             self.bundle.seek(self.bundlefilespos[f])
   241             return bundlefilelog(self.sopener, f, self.bundlefile,
   240             return bundlefilelog(self.sopener, f, self.bundle,
   242                                  self.changelog.rev)
   241                                  self.changelog.rev)
   243         else:
   242         else:
   244             return filelog.filelog(self.sopener, f)
   243             return filelog.filelog(self.sopener, f)
   245 
   244 
   246     def __del__(self):
   245     def __del__(self):
   247         bundlefile = getattr(self, 'bundlefile', None)
   246         del self.bundle
   248         if bundlefile and not bundlefile.closed:
       
   249             bundlefile.close()
       
   250         tempfile = getattr(self, 'tempfile', None)
       
   251         if tempfile is not None:
   247         if tempfile is not None:
   252             os.unlink(tempfile)
   248             os.unlink(tempfile)
   253         if self._tempparent:
   249         if self._tempparent:
   254             shutil.rmtree(self._tempparent, True)
   250             shutil.rmtree(self._tempparent, True)
   255 
   251