Differences between revisions 2 and 8 (spanning 6 versions)
Revision 2 as of 2007-08-12 08:43:41
Size: 4767
Comment: add this page to 'CategoryExtension'
Revision 8 as of 2008-02-19 02:01:11
Size: 10409
Comment:
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
'''This extension is not distributed with Mercurial.'''
Line 4: Line 6:
Line 15: Line 18:
This extension works with Mercurial 0.9.3(and may be later). This extension, which is tested on Mercurial 0.9.3 - 0.9.5,
can be found at http://www.lares.dti.ne.jp/~foozy/fujiguruma/scm/graphviz.py
(this is updated at 2008-02-19).
You can see renderring example at
http://www.lares.dti.ne.jp/~foozy/fujiguruma/scm/hg-visualize.en.html .

=== Questions ===
 * can this be used with jquery [http://people.iola.dk/olau/flot/examples/ flot] library?
Line 45: Line 55:
(see ''-r'' option) or
'''datetime range'''(see ''-d'' option).
(see ''-r'' option),
'''datetime range'''(see ''-d'' option) or
'''interval range'''
(see ''-i'' option)
.
Line 49: Line 61:
you can use any of 'revision number', 'relative number to tip'  you can use any of 'revision number', 'relative number to tip',
Line 59: Line 71:
For '''interval range''',
you should specify interval value from invocation time in '''second'''.
Line 77: Line 92:
-r --revision use revision to limit targets(default)
-d --datetime use datetime to limit targets
-u --urlbase url base for client side image map
-a --aliases file with user name aliases
-f --format format of changeset label (default: r)
   --hook hook module to render node attribute
}}}

=== Label formatting ===

You can specify the format to render label for node corresponded to changeset
in visualized graph.

Format string for ''-f'' option should consists of following characters.

  * u: username(may be aliased)
  * U: username
  * r: revision number
  * s: short-form changeset hash
  * h: changeset hash
  * d: datetime of changeset
  * t: first line of changeset description text

For example, {{{-f rUd}}} causes renderring label
{{{123/who committed <mail@address>/2007-01-01T00:00:00}}}.
 -r --revision use revision to limit targets(default)
 -d --datetime use datetime to limit targets
 -i --interval use interval from NOW to limit targets
 -a --aliases file with user name aliases
    --show-tags show tags in graph
 -u --urlbase url base for client side image map
    --format-label format of changeset label (default: %(r)s)
    --format-tooltip format of changeset tooltip (default: %(U)s/%(t)s)
    --group-by-date group changesets per date
    --group-by-branch group changesets per branch
    --node-attr-hook hook to render node attribute
    --edge-attr-hook hook to render edge attribute
    --cluster-attr-hook hook to render cluster attribute
}}}
Line 105: Line 109:
To reduce label length when username is used as part of it,
you can specify alias configuration file.
This file should consists lines in the following format, or lines
which start with
'#' as comment.
To reduce label length when username is used as part of it, you can
specify alias configuration file. This file should consists lines in
the following format, or lines which start with ''#'' as comment.
Line 114: Line 117:
Alias conversion is enabled when you use {{{u}}}(small u) as label format.

=== Client side image map ===

For example,
you can get HTML page {{{imagemap.html}}},
which cooperates with {{{hg serve}}}
running at local host port 8000, by following procedure.

{{{
% hg graphviz ... -u http://localhost:8000 ... > imagemap.dot

% dot -Tjpg -o imagemap.jpg imagemap.dot

% cat <<END > imagemap.html
<html>
<head><title>Changeset graph between .... </title></head>
<body>
<image src="imagemap.jpg" usemap="_anonymous_0">
END

% dot -Tcmapx imagemap.dot >> imagemap.html

% cat <<END >> imagemap.html
</body>
</html>
END
}}}

=== Renderring customize ===

