def smart_repo(empty_repo: Repo, runner: CliRunner) -> SmartRepo: runner.invoke(smart_git.install, [empty_repo.working_dir, r'E:\env\LLVM\bin\libclang.dll', '--silent']) repo = SmartRepo(empty_repo.working_dir) try: yield repo finally: repo.git.clear_cache() repo.close()
def test_rename_add_merge(smart_repo: SmartRepo): added_line_path = CursorPath( ('a.c', 'main()', (CursorKind.COMPOUND_STMT, 0), (CursorKind.COMPOUND_ASSIGNMENT_OPERATOR, 0))) variable_path = CursorPath(('a.c', 'main()', (CursorKind.COMPOUND_STMT, 0), (CursorKind.DECL_STMT, 0), 'a')) file_added = FileAdded('a.c', smart_repo.contents('a.c', 'initial')) line_added = SubASTInserted(smart_repo, smart_repo.contents('a.c', 'add-line'), added_line_path) renamed = VariableRenamed(variable_path, 'b') final_contents = as_lines('', 'int main() {', ' int b = 0;', ' b += 1;', ' return b;', '}') assert get_changes(smart_repo, 'other-to-master') \ == [[file_added], [renamed], [SubASTInserted(smart_repo, final_contents, added_line_path)]] assert tuple(smart_repo.rev_parse('other-to-master').parents) == ( smart_repo.rev_parse('rename'), smart_repo.rev_parse('add-line')) assert get_changes(smart_repo, 'master-to-other') \ == [[file_added], [line_added], [renamed]] assert tuple(smart_repo.rev_parse('master-to-other').parents) == ( smart_repo.rev_parse('add-line'), smart_repo.rev_parse('rename')) assert smart_repo.contents('a.c', 'other-to-master')\ == smart_repo.contents('a.c', 'master-to-other') \ == final_contents
def detect(cls, repo: SmartRepo, diff: git.DiffIndex) -> Iterable['SubASTInserted']: for m in diff.iter_change_type('M'): with repo.ast(m.a_blob) as a_ast, file_from_blob( m.b_blob) as b_file: b_file.seek(0) b_ast = repo.get_cindex().parse(b_file.name) for inserted_path in cls.detect_ast_insertions( a_ast.cursor, b_ast.cursor, CursorPath([m.a_path])): yield SubASTInserted(repo, b_file.readlines(), inserted_path)
def __init__(self, repo: SmartRepo, file_lines: List[bytes], ast_path: CursorPath): with file_from_text(file_lines, ast_path.file) as file: translation_unit = repo.get_cindex().parse(file.name) cursor = ast_path.locate(translation_unit, ast_path.file) parent_cursor = ast_path.drop(1).locate(translation_unit, ast_path.file) siblings = list(parent_cursor.get_children()) insertion_point = siblings.index(cursor) - 1 self.parent_path = ast_path.drop(1) if insertion_point == -1: from_location = cursor.extent.start self.predecessor_path = None if len(siblings) > 1: to_location = SourceLocation.from_offset( translation_unit, siblings[1].extent.start.file, siblings[1].extent.start.offset - 1) else: to_location = cursor.extent.end else: from_location = siblings[insertion_point].extent.end self.predecessor_path = ast_path.drop(1).appended( parent_cursor, siblings[insertion_point]) to_location = cursor.extent.end self.from_location = from_location.offset self.to_location = to_location.offset self.ast_path = ast_path self.file_lines = file_lines
def test_apply(smart_repo: SmartRepo): state = TreeBackedRepoState(smart_repo, smart_repo.head.commit.tree) change = VariableRenamed(smart_repo.find_cursor('a.c', lambda cur: cur.kind == CursorKind.VAR_DECL and cur.spelling == 'a'), 'meow') change.apply(smart_repo, state) assert state['a.c'] == as_lines('int main() {', ' int meow = 0;', ' return meow + (meow - 1);', '}')
def get_repo(repo_path: str, *expected_statuses: RepoStatus) -> (SmartRepo, RepoStatus): """ Open a GitPython Repo object for the given repository path. :param repo_path: path to the git repository. :param expected_statuses: in which repo status(es) the repo at repo_path should be. An Abort is thrown if it is found in a different status. """ current_status = RepoStatus.of(repo_path) if current_status not in expected_statuses: click.echo('{} should be {}, but is {}'.format( repo_path, 'either ' + ' or '.join(status.name for status in expected_statuses) if len(expected_statuses) > 1 else expected_statuses[0].name, current_status.name), err=True) raise click.Abort return SmartRepo(repo_path), current_status
def apply(self, repo: SmartRepo, repo_state: RepoState) -> None: file_text = repo_state[self.path.file] with file_from_text(file_text, self.path.file) as file: translation_unit = repo.get_cindex().parse(file.name) variable = self.path.locate(translation_unit, self.path.file) usages = search_ast(translation_unit, self.path.file, lambda cursor: cursor.kind == CursorKind.DECL_REF_EXPR and cursor.get_definition() == variable) replacements = [Replacement(variable.location.line - 1, variable.location.column - 1, variable.location.column + len(variable.spelling) - 1, self.new_name)] for usage_path in usages: usage = usage_path.locate(translation_unit, self.path.file) range: SourceRange = usage.extent start: SourceLocation = range.start end: SourceLocation = range.end assert start.line == end.line assert file_text[start.line - 1][start.column - 1:end.column - 1] == variable.spelling.encode('utf-8') replacements.append(Replacement(start.line - 1, start.column - 1, end.column - 1, self.new_name)) file_text = apply_replacements(file_text, replacements) repo_state[self.path.file] = file_text
def get_changes(repo: SmartRepo, revision: str='HEAD') -> List[List[Change]]: changes_file_contents = repo.contents(CHANGES_FILE_NAME, revision) if changes_file_contents is None: return [] return decode_changes(repo, changes_file_contents)
def test_detect(smart_repo: SmartRepo): a = smart_repo.find_cursor('a.c', lambda c: c.spelling == 'b').drop(1).appended('a') assert get_changes(smart_repo) == [[FileAdded('a.c', smart_repo.contents('a.c', 'initial'))], [VariableRenamed(a, 'b')]]
def detect(cls: Type['VariableRenamed'], repo: SmartRepo, diff: git.DiffIndex) -> Iterable['VariableRenamed']: for m in diff.iter_change_type('M'): with file_from_blob(m.a_blob) as a, file_from_blob(m.b_blob) as b: for renamed, new_name in RenamingDetector(repo.get_cindex()).get_renamed_variables(m.a_path, a.name, b.name).items(): yield VariableRenamed(renamed, new_name)