def revchunk(self, revlog, rev, prev, linknode): node = revlog.node(rev) p1, p2 = revlog.parentrevs(rev) base = self.deltaparent(revlog, rev, p1, p2, prev) prefix = '' if revlog.iscensored(base) or revlog.iscensored(rev): try: delta = revlog.revision(node) except error.CensoredNodeError as e: delta = e.tombstone if base == nullrev: prefix = mdiff.trivialdiffheader(len(delta)) else: baselen = revlog.rawsize(base) prefix = mdiff.replacediffheader(baselen, len(delta)) elif base == nullrev: delta = revlog.revision(node) prefix = mdiff.trivialdiffheader(len(delta)) else: delta = revlog.revdiff(base, rev) p1n, p2n = revlog.parents(node) basenode = revlog.node(base) meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode) meta += prefix l = len(meta) + len(delta) yield chunkheader(l) yield meta yield delta
def group(self, nodelist, lookup, infocollect=None, fullrev=False): """Calculate a delta group, yielding a sequence of changegroup chunks (strings). Given a list of changeset revs, return a set of deltas and metadata corresponding to nodes. The first delta is first parent(nodelist[0]) -> nodelist[0], the receiver is guaranteed to have this parent as it has all history before these changesets. In the case firstparent is nullrev the changegroup starts with a full revision. fullrev forces the insertion of the full revision, necessary in the case of shallow clones where the first parent might not exist at the reciever. """ revs = [self.rev(n) for n in nodelist] # if we don't have any revisions touched by these changesets, bail if not revs: yield changegroup.closechunk() return # add the parent of the first rev p = self.parentrevs(revs[0])[0] revs.insert(0, p) if p == nullrev: fullrev = True # build deltas for d in xrange(len(revs) - 1): a, b = revs[d], revs[d + 1] nb = self.node(b) if infocollect is not None: infocollect(nb) p = self.parents(nb) meta = nb + p[0] + p[1] + lookup(nb) if fullrev: d = self.revision(nb) meta += mdiff.trivialdiffheader(len(d)) fullrev = False else: d = self.revdiff(a, b) yield changegroup.chunkheader(len(meta) + len(d)) yield meta yield d yield changegroup.closechunk()
def group(self, nodelist, lookup, infocollect=None): """Calculate a delta group, yielding a sequence of changegroup chunks (strings). Given a list of changeset revs, return a set of deltas and metadata corresponding to nodes. the first delta is parent(nodes[0]) -> nodes[0] the receiver is guaranteed to have this parent as it has all history before these changesets. parent is parent[0] """ revs = [self.rev(n) for n in nodelist] # if we don't have any revisions touched by these changesets, bail if not revs: yield changegroup.closechunk() return # add the parent of the first rev p = self.parentrevs(revs[0])[0] revs.insert(0, p) # build deltas for d in xrange(len(revs) - 1): a, b = revs[d], revs[d + 1] nb = self.node(b) if infocollect is not None: infocollect(nb) p = self.parents(nb) meta = nb + p[0] + p[1] + lookup(nb) if a == -1: d = self.revision(nb) meta += mdiff.trivialdiffheader(len(d)) else: d = self.revdiff(a, b) yield changegroup.chunkheader(len(meta) + len(d)) yield meta if len(d) > 2**20: pos = 0 while pos < len(d): pos2 = pos + 2 ** 18 yield d[pos:pos2] pos = pos2 else: yield d yield changegroup.closechunk()
def group(self, nodelist, lookup, infocollect=None): """Calculate a delta group, yielding a sequence of changegroup chunks (strings). Given a list of changeset revs, return a set of deltas and metadata corresponding to nodes. the first delta is parent(nodes[0]) -> nodes[0] the receiver is guaranteed to have this parent as it has all history before these changesets. parent is parent[0] """ revs = [self.rev(n) for n in nodelist] # if we don't have any revisions touched by these changesets, bail if not revs: yield changegroup.closechunk() return # add the parent of the first rev p = self.parentrevs(revs[0])[0] revs.insert(0, p) # build deltas for d in xrange(len(revs) - 1): a, b = revs[d], revs[d + 1] nb = self.node(b) if infocollect is not None: infocollect(nb) p = self.parents(nb) meta = nb + p[0] + p[1] + lookup(nb) if a == -1: d = self.revision(nb) meta += mdiff.trivialdiffheader(len(d)) else: d = self.revdiff(a, b) yield changegroup.chunkheader(len(meta) + len(d)) yield meta if len(d) > 2**20: pos = 0 while pos < len(d): pos2 = pos + 2**18 yield d[pos:pos2] pos = pos2 else: yield d yield changegroup.closechunk()
def revchunk(self, revlog, rev, prev, linknode): node = revlog.node(rev) p1, p2 = revlog.parentrevs(rev) base = self.deltaparent(revlog, rev, p1, p2, prev) prefix = '' if revlog.iscensored(base) or revlog.iscensored(rev): try: delta = revlog.revision(node) except error.CensoredNodeError, e: delta = e.tombstone if base == nullrev: prefix = mdiff.trivialdiffheader(len(delta)) else: baselen = revlog.rawsize(base) prefix = mdiff.replacediffheader(baselen, len(delta))
def revchunk(self, revlog, rev, prev, linknode): node = revlog.node(rev) p1, p2 = revlog.parentrevs(rev) base = prev prefix = '' if base == nullrev: delta = revlog.revision(node) prefix = mdiff.trivialdiffheader(len(delta)) else: delta = revlog.revdiff(base, rev) p1n, p2n = revlog.parents(node) basenode = revlog.node(base) meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode) meta += prefix l = len(meta) + len(delta) yield chunkheader(l) yield meta yield delta
class cg1packer(object): deltaheader = _CHANGEGROUPV1_DELTA_HEADER version = '01' def __init__(self, repo, bundlecaps=None): """Given a source repo, construct a bundler. bundlecaps is optional and can be used to specify the set of capabilities which can be used to build the bundle. """ # Set of capabilities we can use to build the bundle. if bundlecaps is None: bundlecaps = set() self._bundlecaps = bundlecaps self._changelog = repo.changelog self._manifest = repo.manifest reorder = repo.ui.config('bundle', 'reorder', 'auto') if reorder == 'auto': reorder = None else: reorder = util.parsebool(reorder) self._repo = repo self._reorder = reorder self._progress = repo.ui.progress if self._repo.ui.verbose and not self._repo.ui.debugflag: self._verbosenote = self._repo.ui.note else: self._verbosenote = lambda s: None def close(self): return closechunk() def fileheader(self, fname): return chunkheader(len(fname)) + fname def group(self, nodelist, revlog, lookup, units=None, reorder=None): """Calculate a delta group, yielding a sequence of changegroup chunks (strings). Given a list of changeset revs, return a set of deltas and metadata corresponding to nodes. The first delta is first parent(nodelist[0]) -> nodelist[0], the receiver is guaranteed to have this parent as it has all history before these changesets. In the case firstparent is nullrev the changegroup starts with a full revision. If units is not None, progress detail will be generated, units specifies the type of revlog that is touched (changelog, manifest, etc.). """ # if we don't have any revisions touched by these changesets, bail if len(nodelist) == 0: yield self.close() return # for generaldelta revlogs, we linearize the revs; this will both be # much quicker and generate a much smaller bundle if (revlog._generaldelta and reorder is not False) or reorder: dag = dagutil.revlogdag(revlog) revs = set(revlog.rev(n) for n in nodelist) revs = dag.linearize(revs) else: revs = sorted([revlog.rev(n) for n in nodelist]) # add the parent of the first rev p = revlog.parentrevs(revs[0])[0] revs.insert(0, p) # build deltas total = len(revs) - 1 msgbundling = _('bundling') for r in xrange(len(revs) - 1): if units is not None: self._progress(msgbundling, r + 1, unit=units, total=total) prev, curr = revs[r], revs[r + 1] linknode = lookup(revlog.node(curr)) for c in self.revchunk(revlog, curr, prev, linknode): yield c yield self.close() # filter any nodes that claim to be part of the known set def prune(self, revlog, missing, commonrevs, source): rr, rl = revlog.rev, revlog.linkrev return [n for n in missing if rl(rr(n)) not in commonrevs] def generate(self, commonrevs, clnodes, fastpathlinkrev, source): '''yield a sequence of changegroup chunks (strings)''' repo = self._repo cl = self._changelog mf = self._manifest reorder = self._reorder progress = self._progress # for progress output msgbundling = _('bundling') clrevorder = {} mfs = {} # needed manifests fnodes = {} # needed file nodes changedfiles = set() # Callback for the changelog, used to collect changed files and manifest # nodes. # Returns the linkrev node (identity in the changelog case). def lookupcl(x): c = cl.read(x) clrevorder[x] = len(clrevorder) changedfiles.update(c[3]) # record the first changeset introducing this manifest version mfs.setdefault(c[0], x) return x self._verbosenote(_('uncompressed size of bundle content:\n')) size = 0 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets'), reorder=reorder): size += len(chunk) yield chunk self._verbosenote(_('%8.i (changelog)\n') % size) progress(msgbundling, None) # Callback for the manifest, used to collect linkrevs for filelog # revisions. # Returns the linkrev node (collected in lookupcl). def lookupmf(x): clnode = mfs[x] if not fastpathlinkrev or reorder: mdata = mf.readfast(x) for f, n in mdata.iteritems(): if f in changedfiles: # record the first changeset introducing this filelog # version fclnodes = fnodes.setdefault(f, {}) fclnode = fclnodes.setdefault(n, clnode) if clrevorder[clnode] < clrevorder[fclnode]: fclnodes[n] = clnode return clnode mfnodes = self.prune(mf, mfs, commonrevs, source) size = 0 for chunk in self.group(mfnodes, mf, lookupmf, units=_('manifests'), reorder=reorder): size += len(chunk) yield chunk self._verbosenote(_('%8.i (manifests)\n') % size) progress(msgbundling, None) mfs.clear() needed = set(cl.rev(x) for x in clnodes) def linknodes(filerevlog, fname): if fastpathlinkrev and not reorder: llr = filerevlog.linkrev def genfilenodes(): for r in filerevlog: linkrev = llr(r) if linkrev in needed: yield filerevlog.node(r), cl.node(linkrev) return dict(genfilenodes()) return fnodes.get(fname, {}) for chunk in self.generatefiles(changedfiles, linknodes, commonrevs, source): yield chunk yield self.close() progress(msgbundling, None) if clnodes: repo.hook('outgoing', node=hex(clnodes[0]), source=source) def generatefiles(self, changedfiles, linknodes, commonrevs, source): repo = self._repo progress = self._progress reorder = self._reorder msgbundling = _('bundling') total = len(changedfiles) # for progress output msgfiles = _('files') for i, fname in enumerate(sorted(changedfiles)): filerevlog = repo.file(fname) if not filerevlog: raise util.Abort(_("empty or missing revlog for %s") % fname) linkrevnodes = linknodes(filerevlog, fname) # Lookup for filenodes, we collected the linkrev nodes above in the # fastpath case and with lookupmf in the slowpath case. def lookupfilelog(x): return linkrevnodes[x] filenodes = self.prune(filerevlog, linkrevnodes, commonrevs, source) if filenodes: progress(msgbundling, i + 1, item=fname, unit=msgfiles, total=total) h = self.fileheader(fname) size = len(h) yield h for chunk in self.group(filenodes, filerevlog, lookupfilelog, reorder=reorder): size += len(chunk) yield chunk self._verbosenote(_('%8.i %s\n') % (size, fname)) def deltaparent(self, revlog, rev, p1, p2, prev): return prev def revchunk(self, revlog, rev, prev, linknode): node = revlog.node(rev) p1, p2 = revlog.parentrevs(rev) base = self.deltaparent(revlog, rev, p1, p2, prev) prefix = '' if revlog.iscensored(base) or revlog.iscensored(rev): try: delta = revlog.revision(node) except error.CensoredNodeError, e: delta = e.tombstone if base == nullrev: prefix = mdiff.trivialdiffheader(len(delta)) else: baselen = revlog.rawsize(base) prefix = mdiff.replacediffheader(baselen, len(delta)) elif base == nullrev: delta = revlog.revision(node) prefix = mdiff.trivialdiffheader(len(delta))