Esempio n. 1
0
    def censorrevision(self, tr, censornode, tombstone=b''):
        tombstone = storageutil.packmeta({b'censored': tombstone}, b'')

        # This restriction is cargo culted from revlogs and makes no sense for
        # SQLite, since columns can be resized at will.
        if len(tombstone) > len(self.rawdata(censornode)):
            raise error.Abort(
                _(b'censor tombstone must be no longer than censored data')
            )

        # We need to replace the censored revision's data with the tombstone.
        # But replacing that data will have implications for delta chains that
        # reference it.
        #
        # While "better," more complex strategies are possible, we do something
        # simple: we find delta chain children of the censored revision and we
        # replace those incremental deltas with fulltexts of their corresponding
        # revision. Then we delete the now-unreferenced delta and original
        # revision and insert a replacement.

        # Find the delta to be censored.
        censoreddeltaid = self._db.execute(
            'SELECT deltaid FROM fileindex WHERE id=?',
            (self._revisions[censornode].rid,),
        ).fetchone()[0]

        # Find all its delta chain children.
        # TODO once we support storing deltas for !files, we'll need to look
        # for those delta chains too.
        rows = list(
            self._db.execute(
                'SELECT id, pathid, node FROM fileindex '
                'WHERE deltabaseid=? OR deltaid=?',
                (censoreddeltaid, censoreddeltaid),
            )
        )

        for row in rows:
            rid, pathid, node = row

            fulltext = resolvedeltachain(
                self._db, pathid, node, {}, {-1: None}, zstddctx=self._dctx
            )

            deltahash = hashutil.sha1(fulltext).digest()

            if self._compengine == b'zstd':
                deltablob = self._cctx.compress(fulltext)
                compression = COMPRESSION_ZSTD
            elif self._compengine == b'zlib':
                deltablob = zlib.compress(fulltext)
                compression = COMPRESSION_ZLIB
            elif self._compengine == b'none':
                deltablob = fulltext
                compression = COMPRESSION_NONE
            else:
                raise error.ProgrammingError(
                    b'unhandled compression engine: %s' % self._compengine
                )

            if len(deltablob) >= len(fulltext):
                deltablob = fulltext
                compression = COMPRESSION_NONE

            deltaid = insertdelta(self._db, compression, deltahash, deltablob)

            self._db.execute(
                'UPDATE fileindex SET deltaid=?, deltabaseid=NULL '
                'WHERE id=?',
                (deltaid, rid),
            )

        # Now create the tombstone delta and replace the delta on the censored
        # node.
        deltahash = hashutil.sha1(tombstone).digest()
        tombstonedeltaid = insertdelta(
            self._db, COMPRESSION_NONE, deltahash, tombstone
        )

        flags = self._revisions[censornode].flags
        flags |= FLAG_CENSORED

        self._db.execute(
            'UPDATE fileindex SET flags=?, deltaid=?, deltabaseid=NULL '
            'WHERE pathid=? AND node=?',
            (flags, tombstonedeltaid, self._pathid, censornode),
        )

        self._db.execute('DELETE FROM delta WHERE id=?', (censoreddeltaid,))

        self._refreshindex()
        self._revisioncache.clear()
