from bzrlib import ( branch, builtins, commands, errors, revision as _mod_revision, option, osutils, ) class cmd_flatlog(commands.Command): """Output the log in a flat format.""" takes_options = [ option.Option('limit', short_name='l', help='Limit the output to the first N revisions.', argname='N', type=builtins._parse_limit, ), option.Option('start', type=str, ), option.Option('end', type=str, ), option.Option('timezone', type=str, help="Display timezone as local, original or utc", ), option.Option('forward', help="Display the revisions from oldest to newest", ), ] def iter_all_history_topo_sorted(self, repo, revision_id, limit=None, end=None, no_merges=False): if revision_id in (None, _mod_revision.NULL_REVISION): return if revision_id == end: return if limit is not None: if limit <= 0: raise errors.BzrCommandError("limit option must be positive") if limit == 1: yield revision_id return to_process = [revision_id] indegree = {revision_id:0} graph = repo.get_graph() parents_map = {} no_merge = True _limit = limit while to_process: node = to_process.pop(0) parent_map = graph.get_parent_map([node]) if node not in parent_map: #ghost continue parents = parent_map[node] parents_map[node] = parents for parent in parents: if parent == end: continue done = indegree.setdefault(parent, 0) indegree[parent] += 1 if done == 0: to_process.append(parent) to_process = [revision_id] while to_process: node = to_process.pop(0) parents = parents_map[node] if not no_merges or len(parents) < 2: yield node if limit is not None: limit -= 1 if limit == 0: return for parent in parents: if parent == end or parent is _mod_revision.NULL_REVISION: continue if parent in parents_map: #not ghost indegree[parent] -= 1 if indegree[parent] == 0: to_process.append(parent) def iter_all_history_topo_sorted_forward(self, repo, revision_id, end=None, limit=None, no_merges=False): if revision_id in (None, _mod_revision.NULL_REVISION): return if revision_id == end: return if limit is not None: if limit <= 0: raise errors.BzrCommandError("limit option must be positive") to_process = [revision_id] graph = repo.get_graph() revs = set() while to_process: node = to_process.pop() parent_map = graph.get_parent_map([node]) if node not in parent_map: #ghost continue revs.add(node) parents = parent_map[node] for parent in parents: if parent is not _mod_revision.NULL_REVISION and parent not in revs: to_process.append(parent) for rev_id in graph.iter_topo_order(revs): if no_merges: parent_map = graph.get_parent_map([node]) if len(parent_map[node]) > 1: continue yield rev_id if limit is not None: limit -= 1 if limit == 0: return def iter_revs(self, repo, start, end, limit, forward, no_merges): num = 9 i = 0 rev_ids = [] if forward: rev_generator = self.iter_all_history_topo_sorted_forward(repo, start, limit=limit, end=end, no_merges=no_merges) else: rev_generator = self.iter_all_history_topo_sorted(repo, start, limit=limit, end=end, no_merges=no_merges) for rev_id in rev_generator: rev_ids.append(rev_id) i += 1 if i == num: revs = repo.get_revisions(rev_ids) for rev in revs: yield rev i = 0 rev_ids = [] num = min(int(num * 1.5), 200) revs = repo.get_revisions(rev_ids) for rev in revs: yield rev def run(self, limit=None, start=None, end=None, timezone=None, forward=False, no_merges=False): b = branch.Branch.open_containing('.')[0] if timezone is None: timezone = "original" if (start is not None or end is not None) and forward: raise errors.BzrCommandError("--forward and --start and --end are not supported yet") repo = b.repository repo.lock_read() try: if start is None: start = b.last_revision() for rev in self.iter_revs(repo, start, end, limit, forward, no_merges): self.outf.write("commit %s\nAuthor: %s\nDate: %s\n\n" % (rev.revision_id, rev.get_apparent_author(), osutils.format_date(rev.timestamp, rev.timezone or 0, timezone))) if not rev.message: self.outf.write(" (no message)\n") else: for l in rev.message.split('\n'): self.outf.write(" %s\n" % l) self.outf.write("\n") finally: repo.unlock() commands.register_command(cmd_flatlog)