##// 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 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changelog
3 rhodecode.controllers.changelog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changelog controller for rhodecode
6 changelog controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from mercurial import graphmod
30 from pylons import request, url, session, tmpl_context as c
29 from pylons import request, url, session, tmpl_context as c
31 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
33
32
34 import rhodecode.lib.helpers as h
33 import rhodecode.lib.helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
37 from rhodecode.lib.helpers import RepoPage
36 from rhodecode.lib.helpers import RepoPage
38 from rhodecode.lib.compat import json
37 from rhodecode.lib.compat import json
39
38 from rhodecode.lib.graphmod import _colored, _dagwalker
40 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError
39 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError
41
40
42 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
43
42
44
43
45 class ChangelogController(BaseRepoController):
44 class ChangelogController(BaseRepoController):
46
45
47 @LoginRequired()
46 @LoginRequired()
48 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
49 'repository.admin')
48 'repository.admin')
50 def __before__(self):
49 def __before__(self):
51 super(ChangelogController, self).__before__()
50 super(ChangelogController, self).__before__()
52 c.affected_files_cut_off = 60
51 c.affected_files_cut_off = 60
53
52
54 def index(self):
53 def index(self):
55 limit = 100
54 limit = 100
56 default = 20
55 default = 20
57 if request.params.get('size'):
56 if request.params.get('size'):
58 try:
57 try:
59 int_size = int(request.params.get('size'))
58 int_size = int(request.params.get('size'))
60 except ValueError:
59 except ValueError:
61 int_size = default
60 int_size = default
62 int_size = int_size if int_size <= limit else limit
61 int_size = int_size if int_size <= limit else limit
63 c.size = int_size
62 c.size = int_size
64 session['changelog_size'] = c.size
63 session['changelog_size'] = c.size
65 session.save()
64 session.save()
66 else:
65 else:
67 c.size = int(session.get('changelog_size', default))
66 c.size = int(session.get('changelog_size', default))
68
67
69 p = int(request.params.get('page', 1))
68 p = int(request.params.get('page', 1))
70 branch_name = request.params.get('branch', None)
69 branch_name = request.params.get('branch', None)
71 try:
70 try:
72 if branch_name:
71 if branch_name:
73 collection = [z for z in
72 collection = [z for z in
74 c.rhodecode_repo.get_changesets(start=0,
73 c.rhodecode_repo.get_changesets(start=0,
75 branch_name=branch_name)]
74 branch_name=branch_name)]
76 c.total_cs = len(collection)
75 c.total_cs = len(collection)
77 else:
76 else:
78 collection = c.rhodecode_repo
77 collection = c.rhodecode_repo
79 c.total_cs = len(c.rhodecode_repo)
78 c.total_cs = len(c.rhodecode_repo)
80
79
81 c.pagination = RepoPage(collection, page=p, item_count=c.total_cs,
80 c.pagination = RepoPage(collection, page=p, item_count=c.total_cs,
82 items_per_page=c.size, branch=branch_name)
81 items_per_page=c.size, branch=branch_name)
83 collection = list(c.pagination)
82 collection = list(c.pagination)
84 page_revisions = [x.raw_id for x in collection]
83 page_revisions = [x.raw_id for x in collection]
85 c.comments = c.rhodecode_db_repo.comments(page_revisions)
84 c.comments = c.rhodecode_db_repo.comments(page_revisions)
86
85
87 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
86 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
88 log.error(traceback.format_exc())
87 log.error(traceback.format_exc())
89 h.flash(str(e), category='warning')
88 h.flash(str(e), category='warning')
90 return redirect(url('home'))
89 return redirect(url('home'))
91
90
92 self._graph(c.rhodecode_repo, collection, c.total_cs, c.size, p)
91 self._graph(c.rhodecode_repo, collection, c.total_cs, c.size, p)
93
92
94 c.branch_name = branch_name
93 c.branch_name = branch_name
95 c.branch_filters = [('', _('All Branches'))] + \
94 c.branch_filters = [('', _('All Branches'))] + \
96 [(k, k) for k in c.rhodecode_repo.branches.keys()]
95 [(k, k) for k in c.rhodecode_repo.branches.keys()]
97
96
98 return render('changelog/changelog.html')
97 return render('changelog/changelog.html')
99
98
100 def changelog_details(self, cs):
99 def changelog_details(self, cs):
101 if request.environ.get('HTTP_X_PARTIAL_XHR'):
100 if request.environ.get('HTTP_X_PARTIAL_XHR'):
102 c.cs = c.rhodecode_repo.get_changeset(cs)
101 c.cs = c.rhodecode_repo.get_changeset(cs)
103 return render('changelog/changelog_details.html')
102 return render('changelog/changelog_details.html')
104
103
105 def _graph(self, repo, collection, repo_size, size, p):
104 def _graph(self, repo, collection, repo_size, size, p):
106 """
105 """
107 Generates a DAG graph for mercurial
106 Generates a DAG graph for mercurial
108
107
109 :param repo: repo instance
108 :param repo: repo instance
110 :param size: number of commits to show
109 :param size: number of commits to show
111 :param p: page number
110 :param p: page number
112 """
111 """
113 if not collection:
112 if not collection:
114 c.jsdata = json.dumps([])
113 c.jsdata = json.dumps([])
115 return
114 return
116
115
117 data = []
116 data = []
118 revs = [x.revision for x in collection]
117 revs = [x.revision for x in collection]
119
118
120 if repo.alias == 'git':
119 dag = _dagwalker(repo, revs, repo.alias)
121 for _ in revs:
120 dag = _colored(dag)
122 vtx = [0, 1]
121 for (id, type, ctx, vtx, edges) in dag:
123 edges = [[0, 0, 1]]
122 data.append(['', vtx, edges])
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
132 data.append(['', vtx, edges])
133
123
134 c.jsdata = json.dumps(data)
124 c.jsdata = json.dumps(data)
General Comments 0
You need to be logged in to leave comments. Login now