Esempio n. 2
0
    def _addrawrevision(
        self,
        node,
        revisiondata,
        transaction,
        linkrev,
        p1,
        p2,
        storedelta=None,
        flags=0,
    ):
        if self._pathid is None:
            res = self._db.execute(
                'INSERT INTO filepath (path) VALUES (?)', (self._path,)
            )
            self._pathid = res.lastrowid

        # For simplicity, always store a delta against p1.
        # TODO we need a lot more logic here to make behavior reasonable.

        if storedelta:
            deltabase, delta = storedelta

            if isinstance(deltabase, int):
                deltabase = self.node(deltabase)

        else:
            assert revisiondata is not None
            deltabase = p1

            if deltabase == nullid:
                delta = revisiondata
            else:
                delta = mdiff.textdiff(
                    self.revision(self.rev(deltabase)), revisiondata
                )

        # File index stores a pointer to its delta and the parent delta.
        # The parent delta is stored via a pointer to the fileindex PK.
        if deltabase == nullid:
            baseid = None
        else:
            baseid = self._revisions[deltabase].rid

        # Deltas are stored with a hash of their content. This allows
        # us to de-duplicate. The table is configured to ignore conflicts
        # and it is faster to just insert and silently noop than to look
        # first.
        deltahash = hashutil.sha1(delta).digest()

        if self._compengine == b'zstd':
            deltablob = self._cctx.compress(delta)
            compression = COMPRESSION_ZSTD
        elif self._compengine == b'zlib':
            deltablob = zlib.compress(delta)
            compression = COMPRESSION_ZLIB
        elif self._compengine == b'none':
            deltablob = delta
            compression = COMPRESSION_NONE
        else:
            raise error.ProgrammingError(
                b'unhandled compression engine: %s' % self._compengine
            )

        # Don't store compressed data if it isn't practical.
        if len(deltablob) >= len(delta):
            deltablob = delta
            compression = COMPRESSION_NONE

        deltaid = insertdelta(self._db, compression, deltahash, deltablob)

        rev = len(self)

        if p1 == nullid:
            p1rev = nullrev
        else:
            p1rev = self._nodetorev[p1]

        if p2 == nullid:
            p2rev = nullrev
        else:
            p2rev = self._nodetorev[p2]

        rid = self._db.execute(
            'INSERT INTO fileindex ('
            '    pathid, revnum, node, p1rev, p2rev, linkrev, flags, '
            '    deltaid, deltabaseid) '
            '    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
            (
                self._pathid,
                rev,
                node,
                p1rev,
                p2rev,
                linkrev,
                flags,
                deltaid,
                baseid,
            ),
        ).lastrowid

        entry = revisionentry(
            rid=rid,
            rev=rev,
            node=node,
            p1rev=p1rev,
            p2rev=p2rev,
            p1node=p1,
            p2node=p2,
            linkrev=linkrev,
            flags=flags,
        )

        self._nodetorev[node] = rev
        self._revtonode[rev] = node
        self._revisions[node] = entry

        return node
Esempio n. 3
0
def getlocalkey(file, id):
    pathhash = hex(hashutil.sha1(file).digest())
    return os.path.join(pathhash, id)
Esempio n. 4
0
def getcachekey(reponame, file, id):
    pathhash = hex(hashutil.sha1(file).digest())
    return os.path.join(reponame, pathhash[:2], pathhash[2:], id)
Esempio n. 5
0
def hashdiffopts(diffopts):
    diffoptstr = stringutil.pprint(
        sorted((k, getattr(diffopts, k)) for k in mdiff.diffopts.defaults)
    )
    return hex(hashutil.sha1(diffoptstr).digest())[:6]
