diff --git a/contrib/wix/templates.wxs b/contrib/wix/templates.wxs
--- a/contrib/wix/templates.wxs
+++ b/contrib/wix/templates.wxs
@@ -147,6 +147,8 @@
+
+
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -30,6 +30,7 @@
encoding,
error,
graphmod,
+ obsutil,
pycompat,
revset,
revsetlang,
@@ -469,6 +470,60 @@
"""
return changelog(web, shortlog=True)
+@webcommand('obsgraph')
+def obsgraph(web):
+ """
+ /obsgraph[/{revision}]
+ -----------------------
+
+ Show obsolescence graph of a single changeset.
+ """
+ ctx = webutil.changectx(web.repo, web.req)
+ revs = smartset.baseset([ctx.rev()])
+
+ walker = obsutil.obshistorywalker(web.repo.unfiltered(), revs)
+ tree = [item for item in graphmod.colored(walker, web.repo)]
+
+ def jsdata(context):
+ for (id, type, ctx, vtx, edges) in tree:
+ yield {'node': pycompat.bytestr(ctx),
+ 'graphnode': webutil.getgraphnode(web.repo, ctx),
+ 'vertex': vtx,
+ 'edges': edges}
+
+ def obsgraphentries(context):
+ parity = paritygen(web.stripecount)
+ for row, (id, type, ctx, vtx, edges) in enumerate(tree):
+ if isinstance(ctx, obsutil.missingchangectx):
+ entry = {
+ 'node': hex(ctx.node()),
+ 'obsolete': True,
+ 'missing': True
+ }
+ else:
+ entry = webutil.commonentry(web.repo, ctx)
+ edgedata = [{'col': edge[0],
+ 'nextcol': edge[1],
+ 'color': edge[2] - 1,
+ 'width': edge[3],
+ 'bcolor': edge[4]}
+ for edge in edges]
+
+ entry.update({'col': vtx[0],
+ 'color': vtx[1] - 1,
+ 'parity': next(parity),
+ 'edges': templateutil.mappinglist(edgedata),
+ 'row': row,
+ 'nextrow': row + 1})
+
+ yield entry
+
+ return web.sendtemplate(
+ 'obsgraph',
+ jsdata=templateutil.mappinggenerator(jsdata),
+ obsgraphentries=templateutil.mappinggenerator(obsgraphentries),
+ **webutil.changesetentry(web, ctx))
+
@webcommand('changeset')
def changeset(web):
"""
diff --git a/mercurial/templates/paper/changeset.tmpl b/mercurial/templates/paper/changeset.tmpl
--- a/mercurial/templates/paper/changeset.tmpl
+++ b/mercurial/templates/paper/changeset.tmpl
@@ -17,6 +17,7 @@
diff --git a/mercurial/templates/paper/map b/mercurial/templates/paper/map
--- a/mercurial/templates/paper/map
+++ b/mercurial/templates/paper/map
@@ -10,6 +10,8 @@
shortlogentry = shortlogentry.tmpl
graph = graph.tmpl
graphentry = graphentry.tmpl
+obsgraph = obsgraph.tmpl
+obsgraphentry = obsgraphentry.tmpl
help = help.tmpl
helptopics = helptopics.tmpl
diff --git a/mercurial/templates/paper/changeset.tmpl b/mercurial/templates/paper/obsgraph.tmpl
copy from mercurial/templates/paper/changeset.tmpl
copy to mercurial/templates/paper/obsgraph.tmpl
--- a/mercurial/templates/paper/changeset.tmpl
+++ b/mercurial/templates/paper/obsgraph.tmpl
@@ -1,5 +1,5 @@
{header}
-{repo|escape}: {node|short}
+{repo|escape}: {node|short} obsolescence graph
@@ -16,7 +16,8 @@
branches
@@ -69,26 +70,57 @@
files |
{files} |
-
- diffstat |
-
- {diffsummary}
- [+]
-
- |
-
-
-
-
line diff
-
-{diff}
+
+
+
+
+
{obsgraphentries%obsgraphentry}
-
+
+
diff --git a/mercurial/templates/paper/graphentry.tmpl b/mercurial/templates/paper/obsgraphentry.tmpl
copy from mercurial/templates/paper/graphentry.tmpl
copy to mercurial/templates/paper/obsgraphentry.tmpl
--- a/mercurial/templates/paper/graphentry.tmpl
+++ b/mercurial/templates/paper/obsgraphentry.tmpl
@@ -1,9 +1,9 @@
diff --git a/mercurial/templates/static/mercurial.js b/mercurial/templates/static/mercurial.js
--- a/mercurial/templates/static/mercurial.js
+++ b/mercurial/templates/static/mercurial.js
@@ -233,6 +233,168 @@
};
+function SVGGraph() {
+ this.svg = document.getElementById('svg-graph');
+ var ctm = this.svg.getScreenCTM();
+ var transform = 'translate(' + (ctm.e % 1) + ',' + (ctm.f % 1) + ')';
+ this.svg.getElementById('transformer').setAttribute('transform', transform);
+ this.colors = this.svg.querySelectorAll('defs > [id^="color-"]');
+ this.bg = [0, 4];
+ this.cell = [2, 0];
+ this.columns = 0;
+}
+
+SVGGraph.prototype = {
+ reset: function() {
+ this.bg = [0, 4];
+ this.cell = [2, 0];
+ this.columns = 0;
+ },
+
+ scale: function(height) {
+ this.bgHeight = height;
+ this.boxSize = Math.floor(this.bgHeight / 1.2);
+ },
+
+ getColor: function(color) {
+ if (typeof color === "string") {
+ return "#" + color;
+ } else {
+ color %= this.colors.length;
+ return 'url(#' + this.colors[color].id + ')';
+ }
+ },
+
+ _line: function(x0, y0, x1, y1) {
+ var el = document.createElementNS(this.svg.getAttribute('xmlns'), 'line');
+ el.setAttribute('x1', x0);
+ el.setAttribute('y1', y0);
+ el.setAttribute('x2', x1);
+ el.setAttribute('y2', y1);
+ return el;
+ },
+
+ _curve: function(x0, y0, x1, y1) {
+ var el = document.createElementNS(this.svg.getAttribute('xmlns'), 'path');
+ var xmid = (x0 + x1) / 2;
+ var ymid = (y0 + y1) / 2;
+ var d = 'M ' + [x0, y0].join(' ');
+ d += ' Q ' + [x0, ymid, xmid, ymid].join(' ');
+ d += ' Q ' + [x1, ymid, x1, y1].join(' ');
+ el.setAttribute('d', d);
+ return el;
+ },
+
+ _el: function(x0, y0, x1, y1) {
+ if (x0 === x1) {
+ return this._line(x0, y0, x1, y1);
+ }
+ return this._curve(x0, y0, x1, y1);
+ },
+
+ edge: function(x0, y0, x1, y1, color, width) {
+ var c = this.getColor(color);
+ var line = this._el(x0, y0, x1, y1);
+ line.setAttribute('stroke', c);
+ if (width >= 0) {
+ line.setAttribute('stroke-width', width);
+ }
+ this.svg.getElementById('lines').appendChild(line);
+ },
+
+ _use: function(nodeType, x, y, fill) {
+ var el = document.createElementNS(this.svg.getAttribute('xmlns'), 'use');
+ el.setAttribute('href', '#graph-node-' + nodeType);
+ el.setAttribute('x', x);
+ el.setAttribute('y', y);
+ el.setAttribute('fill', fill);
+ return el;
+ },
+
+ vertex: function(x, y, color, parity, cur) {
+ var c = this.getColor(color);
+ if (cur.graphnode[0] === '@') {
+ var cnode = this._use('current', x, y, c);
+ this.svg.getElementById('nodes').appendChild(cnode);
+ }
+ var nodeType = 'normal';
+ switch (cur.graphnode.substr(-1)) {
+ case '_':
+ nodeType = 'closing';
+ break;
+ case '*':
+ nodeType = 'unstable';
+ break;
+ case 'x':
+ nodeType = 'obsolete';
+ break;
+ }
+ var node = this._use(nodeType, x, y, c);
+ this.svg.getElementById('nodes').appendChild(node);
+
+ var left = (this.bgHeight - this.boxSize) + (this.columns + 1) * this.boxSize;
+ var item = document.querySelector('[data-node="' + cur.node + '"]');
+ if (item) {
+ item.style.paddingLeft = left + 'px';
+ }
+ },
+
+ render: function(data) {
+ var i, j, cur, line, start, end, color, x, y, x0, y0, x1, y1, column;
+ var cols = 0;
+
+ for (i = 0; i < data.length; i++) {
+ var parity = i % 2;
+ this.cell[1] += this.bgHeight;
+ this.bg[1] += this.bgHeight;
+
+ cur = data[i];
+ var fold = false;
+
+ for (j = 0; j < cur.edges.length; j++) {
+ line = cur.edges[j];
+ start = line[0];
+ end = line[1];
+ color = line[2];
+ var width = line[3];
+ var branchcolor = line[4];
+ if (branchcolor) {
+ color = branchcolor;
+ }
+
+ if (end > this.columns || start > this.columns) {
+ this.columns += 1;
+ }
+
+ if (start === this.columns && start > end) {
+ fold = true;
+ }
+
+ x0 = this.cell[0] + this.boxSize * start + this.boxSize / 2;
+ y0 = this.bg[1] - this.bgHeight / 2;
+ x1 = this.cell[0] + this.boxSize * end + this.boxSize / 2;
+ y1 = this.bg[1] + this.bgHeight / 2;
+
+ this.edge(x0, y0, x1, y1, color, width);
+
+ cols = Math.max(cols, start, end);
+ }
+
+ column = cur.vertex[0];
+ color = cur.vertex[1];
+
+ x = this.cell[0] + this.boxSize * column + this.boxSize / 2;
+ y = this.bg[1] - this.bgHeight / 2;
+ this.vertex(x, y, color, parity, cur);
+
+ if (fold) {
+ this.columns -= 1;
+ }
+ }
+ this.svg.setAttribute('width', (cols + 1) * this.bgHeight);
+ this.svg.setAttribute('height', (data.length + 1) * this.bgHeight - 27);
+ }
+};
function process_dates(parentSelector){
diff --git a/mercurial/templates/static/style-paper.css b/mercurial/templates/static/style-paper.css
--- a/mercurial/templates/static/style-paper.css
+++ b/mercurial/templates/static/style-paper.css
@@ -451,7 +451,7 @@
padding: 0;
}
-canvas {
+canvas#graph, svg#svg-graph {
position: absolute;
z-index: 5;
top: -0.7em;
diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t
+++ b/tests/test-hgweb-commands.t
@@ -859,6 +859,7 @@
@@ -964,6 +965,8 @@