##// END OF EJS Templates
updated license to be explicit about GPL version 2
updated license to be explicit about GPL version 2

File last commit:

r8225:46293a0c default
r8225:46293a0c default
Show More
graphlog.py
415 lines | 14.4 KiB | text/x-python | PythonLexer
Joel Rosdahl
Add graphlog extension
r4344 # ASCII graph log extension for Mercurial
#
# Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
Thomas Arendsen Hein
Removed trailing whitespace and tabs from python files
r4516 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 '''show revision graphs in terminal windows
This extension adds a --graph option to the incoming, outgoing and log
commands. When this options is given, an ascii representation of the
revision graph is also shown.
'''
Joel Rosdahl
Add graphlog extension
r4344
Steve Borho
graphlog: add filelog revision grapher...
r5938 import os
Joel Rosdahl
Add graphlog extension
r4344 from mercurial.cmdutil import revrange, show_changeset
Peter Arrenbrecht
cleanup: drop unused imports
r7873 from mercurial.commands import templateopts
Joel Rosdahl
Add graphlog extension
r4344 from mercurial.i18n import _
Joel Rosdahl
Remove unused imports
r6212 from mercurial.node import nullrev
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
Peter Arrenbrecht
cleanup: drop unused imports
r7873 from mercurial import hg, url, util
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 def revisions(repo, start, stop):
"""cset DAG generator yielding (rev, node, [parents]) tuples
Dirkjan Ochtman
graphlog: kill whitespace from earlier refactoring
r7374
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 This generator function walks through the revision history from revision
start to revision stop (which must be less than or equal to start).
"""
assert start >= stop
cur = start
while cur >= stop:
ctx = repo[cur]
Dirkjan Ochtman
graphlog: fix python2.3 incompatibility (used genexp, sorted())
r7379 parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev]
parents.sort()
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 yield (ctx, parents)
cur -= 1
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 def filerevs(repo, path, start, stop):
"""file cset DAG generator yielding (rev, node, [parents]) tuples
Dirkjan Ochtman
graphlog: kill whitespace from earlier refactoring
r7374
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 This generator function walks through the revision history of a single
file from revision start to revision stop (which must be less than or
equal to start).
Steve Borho
graphlog: add filelog revision grapher...
r5938 """
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 assert start >= stop
Matt Mackall
add __len__ and __iter__ methods to repo and revlog
r6750 filerev = len(repo.file(path)) - 1
Steve Borho
graphlog: add filelog revision grapher...
r5938 while filerev >= 0:
fctx = repo.filectx(path, fileid=filerev)
Peter Arrenbrecht
graphlog: fix regression with filelogs introduced by 7bc62ebe7693
r7383 parents = [f.linkrev() for f in fctx.parents() if f.path() == path]
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 parents.sort()
if fctx.rev() <= start:
yield (fctx, parents)
if fctx.rev() <= stop:
break
filerev -= 1
Steve Borho
graphlog: add filelog revision grapher...
r5938
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 def grapher(nodes):
"""grapher for asciigraph on a list of nodes and their parents
Dirkjan Ochtman
graphlog: kill whitespace from earlier refactoring
r7374
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 nodes must generate tuples (node, parents, char, lines) where
- parents must generate the parents of node, in sorted order,
and max length 2,
- char is the char to print as the node symbol, and
Dirkjan Ochtman
graphlog: kill whitespace from earlier refactoring
r7374 - lines are the lines to display next to the node.
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 """
seen = []
for node, parents, char, lines in nodes:
if node not in seen:
seen.append(node)
nodeidx = seen.index(node)
Steve Borho
graphlog: add filelog revision grapher...
r5938
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 knownparents = []
newparents = []
Steve Borho
graphlog: add filelog revision grapher...
r5938 for parent in parents:
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 if parent in seen:
knownparents.append(parent)
else:
newparents.append(parent)
Steve Borho
graphlog: add filelog revision grapher...
r5938
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 ncols = len(seen)
nextseen = seen[:]
nextseen[nodeidx:nodeidx + 1] = newparents
edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
if len(newparents) > 0:
edges.append((nodeidx, nodeidx))
if len(newparents) > 1:
edges.append((nodeidx, nodeidx + 1))
nmorecols = len(nextseen) - ncols
seen = nextseen
yield (char, lines, nodeidx, edges, ncols, nmorecols)
Steve Borho
graphlog: add filelog revision grapher...
r5938
Joel Rosdahl
Add graphlog extension
r4344 def fix_long_right_edges(edges):
for (i, (start, end)) in enumerate(edges):
if end > start:
edges[i] = (start, end + 1)
Dirkjan Ochtman
graphlog: move functions around, eliminate helper function...
r7326 def get_nodeline_edges_tail(
node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
# Still going in the same non-vertical direction.
if n_columns_diff == -1:
start = max(node_index + 1, p_node_index)
tail = ["|", " "] * (start - node_index - 1)
tail.extend(["/", " "] * (n_columns - start))
return tail
else:
return ["\\", " "] * (n_columns - node_index - 1)
else:
return ["|", " "] * (n_columns - node_index - 1)
Joel Rosdahl
Add graphlog extension
r4344 def draw_edges(edges, nodeline, interline):
for (start, end) in edges:
if start == end + 1:
interline[2 * end + 1] = "/"
elif start == end - 1:
interline[2 * start + 1] = "\\"
elif start == end:
interline[2 * start] = "|"
else:
nodeline[2 * end] = "+"
if start > end:
(start, end) = (end,start)
for i in range(2 * start + 1, 2 * end):
if nodeline[i] != "+":
nodeline[i] = "-"
def get_padding_line(ni, n_columns, edges):
line = []
line.extend(["|", " "] * ni)
if (ni, ni - 1) in edges or (ni, ni) in edges:
# (ni, ni - 1) (ni, ni)
# | | | | | | | |
# +---o | | o---+
# | | c | | c | |
# | |/ / | |/ /
# | | | | | |
c = "|"
else:
c = " "
line.extend([c, " "])
line.extend(["|", " "] * (n_columns - ni - 1))
return line
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 def ascii(ui, grapher):
"""prints an ASCII graph of the DAG returned by the grapher
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 grapher is a generator that emits tuples with the following elements:
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 - Character to use as node's symbol.
- List of lines to display as the node's text.
- Column of the current node in the set of ongoing edges.
- Edges; a list of (col, next_col) indicating the edges between
the current node and its parents.
- Number of columns (ongoing edges) in the current revision.
- The difference between the number of columns (ongoing edges)
in the next revision and the number of columns (ongoing edges)
in the current revision. That is: -1 means one column removed;
0 means no columns added or removed; 1 means one column added.
"""
Joel Rosdahl
Add graphlog extension
r4344 prev_n_columns_diff = 0
prev_node_index = 0
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 for (node_ch, node_lines, node_index, edges, n_columns, n_columns_diff) in grapher:
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: add assertion for allowed n_column_diff range
r7356 assert -2 < n_columns_diff < 2
Joel Rosdahl
Add graphlog extension
r4344 if n_columns_diff == -1:
# Transform
#
# | | | | | |
# o | | into o---+
# |X / |/ /
# | | | |
fix_long_right_edges(edges)
# add_padding_line says whether to rewrite
#
# | | | | | | | |
# | o---+ into | o---+
# | / / | | | # <--- padding line
# o | | | / /
# o | |
Peter Arrenbrecht
graphlog: rename some vars prior to refactoring
r7324 add_padding_line = (len(node_lines) > 2 and
Thomas Arendsen Hein
Cleanup of whitespace, indentation and line continuation.
r4633 n_columns_diff == -1 and
[x for (x, y) in edges if x + 1 < y])
Joel Rosdahl
Add graphlog extension
r4344
# fix_nodeline_tail says whether to rewrite
#
# | | o | | | | o | |
# | | |/ / | | |/ /
# | o | | into | o / / # <--- fixed nodeline tail
# | |/ / | |/ /
# o | | o | |
Peter Arrenbrecht
graphlog: rename some vars prior to refactoring
r7324 fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # nodeline is the line containing the node character (typically o)
Joel Rosdahl
Add graphlog extension
r4344 nodeline = ["|", " "] * node_index
nodeline.extend([node_ch, " "])
nodeline.extend(
get_nodeline_edges_tail(
node_index, prev_node_index, n_columns, n_columns_diff,
prev_n_columns_diff, fix_nodeline_tail))
# shift_interline is the line containing the non-vertical
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # edges between this entry and the next
Joel Rosdahl
Add graphlog extension
r4344 shift_interline = ["|", " "] * node_index
if n_columns_diff == -1:
n_spaces = 1
edge_ch = "/"
elif n_columns_diff == 0:
n_spaces = 2
edge_ch = "|"
else:
n_spaces = 3
edge_ch = "\\"
shift_interline.extend(n_spaces * [" "])
shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # draw edges from the current node to its parents
Joel Rosdahl
Add graphlog extension
r4344 draw_edges(edges, nodeline, shift_interline)
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # lines is the list of all graph lines to print
Joel Rosdahl
Add graphlog extension
r4344 lines = [nodeline]
if add_padding_line:
lines.append(get_padding_line(node_index, n_columns, edges))
lines.append(shift_interline)
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # make sure that there are as many graph lines as there are
# log strings
Peter Arrenbrecht
graphlog: rename some vars prior to refactoring
r7324 while len(node_lines) < len(lines):
node_lines.append("")
if len(lines) < len(node_lines):
Joel Rosdahl
Add graphlog extension
r4344 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
Peter Arrenbrecht
graphlog: rename some vars prior to refactoring
r7324 while len(lines) < len(node_lines):
Joel Rosdahl
Add graphlog extension
r4344 lines.append(extra_interline)
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # print lines
Joel Rosdahl
Add graphlog extension
r4344 indentation_level = max(n_columns, n_columns + n_columns_diff)
Peter Arrenbrecht
graphlog: rename some vars prior to refactoring
r7324 for (line, logstr) in zip(lines, node_lines):
Dirkjan Ochtman
graphlog: move functions around, eliminate helper function...
r7326 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
ui.write(ln.rstrip() + '\n')
Joel Rosdahl
Add graphlog extension
r4344
Peter Arrenbrecht
graphlog: make some comment lines more like others in punctuation
r7323 # ... and start over
Joel Rosdahl
Add graphlog extension
r4344 prev_node_index = node_index
prev_n_columns_diff = n_columns_diff
Dirkjan Ochtman
graphlog: move functions around, eliminate helper function...
r7326 def get_revs(repo, rev_opt):
if rev_opt:
revs = revrange(repo, rev_opt)
return (max(revs), min(revs))
else:
return (len(repo) - 1, 0)
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 def check_unsupported_flags(opts):
for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
"only_merges", "user", "only_branch", "prune", "newest_first",
"no_merges", "include", "exclude"]:
if op in opts and opts[op]:
Dirkjan Ochtman
graphlog: import util module rather than selected functions
r7713 raise util.Abort(_("--graph option is incompatible with --%s") % op)
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 def graphlog(ui, repo, path=None, **opts):
"""show revision history alongside an ASCII revision graph
Print a revision history alongside a revision graph drawn with
ASCII characters.
Nodes printed as an @ character are parents of the working
directory.
"""
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 check_unsupported_flags(opts)
Dirkjan Ochtman
graphlog: reuse cmdutil.loglimit() instead of redefining
r7715 limit = cmdutil.loglimit(opts)
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 start, stop = get_revs(repo, opts["rev"])
stop = max(stop, start - limit + 1)
if start == nullrev:
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 return
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 if path:
Dirkjan Ochtman
graphlog: import util module rather than selected functions
r7713 path = util.canonpath(repo.root, os.getcwd(), path)
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 if path: # could be reset in canonpath
revdag = filerevs(repo, path, start, stop)
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325 else:
Peter Arrenbrecht
graphlog: refactor common grapher code...
r7370 revdag = revisions(repo, start, stop)
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 graphdag = graphabledag(ui, repo, revdag, opts)
ascii(ui, grapher(graphdag))
def graphrevs(repo, nodes, opts):
Martin Geisler
util: use built-in set and frozenset...
r8150 include = set(nodes)
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 limit = cmdutil.loglimit(opts)
count = 0
Matt Mackall
replace various uses of list.reverse()
r8210 for node in reversed(nodes):
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 if count >= limit:
break
ctx = repo[node]
parents = [p.rev() for p in ctx.parents() if p.node() in include]
parents.sort()
yield (ctx, parents)
count += 1
def graphabledag(ui, repo, revdag, opts):
showparents = [ctx.node() for ctx in repo[None].parents()]
Dirkjan Ochtman
graphlog: use built-in log output buffering
r7371 displayer = show_changeset(ui, repo, opts, buffered=True)
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 for (ctx, parents) in revdag:
displayer.show(ctx)
lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
char = ctx.node() in showparents and '@' or 'o'
yield (ctx.rev(), parents, char, lines)
def goutgoing(ui, repo, dest=None, **opts):
"""show the outgoing changesets alongside an ASCII revision graph
Peter Arrenbrecht
graphlog: split the actual DAG grapher out into a separate method...
r7325
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 Print the outgoing changesets alongside a revision graph drawn with
ASCII characters.
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 Nodes printed as an @ character are parents of the working
directory.
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 """
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716
check_unsupported_flags(opts)
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 dest, revs, checkout = hg.parseurl(
ui.expandpath(dest or 'default-push', dest or 'default'),
opts.get('rev'))
if revs:
revs = [repo.lookup(rev) for rev in revs]
Matt Mackall
add cmdutil.remoteui...
r8188 other = hg.repository(cmdutil.remoteui(ui, opts), dest)
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
o = repo.findoutgoing(other, force=opts.get('force'))
if not o:
ui.status(_("no changes found\n"))
return
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 o = repo.changelog.nodesbetween(o, revs)[0]
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 revdag = graphrevs(repo, o, opts)
graphdag = graphabledag(ui, repo, revdag, opts)
ascii(ui, grapher(graphdag))
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426
def gincoming(ui, repo, source="default", **opts):
"""show the incoming changesets alongside an ASCII revision graph
Print the incoming changesets alongside a revision graph drawn with
ASCII characters.
Nodes printed as an @ character are parents of the working
directory.
"""
check_unsupported_flags(opts)
source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
Matt Mackall
add cmdutil.remoteui...
r8188 other = hg.repository(cmdutil.remoteui(repo, opts), source)
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 ui.status(_('comparing with %s\n') % url.hidepassword(source))
if revs:
revs = [other.lookup(rev) for rev in revs]
incoming = repo.findincoming(other, heads=revs, force=opts["force"])
if not incoming:
try:
os.unlink(opts["bundle"])
except:
pass
ui.status(_("no changes found\n"))
return
cleanup = None
try:
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426 fname = opts["bundle"]
if fname or not other.local():
# create a bundle (uncompressed if other repo is not local)
if revs is None:
cg = other.changegroup(incoming, "incoming")
else:
cg = other.changegroupsubset(incoming, revs, 'incoming')
bundletype = other.local() and "HG10BZ" or "HG10UN"
fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
# keep written bundle?
if opts["bundle"]:
cleanup = None
if not other.local():
# use the created uncompressed bundlerepo
other = bundlerepo.bundlerepository(ui, repo.root, fname)
chlist = other.changelog.nodesbetween(incoming, revs)[0]
Dirkjan Ochtman
graphlog: extract large parts of repeated code from incoming/outgoing
r7716 revdag = graphrevs(other, chlist, opts)
graphdag = graphabledag(ui, repo, revdag, opts)
ascii(ui, grapher(graphdag))
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426
finally:
if hasattr(other, 'close'):
other.close()
if cleanup:
os.unlink(cleanup)
def uisetup(ui):
'''Initialize the extension.'''
_wrapcmd(ui, 'log', commands.table, graphlog)
_wrapcmd(ui, 'incoming', commands.table, gincoming)
_wrapcmd(ui, 'outgoing', commands.table, goutgoing)
def _wrapcmd(ui, cmd, table, wrapfn):
'''wrap the command'''
def graph(orig, *args, **kwargs):
if kwargs['graph']:
return wrapfn(*args, **kwargs)
return orig(*args, **kwargs)
entry = extensions.wrapcommand(table, cmd, graph)
Jim Correia
log-like commands now use -G for --graph, -g for --git
r7763 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
Alpar Juttner
Graphlog extension adds a --graph option to log/in/out...
r7426
Joel Rosdahl
Add graphlog extension
r4344 cmdtable = {
"glog":
Thomas Arendsen Hein
Updated command tables in commands.py and hgext extensions....
r4730 (graphlog,
[('l', 'limit', '', _('limit number of changes displayed')),
('p', 'patch', False, _('show patch')),
('r', 'rev', [], _('show the specified revision or range')),
Thomas Arendsen Hein
Introduce templateopts and logopts to reduce duplicate option definitions.
r6192 ] + templateopts,
Thomas Arendsen Hein
glog shows at most one file: correct synopsis
r5942 _('hg glog [OPTION]... [FILE]')),
Joel Rosdahl
Add graphlog extension
r4344 }