def test_reuse_detector(self): blob = make_object(Blob, data='blob') tree1 = self.commit_tree([('a', blob)]) tree2 = self.commit_tree([('b', blob)]) detector = RenameDetector(self.store) changes = [TreeChange(CHANGE_RENAME, ('a', F, blob.id), ('b', F, blob.id))] self.assertEqual(changes, detector.changes_with_renames(tree1.id, tree2.id)) self.assertEqual(changes, detector.changes_with_renames(tree1.id, tree2.id))
def test_reuse_detector(self): blob = make_object(Blob, data='blob') tree1 = self.commit_tree([('a', blob)]) tree2 = self.commit_tree([('b', blob)]) detector = RenameDetector(self.store) changes = [TreeChange(CHANGE_RENAME, ('a', F, blob.id), ('b', F, blob.id))] self.assertEqual(changes, detector.changes_with_renames(tree1.id, tree2.id)) self.assertEqual(changes, detector.changes_with_renames(tree1.id, tree2.id))
def main(args, hear, talk, complain): """Reset boring changes See doc-string of this file for outline. Required arguments - args, hear, talk and complain -- should, respectively, be (or behave as, e.g. if mocking to test) sys.argv, sys.stdin, sys.stdout and sys.stderr. The only command-line option supported (in args) is a '--disclaim' flag, to treat as boring all changes in files with the standard 'We mean it' disclaimer; it is usual to pass this flag.\n""" ignore = Scanner.disclaimed if '--disclaim' in args else ( lambda p, w: False) # We're in the root directory of the module: repo = Repo('.') store, index = repo.object_store, repo.open_index() renamer = RenameDetector(store) try: # TODO: demand stronger similarity for a copy than for rename; # our huge copyright headers (and common boilerplate) make # small header files look very similar despite their real # content all being quite different. Probably need to hack # dulwich (find_copies_harder is off by default anyway). for kind, old, new in \ renamer.changes_with_renames(store[repo.refs['HEAD']].tree, index.commit(store)): # Each of old, new is a named triple of .path, .mode and # .sha; kind is the change type, in ('add', 'modify', # 'delete', 'rename', 'copy', 'unchanged'), although we # shouldn't get the last. If new.path is None, file was # removed, not renamed; otherwise, if new has a # disclaimer, it's private despite its name and path. if new.path and not ignore(new.path, complain.write): assert kind not in ('unchanged', 'delete'), kind if kind != 'add': # Filter out boring changes index[new.path] = Selector(store, new.sha, old.sha, old.mode or new.mode).refine() elif old.path: # disclaimed or removed: ignore by restoring assert new.path or kind == 'delete', (kind, new.path) index[old.path] = Selector.restore(store[old.sha], old.mode) talk.write(old.path + '\n') if new.path and new.path != old.path: talk.write(new.path + '\n') else: # new but disclaimed: ignore by discarding assert kind == 'add' and new.path, (kind, new.path) del index[new.path] talk.write(new.path + '\n') index.write() except IOError: # ... and any other errors that just mean failure. return 1 return 0
def last_commit_renamed_path(repo, history, path): """ Check if the last commit of this history renames the given path If so, return the new path, otherwise None """ if len(history) <= 1: return None; newtree = history[0].tree oldtree = history[1].tree rd = RenameDetector(repo) result = ""; for change in rd.changes_with_renames(oldtree, newtree): if change.type == 'rename' and change.old.path == path: return change.new.path return None
def main(args, hear, talk, complain): # Future: we may want to parse more args, query the user or wrap # talk, complain for verbosity control. ignore = Scanner.disclaimed if '--disclaim' in args else ( lambda p, w: False) # We're in the root directory of the module: repo = Repo('.') store, index = repo.object_store, repo.open_index() renamer = RenameDetector(store) try: # TODO: demand stronger similarity for a copy than for rename; # our huge copyright headers (and common boilerplate) make # small header files look very similar despite their real # content all being quite different. Probably need to hack # dulwich (find_copies_harder is off by default anyway). for kind, old, new in \ renamer.changes_with_renames(store[repo.refs['HEAD']].tree, index.commit(store)): # Each of old, new is a named triple of .path, .mode and # .sha; kind is the change type, in ('add', 'modify', # 'delete', 'rename', 'copy', 'unchanged'), although we # shouldn't get the last. If new.path is None, file was # removed, not renamed; otherwise, if new has a # disclaimer, it's private despite its name and path. if new.path and not ignore(new.path, complain.write): assert kind not in ('unchanged', 'delete'), kind if kind != 'add': # Filter out boring changes index[new.path] = Selector(store, new.sha, old.sha, old.mode or new.mode).refine() elif old.path: # disclaimed or removed: ignore by restoring assert new.path or kind == 'delete', (kind, new.path) index[old.path] = Selector.restore(store[old.sha], old.mode) else: # new but disclaimed: ignore by discarding assert kind == 'add' and new.path, (kind, new.path) del index[new.path] index.write() except IOError: # ... and any other errors that just mean failure. return 1 return 0
def detect_renames(self, tree1, tree2, want_unchanged=False, **kwargs): detector = RenameDetector(self.store, **kwargs) return detector.changes_with_renames(tree1.id, tree2.id, want_unchanged=want_unchanged)
def detect_renames(self, tree1, tree2, want_unchanged=False, **kwargs): detector = RenameDetector(self.store, **kwargs) return detector.changes_with_renames(tree1.id, tree2.id, want_unchanged=want_unchanged)
def detect_renames(self, tree1, tree2, **kwargs): detector = RenameDetector(self.store, tree1.id, tree2.id, **kwargs) return detector.changes_with_renames()