Esempio n. 6
0
def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
                            matcher, size, lfiletohash):
    # Convert src parents to dst parents
    parents = _convertparents(ctx, revmap)

    # Generate list of changed files
    files = _getchangedfiles(ctx, parents)

    dstfiles = []
    for f in files:
        if f not in lfiles and f not in normalfiles:
            islfile = _islfile(f, ctx, matcher, size)
            # If this file was renamed or copied then copy
            # the largefile-ness of its predecessor
            if f in ctx.manifest():
                fctx = ctx.filectx(f)
                renamed = fctx.copysource()
                if renamed is None:
                    # the code below assumes renamed to be a boolean or a list
                    # and won't quite work with the value None
                    renamed = False
                renamedlfile = renamed and renamed in lfiles
                islfile |= renamedlfile
                if b'l' in fctx.flags():
                    if renamedlfile:
                        raise error.Abort(
                            _(b'renamed/copied largefile %s becomes symlink') %
                            f)
                    islfile = False
            if islfile:
                lfiles.add(f)
            else:
                normalfiles.add(f)

        if f in lfiles:
            fstandin = lfutil.standin(f)
            dstfiles.append(fstandin)
            # largefile in manifest if it has not been removed/renamed
            if f in ctx.manifest():
                fctx = ctx.filectx(f)
                if b'l' in fctx.flags():
                    renamed = fctx.copysource()
                    if renamed and renamed in lfiles:
                        raise error.Abort(
                            _(b'largefile %s becomes symlink') % f)

                # largefile was modified, update standins
                m = hashutil.sha1(b'')
                m.update(ctx[f].data())
                hash = hex(m.digest())
                if f not in lfiletohash or lfiletohash[f] != hash:
                    rdst.wwrite(f, ctx[f].data(), ctx[f].flags())
                    executable = b'x' in ctx[f].flags()
                    lfutil.writestandin(rdst, fstandin, hash, executable)
                    lfiletohash[f] = hash
        else:
            # normal file
            dstfiles.append(f)

    def getfilectx(repo, memctx, f):
        srcfname = lfutil.splitstandin(f)
        if srcfname is not None:
            # if the file isn't in the manifest then it was removed
            # or renamed, return None to indicate this
            try:
                fctx = ctx.filectx(srcfname)
            except error.LookupError:
                return None
            renamed = fctx.copysource()
            if renamed:
                # standin is always a largefile because largefile-ness
                # doesn't change after rename or copy
                renamed = lfutil.standin(renamed)

            return context.memfilectx(
                repo,
                memctx,
                f,
                lfiletohash[srcfname] + b'\n',
                b'l' in fctx.flags(),
                b'x' in fctx.flags(),
                renamed,
            )
        else:
            return _getnormalcontext(repo, ctx, f, revmap)

    # Commit
    _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
Esempio n. 7
0
    def _findsection(self, name):
        params = self.params
        namehash = hashutil.sha1(name).digest()
        fanoutkey = struct.unpack(
            params.fanoutstruct, namehash[: params.fanoutprefix]
        )[0]
        fanout = self._fanouttable

        start = fanout[fanoutkey] + params.indexstart
        indexend = self._indexend

        for i in pycompat.xrange(fanoutkey + 1, params.fanoutcount):
            end = fanout[i] + params.indexstart
            if end != start:
                break
        else:
            end = indexend

        entry = self._bisect(namehash, start, end, self.INDEXENTRYLENGTH)
        if not entry:
            raise KeyError(name)

        rawentry = struct.unpack(self.INDEXFORMAT, entry)
        x, offset, size, nodeindexoffset, nodeindexsize = rawentry
        rawnamelen = self._index[
            nodeindexoffset : nodeindexoffset + constants.FILENAMESIZE
        ]
        actualnamelen = struct.unpack(b'!H', rawnamelen)[0]
        nodeindexoffset += constants.FILENAMESIZE
        actualname = self._index[
            nodeindexoffset : nodeindexoffset + actualnamelen
        ]
        if actualname != name:
            raise KeyError(
                b"found file name %s when looking for %s" % (actualname, name)
            )
        nodeindexoffset += actualnamelen

        filenamelength = struct.unpack(
            b'!H', self._data[offset : offset + constants.FILENAMESIZE]
        )[0]
        offset += constants.FILENAMESIZE

        actualname = self._data[offset : offset + filenamelength]
        offset += filenamelength

        if name != actualname:
            raise KeyError(
                b"found file name %s when looking for %s" % (actualname, name)
            )

        # Skip entry list size
        offset += ENTRYCOUNTSIZE

        nodelistoffset = offset
        nodelistsize = (
            size - constants.FILENAMESIZE - filenamelength - ENTRYCOUNTSIZE
        )
        return (
            name,
            nodelistoffset,
            nodelistsize,
            nodeindexoffset,
            nodeindexsize,
        )