Please specify module name by {{{--hook}}} option, if you want to
customize attribute of 'node'. Such module should have the function
definition shown below(name of function should be {{{attrhook}}}
exactlly):

{{{
def attrhook(repository, revision, node, changes):

=== Label/Tooltip formatting ===

You can specify format of both label and tooltip(shown in client side
image map, for example) for renderred changesets.

You can use keywords shown below in format string, which should be in
Python style formatting. All keywords will be substituted by STRING.

    * u: username(may be aliased)
    * U: username(not aliased)
    * r: changeset revision number
    * s: short-form changeset hash (12 digits of hexadecimal)
    * h: changeset hash (40 digits of hexadecimal)
    * d: datetime of changeset
    * t: first line of changeset description text

For example, '{{{%(u)s|%(r)s}}}' causes '{{{commiter|revision#}}}'.


=== Grouping ===

Grouping, especially of ''by-date'', may render changesets at
appropriate location for your sense.

With {{{group-by-date}}} option, graphviz extension groups changesets per
date. This grouping causes renderring rectangle per date.

With {{{group-by-branch}}} option, graphviz extension groups changesets
per branch, except for '{{{default}}}' one. This grouping causes renderring
rectangle per branche.

Please see '''Renderring customization''', if you want to hide such rectangles.


=== Renderring customization ===

There are three hook options to control graph renderring.

Each option can accept two format:

    * string, which starts with '{{{lambda }}}', as instant definition

    * fully qualified Python function name to load *.py file
      (e.g.: {{{module.name.function}}})

When hook returns empty string or None, graphviz extension does not
append attribute to renderring target object.

==== (1) node attribute hook ====

This hook can append attributes to each nodes, and should have
signature shown below:

{{{
def hook(repository, revision, node, changes, getalias):
Line 156: Line 177:
  * {{{repository}}}: to which target revision belongs
  * {{{revision}}}: revision number
  * {{{node}}}: gotten by {{{repository.lookup(revision)}}}
  * {{{changes}}}: gotten by {{{repository.changelog.rev(node)}}}.

Non empty string returned by {{{attrhook}}} is joinned to attribute of
'node', so you can highlight 'node's corresponded to changesets
which certain use made by code shown below, for example.

{{{
def attrhook(repository, revision, node, changes):
    return ((changes[1] == 'someone highlighted')
            and 'style = "filled", color = "black", fontcolor = "white"'
    * repo : to which target revision belongs
    * rev : revision number
    * node : gotten by '{{{repo.lookup(rev)}}}'
    * changes : gotten by '{{{repo.changelog.rev(node)}}}'
    * getalias : '{{{lambda name:}}}' to get alias name for given name

For example, you can highlight changesets, which certain user
made, by code shown below:

{{{
def hook(repo, rev, node, changes, getalias):
    return ((getalias(changes[1]) == 'someone')
            and 'style = "filled", fontcolor = "white"'
Line 171: Line 192:

==== (2) edge attribute hook ====

This hook can append attributes to each edges, and should have
signature shown below:

{{{
def hook(repo, parent, child, first, getalias):
}}}

Arguments are:

    * repo : to which target revision belongs
    * parent : form which edge is drawn
    * child : to which edge is drawn
    * first : whether parent is 1st one for child or not
    * getalias : '{{{lambda name:}}}' to get alias name for given name

Both {{{parent}}} and {{{child}}} are {{{(rev, node, changes)}}}, so you can
also define edge attribute hook as:

{{{
def hook(repo, (pr, pn, pc), (cr, cn, cc), first, getalias):
}}}

==== (3) cluster attribute hook ====

This hook can append attributes to each clusters(= grouping
rectangles), and should have signature shown below:

{{{
def hook(repo, branch, date)
}}}

Arguments are:

    * repo : to which target revision belongs
    * branch : branch name
    * date : date string({{{YYYYMMDD}}} format) or {{{""}}}

With {{{group-by-branch}}} option, this hook is invoked for each branches
other than '{{{default}}}'. At invocation, {{{""}}} is specified as
date.

With {{{group-by-date}}} option, this hook is invoked for each dates with
branch name, which may be '{{{default}}}'.

Without {{{group-by-date}}} nor {{{group-by-branch}}} options, this hook is
never invoked.

For example, you can hide all rectangles by code shown below:

{{{
def hook(repo, branch, date):
    return 'style=invis'
}}}

Or to show rectangles only for branch grouping:

{{{
def hook(repo, branch, date):
    return (date and 'style=invis')
}}}

To fit for Graphviz DOT language specification, this hook should
return '{{{;}}}' separated values differently from other hooks, which
should return '{{{,}}}' separated values.


=== Hook for Mercurial ===

To update image file automatically, graphviz extension also provides
{{{hook}}} function. You can specify this for any hook configurations,
because it does not use any hook type depend arguments. But it may
be useful mostly as {{{changegroup}}}/{{{commit}}} hook.

{{{
[hooks]
changegroup = python:graphviz.hook
commit = python:graphviz.hook
}}}

You can control hook behavior by {{{graphviz}}} section in your
{{{hgrc}}} file(s).

{{{
[graphviz]
# REQUIRED: image file output location
imagefile=%%(hgroot)s/.hg/revtree.jpg

# opt.* is equivalent to corresponded option appearance in command line
opt.revision=
#opt.datetime=
#opt.interval=
opt.aliases=%%(hgroot)s/aliases
opt.show-tags=
opt.urlbase=http://localhsot:8000
opt.format-label=%%(r)s
opt.format-tooltip=%%(U)s/%%(t)s
opt.group-by-date=
opt.group-by-branch=
#opt.node-attr-hook=
opt.edge-attr-hook=fully.qualified.python.function
opt.cluster-attr-hook=lambda repo, branch, date: 'style=invis'

# limit-spec list
limitspecs=-50,

# command line option of Graphviz DOT command.
dot_opts=-Nfontsize=9 -Grankdir=BT

# intermediate DOT language source will be saved, if this is given.
# it is saved in same location of 'imagefile', with '*.dot' suffix.
dot_source=

# client side image map file will be created, if this is given.
# it is saved in same location of 'imagefile', with '*.html' suffix.
imagemap=

# prologue(= before '<img> tag') part of image map HTML file.
prologue=<html><head><title>last updated by %%(r)s</title>....

# epilogue(= after '<area> tag') part of image map HTML file.
epilogue=....</html>
}}}

As you know from above example, you can specify some keyword
substitution in configuration. But you should use {{{%%}}}
instead of {{{%}}}
in normal Python notation, to prevent Python library from auto
substitution at configuration file reading.

All keywords for {{{format-label}}} and {{{format-tooltip}}} are available,
and values for them are from tip changeset.

In addtion, keywords shown below are also available.

  * now: datetime at hook invocation
  * hgroot: root of target repository

Keyword substitution is applied for:

  * imagefile
  * opt.aliases
  * prologue
  * epilogue


=== Manual hook invocation ===

By {{{graphviz-hook}}} command, you can invoke graphviz extension hook
manually.

This allows you to examine hook configuration without commit/pull.

----

translated in: [:JapaneseGraphvizExtension:Japanese]

Graphviz extension

This extension is not distributed with Mercurial.

Author: Katsunori FUJIWARA

Overview

This extension generates DOT language (of [http://www.graphviz.org/ Graphviz]) source to visualize changeset tree.

Graphviz allows you to get not only image(e.g. *.jpg) files, but also client side image map. So you can get HTML page which cooperates with hg serve.

This extension, which is tested on Mercurial 0.9.3 - 0.9.5, can be found at http://www.lares.dti.ne.jp/~foozy/fujiguruma/scm/graphviz.py (this is updated at 2008-02-19). You can see renderring example at http://www.lares.dti.ne.jp/~foozy/fujiguruma/scm/hg-visualize.en.html .

Questions

Configuration

This extension is enabled by adding the following lines to your configuration file (hgrc), if you place graphviz.py in the directory where your python knows:

[extensions]
graphviz = 

Otherwise:

[extensions]
graphviz = /path/to/graphviz.py

Usage

hg graphviz [OPTIONS] [limit-spec ...]

Without limit-spec, graphviz recognizes all changesets as renderring target.

You can limit target changesets by revision range (see -r option), datetime range(see -d option) or interval range (see -i option).

For revision range, you can use any of 'revision number', 'relative number to tip', 'short changeset ID', 'long changeset ID' or 'tag name'.

For datetime range, you should use XML Schema dateTime format, known as YYYY-MM-DDThh:mm:ss(middle T should be exactly T). But it is not so strongly restricted that you can specify 2007-99-99T99:99:99, for example.

For interval range, you should specify interval value from invocation time in second.

limit-spec should be in one of three format.

  1. 'Start,End' format limits target changesets to ones between 'Start' and 'End'.

  2. 'Start,' format limits target changesets to ones after 'Start'.

  3. ',End' format limits target changesets to ones before 'End'.

ATTENTION: End is treated as inclusive, so you should specify 2007-12-31T24:00:00(or such one) to ignore changesets created after 2007, for example.

graphviz recognizes following options.

 -r --revision           use revision to limit targets(default)
 -d --datetime           use datetime to limit targets
 -i --interval           use interval from NOW to limit targets
 -a --aliases            file with user name aliases
    --show-tags          show tags in graph
 -u --urlbase            url base for client side image map
    --format-label       format of changeset label (default: %(r)s)
    --format-tooltip     format of changeset tooltip (default: %(U)s/%(t)s)
    --group-by-date      group changesets per date
    --group-by-branch    group changesets per branch
    --node-attr-hook     hook to render node attribute
    --edge-attr-hook     hook to render edge attribute
    --cluster-attr-hook  hook to render cluster attribute

User name aliasing

To reduce label length when username is used as part of it, you can specify alias configuration file. This file should consists lines in the following format, or lines which start with # as comment.

AliasName=ActualName

Label/Tooltip formatting

You can specify format of both label and tooltip(shown in client side image map, for example) for renderred changesets.

You can use keywords shown below in format string, which should be in Python style formatting. All keywords will be substituted by STRING.

  • u: username(may be aliased)
  • U: username(not aliased)
  • r: changeset revision number
  • s: short-form changeset hash (12 digits of hexadecimal)
  • h: changeset hash (40 digits of hexadecimal)
  • d: datetime of changeset
  • t: first line of changeset description text

For example, '%(u)s|%(r)s' causes 'commiter|revision#'.

Grouping

Grouping, especially of by-date, may render changesets at appropriate location for your sense.

With group-by-date option, graphviz extension groups changesets per date. This grouping causes renderring rectangle per date.

With group-by-branch option, graphviz extension groups changesets per branch, except for 'default' one. This grouping causes renderring rectangle per branche.

Please see Renderring customization, if you want to hide such rectangles.

Renderring customization

There are three hook options to control graph renderring.

Each option can accept two format:

  • string, which starts with 'lambda ', as instant definition

  • fully qualified Python function name to load *.py file
    • (e.g.: module.name.function)

When hook returns empty string or None, graphviz extension does not append attribute to renderring target object.

(1) node attribute hook

This hook can append attributes to each nodes, and should have signature shown below:

def hook(repository, revision, node, changes, getalias):

Arguments are:

  • repo : to which target revision belongs
  • rev : revision number
  • node : gotten by 'repo.lookup(rev)'

  • changes : gotten by 'repo.changelog.rev(node)'

  • getalias : 'lambda name:' to get alias name for given name

For example, you can highlight changesets, which certain user made, by code shown below:

def hook(repo, rev, node, changes, getalias):
    return ((getalias(changes[1]) == 'someone')
            and 'style = "filled", fontcolor = "white"'
            or None)

(2) edge attribute hook

This hook can append attributes to each edges, and should have signature shown below:

def hook(repo, parent, child, first, getalias):

Arguments are:

  • repo : to which target revision belongs
  • parent : form which edge is drawn
  • child : to which edge is drawn
  • first : whether parent is 1st one for child or not
  • getalias : 'lambda name:' to get alias name for given name

Both parent and child are (rev, node, changes), so you can also define edge attribute hook as:

def hook(repo, (pr, pn, pc), (cr, cn, cc), first, getalias):

(3) cluster attribute hook

This hook can append attributes to each clusters(= grouping rectangles), and should have signature shown below:

def hook(repo, branch, date)

Arguments are:

  • repo : to which target revision belongs
  • branch : branch name
  • date : date string(YYYYMMDD format) or ""

With group-by-branch option, this hook is invoked for each branches other than 'default'. At invocation, "" is specified as date.

With group-by-date option, this hook is invoked for each dates with branch name, which may be 'default'.

Without group-by-date nor group-by-branch options, this hook is never invoked.

For example, you can hide all rectangles by code shown below:

def hook(repo, branch, date):
    return 'style=invis'

Or to show rectangles only for branch grouping:

def hook(repo, branch, date):
    return (date and 'style=invis')

To fit for Graphviz DOT language specification, this hook should return ';' separated values differently from other hooks, which should return ',' separated values.

Hook for Mercurial

To update image file automatically, graphviz extension also provides hook function. You can specify this for any hook configurations, because it does not use any hook type depend arguments. But it may be useful mostly as changegroup/commit hook.

[hooks]
changegroup = python:graphviz.hook
commit = python:graphviz.hook

You can control hook behavior by graphviz section in your hgrc file(s).

[graphviz]
# REQUIRED: image file output location
imagefile=%%(hgroot)s/.hg/revtree.jpg

# opt.* is equivalent to corresponded option appearance in command line
opt.revision=
#opt.datetime=
#opt.interval=
opt.aliases=%%(hgroot)s/aliases
opt.show-tags=
opt.urlbase=http://localhsot:8000
opt.format-label=%%(r)s
opt.format-tooltip=%%(U)s/%%(t)s
opt.group-by-date=
opt.group-by-branch=
#opt.node-attr-hook=
opt.edge-attr-hook=fully.qualified.python.function
opt.cluster-attr-hook=lambda repo, branch, date: 'style=invis'

# limit-spec list
limitspecs=-50,

# command line option of Graphviz DOT command.
dot_opts=-Nfontsize=9 -Grankdir=BT

# intermediate DOT language source will be saved, if this is given.
# it is saved in same location of 'imagefile', with '*.dot' suffix.
dot_source=

# client side image map file will be created, if this is given.
# it is saved in same location of 'imagefile', with '*.html' suffix.
imagemap=

# prologue(= before '<img> tag') part of image map HTML file.
prologue=<html><head><title>last updated by %%(r)s</title>....

# epilogue(= after '<area> tag') part of image map HTML file.
epilogue=....</html>

As you know from above example, you can specify some keyword substitution in configuration. But you should use %% instead of % in normal Python notation, to prevent Python library from auto substitution at configuration file reading.

All keywords for format-label and format-tooltip are available, and values for them are from tip changeset.

In addtion, keywords shown below are also available.

  • now: datetime at hook invocation
  • hgroot: root of target repository

Keyword substitution is applied for:

  • imagefile
  • opt.aliases
  • prologue
  • epilogue

Manual hook invocation

By graphviz-hook command, you can invoke graphviz extension hook manually.

This allows you to examine hook configuration without commit/pull.


translated in: [:JapaneseGraphvizExtension:Japanese]


CategoryExtension

GraphvizExtension (last edited 2012-02-08 12:56:27 by euro)