<s> import dbutils <EOL> import gitutils <EOL> import itertools <EOL> import diff <EOL> from operation import Operation , OperationResult , OperationError , Optional <EOL> from log . commitset import CommitSet <EOL> from changeset . utils import createChangeset <EOL> from changeset . load import loadChangesetsForCommits <EOL> class LineAnnotator : <EOL> class NotSupported : pass <EOL> def __init__ ( self , db , parent , child , file_ids = None , commits = None , changeset_cache = None ) : <EOL> self . parent = parent <EOL> self . child = child <EOL> self . commitset = CommitSet . fromRange ( db , parent , child , commits = commits ) <EOL> self . changesets = { } <EOL> if not self . commitset : raise LineAnnotator . NotSupported <EOL> commits = [ ] <EOL> if not changeset_cache : changeset_cache = { } <EOL> for commit in self . commitset : <EOL> if len ( commit . parents ) > <NUM_LIT:1> : raise LineAnnotator . NotSupported <EOL> if commit in changeset_cache : <EOL> self . changesets [ commit . sha1 ] = changeset_cache [ commit ] <EOL> else : <EOL> commits . append ( commit ) <EOL> for changeset in loadChangesetsForCommits ( db , parent . repository , commits , filtered_file_ids = file_ids ) : <EOL> self . changesets [ changeset . child . sha1 ] = changeset_cache [ changeset . child ] = changeset <EOL> for commit in set ( self . commitset ) - set ( self . changesets . keys ( ) ) : <EOL> changesets = createChangeset ( db , None , commit . repository , commit = commit , filtered_file_ids = file_ids , do_highlight = False ) <EOL> assert len ( changesets ) == <NUM_LIT:1> <EOL> self . changesets [ commit . sha1 ] = changeset_cache [ commit ] = changesets [ <NUM_LIT:0> ] <EOL> self . commits = [ parent ] <EOL> self . commit_index = { parent . sha1 : <NUM_LIT:0> } <EOL> for commit in self . commitset : <EOL> self . commit_index [ commit . sha1 ] = len ( self . commits ) <EOL> self . commits . append ( commit ) <EOL> class Line : <EOL> def __init__ ( self , sha1 , primary ) : <EOL> self . sha1 = sha1 <EOL> self . primary = primary <EOL> self . untouched = True <EOL> def touch ( self , sha1 ) : <EOL> if self . untouched : <EOL> self . sha1 = sha1 <EOL> self . untouched = False <EOL> def __repr__ ( self ) : <EOL> return self . sha1 [ : <NUM_LIT:8> ] <EOL> def annotate ( self , file_id , first , last , check_user = None ) : <EOL> offset = first <EOL> count = last - first + <NUM_LIT:1> <EOL> initial_lines = [ LineAnnotator . Line ( sha1 , True ) for sha1 in itertools . repeat ( self . parent . sha1 , count ) ] <EOL> lines = initial_lines [ : ] <EOL> commit = self . commitset . getHeads ( ) . pop ( ) <EOL> while True : <EOL> changeset = self . changesets [ commit . sha1 ] <EOL> changeset_file = changeset . getFile ( file_id ) <EOL> if changeset_file : <EOL> changeset_file . loadOldLines ( ) <EOL> changeset_file . loadNewLines ( ) <EOL> offset_delta = <NUM_LIT:0> <EOL> modifications = [ ] <EOL> for chunk in changeset_file . chunks : <EOL> if chunk . insertEnd ( ) < offset : <EOL> offset_delta -= chunk . delta ( ) <EOL> continue <EOL> if chunk . insert_offset < offset + count : <EOL> if not chunk . deleted_lines : chunk . deleted_lines = changeset_file . getOldLines ( chunk ) <EOL> if not chunk . inserted_lines : chunk . inserted_lines = changeset_file . getNewLines ( chunk ) <EOL> for line in chunk . getLines ( ) : <EOL> if line . new_offset < offset : <EOL> if line . type == line . DELETED : <EOL> offset_delta += <NUM_LIT:1> <EOL> elif line . type == line . INSERTED : <EOL> offset_delta -= <NUM_LIT:1> <EOL> elif line . new_offset < offset + count : <EOL> if line . type == line . CONTEXT : <EOL> pass <EOL> elif line . type == line . DELETED : <EOL> modifications . append ( ( line . new_offset , - <NUM_LIT:1> ) ) <EOL> else : <EOL> if line . type == line . INSERTED : <EOL> modifications . append ( ( line . new_offset , <NUM_LIT:1> ) ) <EOL> line = lines [ line . new_offset - offset ] <EOL> if check_user and line . primary and line . untouched and commit . author . email == check_user . email : <EOL> return True <EOL> line . touch ( commit . sha1 ) <EOL> else : <EOL> break <EOL> modification_offset = offset <EOL> for line_offset , delta in modifications : <EOL> if delta > <NUM_LIT:0> : <EOL> del lines [ line_offset - modification_offset ] <EOL> count -= <NUM_LIT:1> <EOL> modification_offset += <NUM_LIT:1> <EOL> else : <EOL> lines . insert ( line_offset - modification_offset , LineAnnotator . Line ( None , False ) ) <EOL> count += <NUM_LIT:1> <EOL> modification_offset -= <NUM_LIT:1> <EOL> offset += offset_delta <EOL> parents = self . commitset . getParents ( commit ) <EOL> if len ( parents ) > <NUM_LIT:1> : raise LineAnnotator . NotSupported <EOL> if parents : commit = parents . pop ( ) <EOL> else : break <EOL> if check_user : <EOL> if self . parent . author . email == check_user . email : <EOL> return any ( itertools . imap ( lambda line : line . untouched , initial_lines ) ) <EOL> else : <EOL> return False <EOL> else : <EOL> return [ ( first + index , self . commit_index [ line . sha1 ] ) for index , line in enumerate ( initial_lines ) ] <EOL> class Blame ( Operation ) : <EOL> def __init__ ( self ) : <EOL> Operation . __init__ ( self , { "<STR_LIT>" : int , <EOL> "<STR_LIT>" : int , <EOL> "<STR_LIT>" : [ { "<STR_LIT:id>" : int , <EOL> "<STR_LIT>" : [ { "<STR_LIT>" : int , <EOL> "<STR_LIT>" : int } ] <EOL> } ] <EOL> } ) <EOL> def process ( self , db , user , repository_id , changeset_id , files ) : <EOL> repository = gitutils . Repository . fromId ( db , repository_id ) <EOL> cursor = db . cursor ( ) <EOL> cursor . execute ( "<STR_LIT>" , ( changeset_id , ) ) <EOL> parent_id , child_id = cursor . fetchone ( ) <EOL> parent = gitutils . Commit . fromId ( db , repository , parent_id ) <EOL> child = gitutils . Commit . fromId ( db , repository , child_id ) <EOL> try : <EOL> annotator = LineAnnotator ( db , parent , child ) <EOL> for file in files : <EOL> for block in file [ "<STR_LIT>" ] : <EOL> lines = annotator . annotate ( file [ "<STR_LIT:id>" ] , block [ "<STR_LIT>" ] , block [ "<STR_LIT>" ] ) <EOL> block [ "<STR_LIT>" ] = [ { "<STR_LIT>" : offset , "<STR_LIT>" : commit } for offset , commit in lines ] <EOL> return OperationResult ( commits = [ { "<STR_LIT>" : commit . sha1 , <EOL> "<STR_LIT>" : commit . author . name , <EOL> "<STR_LIT>" : commit . author . email , <EOL> "<STR_LIT>" : commit . niceSummary ( ) , <EOL> "<STR_LIT:message>" : commit . message , <EOL> "<STR_LIT>" : commit == parent , <EOL> "<STR_LIT>" : commit == child } <EOL> for commit in annotator . commits ] , <EOL> files = files ) <EOL> except LineAnnotator . NotSupported : <EOL> blame = gitutils . Blame ( parent , child ) <EOL> paths = { } <EOL> for file in files : <EOL> file_id = file [ "<STR_LIT:id>" ] <EOL> path = paths . get ( file_id ) <EOL> if not path : <EOL> path = paths [ file_id ] = dbutils . describe_file ( db , file_id ) <EOL> for block in file [ "<STR_LIT>" ] : <EOL> block [ "<STR_LIT>" ] = blame . blame ( db , path , block [ "<STR_LIT>" ] , block [ "<STR_LIT>" ] ) <EOL> return OperationResult ( commits = blame . commits , files = files ) </s>
