def linelog(self): if self._linelog is None: if os.path.exists(self.linelogpath): with open(self.linelogpath, 'rb') as f: try: self._linelog = linelogmod.linelog.fromdata(f.read()) except linelogmod.LineLogError: self._linelog = linelogmod.linelog() else: self._linelog = linelogmod.linelog() return self._linelog
def testanothersimplecase(self): ll = linelog.linelog() ll.replacelines(3, 0, 0, 0, 2) ll.replacelines(4, 0, 2, 0, 0) self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(4)], []) self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(3)], [(3, 0), (3, 1)]) # rev 2 is empty because contents were only ever introduced in rev 3 self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(2)], [])
def annotatealllines(self, rev, showpath=False, showlines=False): """(rev : str) -> [(node : str, linenum : int, path : str)] the result has the same format with annotate, but include all (including deleted) lines up to rev. call this after calling annotate(rev, ...) for better performance and accuracy. """ revfctx = self._resolvefctx(rev, resolverev=True, adjustctx=True) # find a chain from rev to anything in the mainbranch if revfctx not in self.revmap: chain = [revfctx] a = '' while True: f = chain[-1] pl = self._parentfunc(f) if not pl: break if pl[0] in self.revmap: a = pl[0].data() break chain.append(pl[0]) # both self.linelog and self.revmap is backed by filesystem. now # we want to modify them but do not want to write changes back to # files. so we create in-memory objects and copy them. it's like # a "fork". linelog = linelogmod.linelog() linelog.copyfrom(self.linelog) linelog.annotate(linelog.maxrev) revmap = revmapmod.revmap() revmap.copyfrom(self.revmap) for f in reversed(chain): b = f.data() blocks = list(self._diffblocks(a, b)) self._doappendrev(linelog, revmap, f, blocks) a = b else: # fastpath: use existing linelog, revmap as we don't write to them linelog = self.linelog revmap = self.revmap lines = linelog.getalllines() hsh = revfctx.node() llrev = revmap.hsh2rev(hsh) result = [(revmap.rev2hsh(r), l) for r, l in lines if r <= llrev] # cannot use _refineannotateresult since we need custom logic for # resolving line contents if showpath: result = self._addpathtoresult(result, revmap) if showlines: linecontents = self._resolvelines(result, revmap, linelog) result = (result, linecontents) return result
def testparseclinelogfile(self): # This data is what the replacements in testsimpleedits # produce when fed to the original linelog.c implementation. data = (b'\x00\x00\x00\x0c\x00\x00\x00\x0f' b'\x00\x00\x00\x00\x00\x00\x00\x02' b'\x00\x00\x00\x05\x00\x00\x00\x06' b'\x00\x00\x00\x06\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x07' b'\x00\x00\x00\x06\x00\x00\x00\x02' b'\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\t\x00\x00\x00\t' b'\x00\x00\x00\x00\x00\x00\x00\x0c' b'\x00\x00\x00\x08\x00\x00\x00\x05' b'\x00\x00\x00\x06\x00\x00\x00\x01' b'\x00\x00\x00\x00\x00\x00\x00\x05' b'\x00\x00\x00\x0c\x00\x00\x00\x05' b'\x00\x00\x00\n\x00\x00\x00\x01' b'\x00\x00\x00\x00\x00\x00\x00\t') llc = linelog.linelog.fromdata(data) self.assertEqual([(l.rev, l.linenum) for l in llc.annotate(1)], [ (1, 0), (1, 1), (1, 2), ]) self.assertEqual([(l.rev, l.linenum) for l in llc.annotate(2)], [ (1, 0), (2, 1), (1, 2), ]) self.assertEqual([(l.rev, l.linenum) for l in llc.annotate(3)], [ (1, 0), (1, 2), ]) # Check we emit the same bytecode. ll = linelog.linelog() # Initial revision: add lines 0, 1, and 2 ll.replacelines(1, 0, 0, 0, 3) # Replace line 1 with a new line ll.replacelines(2, 1, 2, 1, 2) # delete a line out of 2 ll.replacelines(3, 1, 2, 0, 0) diff = '\n ' + '\n '.join( difflib.unified_diff(ll.debugstr().splitlines(), llc.debugstr().splitlines(), 'python', 'c', lineterm='')) self.assertEqual(ll._program, llc._program, 'Program mismatch: ' + diff) # Done as a secondary step so we get a better result if the # program is where the mismatch is. self.assertEqual(ll, llc) self.assertEqual(ll.encode(), data)
def _buildlinelog(self): """calculate the initial linelog based on self.content{,line}s. this is similar to running a partial "annotate". """ llog = linelog.linelog() a, alines = b'', [] for i in pycompat.xrange(len(self.contents)): b, blines = self.contents[i], self.contentlines[i] llrev = i * 2 + 1 chunks = self._alldiffchunks(a, b, alines, blines) for a1, a2, b1, b2 in reversed(list(chunks)): llog.replacelines(llrev, a1, a2, b1, b2) a, alines = b, blines return llog
def testrandomedits(self): # Inspired by original linelog tests. seed = random.random() numrevs = 2000 ll = linelog.linelog() # Populate linelog for lines, rev, a1, a2, b1, b2, blines, usevec in _genedits( seed, numrevs): if usevec: ll.replacelines_vec(rev, a1, a2, blines) else: ll.replacelines(rev, a1, a2, b1, b2) ar = ll.annotate(rev) self.assertEqual(ll.annotateresult, lines) # Verify we can get back these states by annotating each rev for lines, rev, a1, a2, b1, b2, blines, usevec in _genedits( seed, numrevs): ar = ll.annotate(rev) self.assertEqual([(l.rev, l.linenum) for l in ar], lines)
def testsimpleedits(self): ll = linelog.linelog() # Initial revision: add lines 0, 1, and 2 ll.replacelines(1, 0, 0, 0, 3) self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(1)], [ (1, 0), (1, 1), (1, 2), ]) # Replace line 1 with a new line ll.replacelines(2, 1, 2, 1, 2) self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(2)], [ (1, 0), (2, 1), (1, 2), ]) # delete a line out of 2 ll.replacelines(3, 1, 2, 0, 0) self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(3)], [ (1, 0), (1, 2), ]) # annotation of 1 is unchanged self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(1)], [ (1, 0), (1, 1), (1, 2), ]) ll.annotate(3) # set internal state to revision 3 start = ll.getoffset(0) end = ll.getoffset(1) self.assertEqual(ll.getalllines(start, end), [ (1, 0), (2, 1), (1, 1), ]) self.assertEqual(ll.getalllines(), [ (1, 0), (2, 1), (1, 1), (1, 2), ])
def testlinelogencodedecode(self): program = [ linelog._eof(0, 0), linelog._jge(41, 42), linelog._jump(0, 43), linelog._eof(0, 0), linelog._jl(44, 45), linelog._line(46, 47), ] ll = linelog.linelog(program, maxrev=100) enc = ll.encode() # round-trips okay self.assertEqual(linelog.linelog.fromdata(enc)._program, ll._program) self.assertEqual(linelog.linelog.fromdata(enc), ll) # This encoding matches the encoding used by hg-experimental's # linelog file, or is supposed to if it doesn't. self.assertEqual(enc, (b'\x00\x00\x01\x90\x00\x00\x00\x06' b'\x00\x00\x00\xa4\x00\x00\x00*' b'\x00\x00\x00\x00\x00\x00\x00+' b'\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\xb1\x00\x00\x00-' b'\x00\x00\x00\xba\x00\x00\x00/'))