##// END OF EJS Templates
Unified DAG generation for hg and git...
marcink -
r2380:0c7dc340 beta
parent child Browse files
Show More
@@ -0,0 +1,127 b''
1 """
2 Modified mercurial DAG graph functions that re-uses VCS structure
3
4 It allows to have a shared codebase for DAG generation for hg and git repos
5 """
6
7 nullrev = -1
8
9
10 def grandparent(parentrev_func, lowestrev, roots, head):
11 """
12 Return all ancestors of head in roots which revision is
13 greater or equal to lowestrev.
14 """
15 pending = set([head])
16 seen = set()
17 kept = set()
18 llowestrev = max(nullrev, lowestrev)
19 while pending:
20 r = pending.pop()
21 if r >= llowestrev and r not in seen:
22 if r in roots:
23 kept.add(r)
24 else:
25 pending.update([p for p in parentrev_func(r)])
26 seen.add(r)
27 return sorted(kept)
28
29
30 def _dagwalker(repo, revs, alias):
31 if not revs:
32 return
33
34 if alias == 'hg':
35 cl = repo._repo.changelog.parentrevs
36 repo = repo
37 elif alias == 'git':
38 def cl(rev):
39 return [x.revision for x in repo[rev].parents()]
40 repo = repo
41
42 lowestrev = min(revs)
43 gpcache = {}
44
45 knownrevs = set(revs)
46 for rev in revs:
47 ctx = repo[rev]
48 parents = sorted(set([p.revision for p in ctx.parents
49 if p.revision in knownrevs]))
50 mpars = [p.revision for p in ctx.parents if
51 p.revision != nullrev and p.revision not in parents]
52
53 for mpar in mpars:
54 gp = gpcache.get(mpar)
55 if gp is None:
56 gp = gpcache[mpar] = grandparent(cl, lowestrev, revs, mpar)
57 if not gp:
58 parents.append(mpar)
59 else:
60 parents.extend(g for g in gp if g not in parents)
61
62 yield (ctx.revision, 'C', ctx, parents)
63
64
65 def _colored(dag):
66 """annotates a DAG with colored edge information
67
68 For each DAG node this function emits tuples::
69
70 (id, type, data, (col, color), [(col, nextcol, color)])
71
72 with the following new elements:
73
74 - Tuple (col, color) with column and color index for the current node
75 - A list of tuples indicating the edges between the current node and its
76 parents.
77 """
78 seen = []
79 colors = {}
80 newcolor = 1
81
82 getconf = lambda rev: {}
83
84 for (cur, type, data, parents) in dag:
85
86 # Compute seen and next
87 if cur not in seen:
88 seen.append(cur) # new head
89 colors[cur] = newcolor
90 newcolor += 1
91
92 col = seen.index(cur)
93 color = colors.pop(cur)
94 next = seen[:]
95
96 # Add parents to next
97 addparents = [p for p in parents if p not in next]
98 next[col:col + 1] = addparents
99
100 # Set colors for the parents
101 for i, p in enumerate(addparents):
102 if not i:
103 colors[p] = color
104 else:
105 colors[p] = newcolor
106 newcolor += 1
107
108 # Add edges to the graph
109 edges = []
110 for ecol, eid in enumerate(seen):
111 if eid in next:
112 bconf = getconf(eid)
113 edges.append((
114 ecol, next.index(eid), colors[eid],
115 bconf.get('width', -1),
116 bconf.get('color', '')))
117 elif eid == cur:
118 for p in parents:
119 bconf = getconf(p)
120 edges.append((
121 ecol, next.index(p), color,
122 bconf.get('width', -1),
123 bconf.get('color', '')))
124
125 # Yield and move on
126 yield (cur, type, data, (col, color), edges)
127 seen = next
@@ -1,134 +1,124 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changelog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changelog controller for rhodecode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 from mercurial import graphmod
30 29 from pylons import request, url, session, tmpl_context as c
31 30 from pylons.controllers.util import redirect
32 31 from pylons.i18n.translation import _
33 32
34 33 import rhodecode.lib.helpers as h
35 34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 35 from rhodecode.lib.base import BaseRepoController, render
37 36 from rhodecode.lib.helpers import RepoPage
38 37 from rhodecode.lib.compat import json
39
38 from rhodecode.lib.graphmod import _colored, _dagwalker
40 39 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError
41 40
42 41 log = logging.getLogger(__name__)
43 42
44 43
45 44 class ChangelogController(BaseRepoController):
46 45
47 46 @LoginRequired()
48 47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
49 48 'repository.admin')
50 49 def __before__(self):
51 50 super(ChangelogController, self).__before__()
52 51 c.affected_files_cut_off = 60
53 52
54 53 def index(self):
55 54 limit = 100
56 55 default = 20
57 56 if request.params.get('size'):
58 57 try:
59 58 int_size = int(request.params.get('size'))
60 59 except ValueError:
61 60 int_size = default
62 61 int_size = int_size if int_size <= limit else limit
63 62 c.size = int_size
64 63 session['changelog_size'] = c.size
65 64 session.save()
66 65 else:
67 66 c.size = int(session.get('changelog_size', default))
68 67
69 68 p = int(request.params.get('page', 1))
70 69 branch_name = request.params.get('branch', None)
71 70 try:
72 71 if branch_name:
73 72 collection = [z for z in
74 73 c.rhodecode_repo.get_changesets(start=0,
75 74 branch_name=branch_name)]
76 75 c.total_cs = len(collection)
77 76 else:
78 77 collection = c.rhodecode_repo
79 78 c.total_cs = len(c.rhodecode_repo)
80 79
81 80 c.pagination = RepoPage(collection, page=p, item_count=c.total_cs,
82 81 items_per_page=c.size, branch=branch_name)
83 82 collection = list(c.pagination)
84 83 page_revisions = [x.raw_id for x in collection]
85 84 c.comments = c.rhodecode_db_repo.comments(page_revisions)
86 85
87 86 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
88 87 log.error(traceback.format_exc())
89 88 h.flash(str(e), category='warning')
90 89 return redirect(url('home'))
91 90
92 91 self._graph(c.rhodecode_repo, collection, c.total_cs, c.size, p)
93 92
94 93 c.branch_name = branch_name
95 94 c.branch_filters = [('', _('All Branches'))] + \
96 95 [(k, k) for k in c.rhodecode_repo.branches.keys()]
97 96
98 97 return render('changelog/changelog.html')
99 98
100 99 def changelog_details(self, cs):
101 100 if request.environ.get('HTTP_X_PARTIAL_XHR'):
102 101 c.cs = c.rhodecode_repo.get_changeset(cs)
103 102 return render('changelog/changelog_details.html')
104 103
105 104 def _graph(self, repo, collection, repo_size, size, p):
106 105 """
107 106 Generates a DAG graph for mercurial
108 107
109 108 :param repo: repo instance
110 109 :param size: number of commits to show
111 110 :param p: page number
112 111 """
113 112 if not collection:
114 113 c.jsdata = json.dumps([])
115 114 return
116 115
117 116 data = []
118 117 revs = [x.revision for x in collection]
119 118
120 if repo.alias == 'git':
121 for _ in revs:
122 vtx = [0, 1]
123 edges = [[0, 0, 1]]
124 data.append(['', vtx, edges])
125
126 elif repo.alias == 'hg':
127 dag = graphmod.dagwalker(repo._repo, revs)
128 c.dag = graphmod.colored(dag, repo._repo)
129 for (id, type, ctx, vtx, edges) in c.dag:
130 if type != graphmod.CHANGESET:
131 continue
119 dag = _dagwalker(repo, revs, repo.alias)
120 dag = _colored(dag)
121 for (id, type, ctx, vtx, edges) in dag:
132 122 data.append(['', vtx, edges])
133 123
134 124 c.jsdata = json.dumps(data)
General Comments 0
You need to be logged in to leave comments. Login now