Mercurial > hg
annotate hgext/graphlog.py @ 5195:33015dac5df5
convert: fix mercurial_sink.putcommit
Changeset 4ebc8693ce72 added some code to putcommit to avoid creating a
revision that touches no files, but this can break regular conversions
from some repositories:
- conceptually, since we're converting a repo, we should try to make
the new hg repo as similar as possible to the original repo - we
should create a new changeset, even if the original revision didn't
touch any files (maybe the commit message had some important bit);
- even if a "regular" revision that doesn't touch any file may seem
weird (and maybe even broken), it's completely legitimate for a merge
revision to not touch any file, and, if we just skip it, the
converted repo will end up with wrong history and possibly an extra
head.
As an example, say the crew and main hg repos are sync'ed. Somebody
sends an important patch to the mailing list. Matt quickly applies
and pushes it. But at the same time somebody also applies it to crew
and pushes it. Suppose the commit message ended up being a bit
different (say, there was a typo and somebody didn't fix it) or that
the date ended up being different (because of different patch-applying
scripts): the changeset hashes will be different, but the manifests
will be the same.
Since both changesets were pushed to public repos, it's hard to recall
them. If both are merged, the manifest from the resulting merge
revision will have the exact same contents as its parents - i.e. the
merge revision really doesn't touch any file at all.
To keep the file filtering stuff "working", the generic code was changed
to skip empty revisions if we're filtering the repo, fixing a bug in the
process (we want parents[0] instead of tip).
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Fri, 17 Aug 2007 20:18:05 -0300 |
parents | 8d4fac0a9df7 |
children | 9ed100559851 |
rev | line source |
---|---|
4344 | 1 # ASCII graph log extension for Mercurial |
2 # | |
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net> | |
4516
96d8a56d4ef9
Removed trailing whitespace and tabs from python files
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4509
diff
changeset
|
4 # |
4344 | 5 # This software may be used and distributed according to the terms of |
6 # the GNU General Public License, incorporated herein by reference. | |
7 | |
8 import sys | |
9 from mercurial.cmdutil import revrange, show_changeset | |
10 from mercurial.i18n import _ | |
11 from mercurial.node import nullid, nullrev | |
12 from mercurial.util import Abort | |
13 | |
14 def revision_grapher(repo, start_rev, stop_rev): | |
15 """incremental revision grapher | |
16 | |
17 This generator function walks through the revision history from | |
18 revision start_rev to revision stop_rev (which must be less than | |
19 or equal to start_rev) and for each revision emits tuples with the | |
20 following elements: | |
21 | |
22 - Current revision. | |
23 - Current node. | |
24 - Column of the current node in the set of ongoing edges. | |
25 - Edges; a list of (col, next_col) indicating the edges between | |
26 the current node and its parents. | |
27 - Number of columns (ongoing edges) in the current revision. | |
28 - The difference between the number of columns (ongoing edges) | |
29 in the next revision and the number of columns (ongoing edges) | |
30 in the current revision. That is: -1 means one column removed; | |
31 0 means no columns added or removed; 1 means one column added. | |
32 """ | |
33 | |
34 assert start_rev >= stop_rev | |
35 curr_rev = start_rev | |
36 revs = [] | |
37 while curr_rev >= stop_rev: | |
38 node = repo.changelog.node(curr_rev) | |
39 | |
40 # Compute revs and next_revs. | |
41 if curr_rev not in revs: | |
42 # New head. | |
43 revs.append(curr_rev) | |
44 rev_index = revs.index(curr_rev) | |
45 next_revs = revs[:] | |
46 | |
47 # Add parents to next_revs. | |
48 parents = get_rev_parents(repo, curr_rev) | |
49 parents_to_add = [] | |
50 for parent in parents: | |
51 if parent not in next_revs: | |
52 parents_to_add.append(parent) | |
53 parents_to_add.sort() | |
54 next_revs[rev_index:rev_index + 1] = parents_to_add | |
55 | |
56 edges = [] | |
57 for parent in parents: | |
58 edges.append((rev_index, next_revs.index(parent))) | |
59 | |
60 n_columns_diff = len(next_revs) - len(revs) | |
61 yield (curr_rev, node, rev_index, edges, len(revs), n_columns_diff) | |
62 | |
63 revs = next_revs | |
64 curr_rev -= 1 | |
65 | |
66 def get_rev_parents(repo, rev): | |
67 return [x for x in repo.changelog.parentrevs(rev) if x != nullrev] | |
68 | |
69 def fix_long_right_edges(edges): | |
70 for (i, (start, end)) in enumerate(edges): | |
71 if end > start: | |
72 edges[i] = (start, end + 1) | |
73 | |
74 def draw_edges(edges, nodeline, interline): | |
75 for (start, end) in edges: | |
76 if start == end + 1: | |
77 interline[2 * end + 1] = "/" | |
78 elif start == end - 1: | |
79 interline[2 * start + 1] = "\\" | |
80 elif start == end: | |
81 interline[2 * start] = "|" | |
82 else: | |
83 nodeline[2 * end] = "+" | |
84 if start > end: | |
85 (start, end) = (end,start) | |
86 for i in range(2 * start + 1, 2 * end): | |
87 if nodeline[i] != "+": | |
88 nodeline[i] = "-" | |
89 | |
90 def format_line(line, level, logstr): | |
91 text = "%-*s %s" % (2 * level, "".join(line), logstr) | |
92 return "%s\n" % text.rstrip() | |
93 | |
94 def get_nodeline_edges_tail( | |
95 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): | |
96 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: | |
97 # Still going in the same non-vertical direction. | |
98 if n_columns_diff == -1: | |
99 start = max(node_index + 1, p_node_index) | |
100 tail = ["|", " "] * (start - node_index - 1) | |
101 tail.extend(["/", " "] * (n_columns - start)) | |
102 return tail | |
103 else: | |
104 return ["\\", " "] * (n_columns - node_index - 1) | |
105 else: | |
106 return ["|", " "] * (n_columns - node_index - 1) | |
107 | |
108 def get_padding_line(ni, n_columns, edges): | |
109 line = [] | |
110 line.extend(["|", " "] * ni) | |
111 if (ni, ni - 1) in edges or (ni, ni) in edges: | |
112 # (ni, ni - 1) (ni, ni) | |
113 # | | | | | | | | | |
114 # +---o | | o---+ | |
115 # | | c | | c | | | |
116 # | |/ / | |/ / | |
117 # | | | | | | | |
118 c = "|" | |
119 else: | |
120 c = " " | |
121 line.extend([c, " "]) | |
122 line.extend(["|", " "] * (n_columns - ni - 1)) | |
123 return line | |
124 | |
125 def get_limit(limit_opt): | |
126 if limit_opt: | |
127 try: | |
128 limit = int(limit_opt) | |
129 except ValueError: | |
130 raise Abort(_("limit must be a positive integer")) | |
131 if limit <= 0: | |
132 raise Abort(_("limit must be positive")) | |
133 else: | |
134 limit = sys.maxint | |
135 return limit | |
136 | |
137 def get_revs(repo, rev_opt): | |
138 if rev_opt: | |
139 revs = revrange(repo, rev_opt) | |
140 return (max(revs), min(revs)) | |
141 else: | |
142 return (repo.changelog.count() - 1, 0) | |
143 | |
4735
8d4fac0a9df7
Make hg glog abort if unused arguments are given.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4730
diff
changeset
|
144 def graphlog(ui, repo, **opts): |
4344 | 145 """show revision history alongside an ASCII revision graph |
146 | |
147 Print a revision history alongside a revision graph drawn with | |
148 ASCII characters. | |
149 | |
4583
11cf78983961
Reverted changesets 9d1380e5c8c5 and 1d46169ec197: show @ as glog parent again.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4516
diff
changeset
|
150 Nodes printed as an @ character are parents of the working |
4344 | 151 directory. |
152 """ | |
153 | |
154 limit = get_limit(opts["limit"]) | |
155 (start_rev, stop_rev) = get_revs(repo, opts["rev"]) | |
156 stop_rev = max(stop_rev, start_rev - limit + 1) | |
157 if start_rev == nullrev: | |
158 return | |
159 cs_printer = show_changeset(ui, repo, opts) | |
160 grapher = revision_grapher(repo, start_rev, stop_rev) | |
161 repo_parents = repo.dirstate.parents() | |
162 prev_n_columns_diff = 0 | |
163 prev_node_index = 0 | |
164 | |
165 for (rev, node, node_index, edges, n_columns, n_columns_diff) in grapher: | |
166 # log_strings is the list of all log strings to draw alongside | |
167 # the graph. | |
168 ui.pushbuffer() | |
169 cs_printer.show(rev, node) | |
170 log_strings = ui.popbuffer().split("\n")[:-1] | |
171 | |
172 if n_columns_diff == -1: | |
173 # Transform | |
174 # | |
175 # | | | | | | | |
176 # o | | into o---+ | |
177 # |X / |/ / | |
178 # | | | | | |
179 fix_long_right_edges(edges) | |
180 | |
181 # add_padding_line says whether to rewrite | |
182 # | |
183 # | | | | | | | | | |
184 # | o---+ into | o---+ | |
185 # | / / | | | # <--- padding line | |
186 # o | | | / / | |
187 # o | | | |
4633
ff7253a0d1da
Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4583
diff
changeset
|
188 add_padding_line = (len(log_strings) > 2 and |
ff7253a0d1da
Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4583
diff
changeset
|
189 n_columns_diff == -1 and |
ff7253a0d1da
Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4583
diff
changeset
|
190 [x for (x, y) in edges if x + 1 < y]) |
4344 | 191 |
192 # fix_nodeline_tail says whether to rewrite | |
193 # | |
194 # | | o | | | | o | | | |
195 # | | |/ / | | |/ / | |
196 # | o | | into | o / / # <--- fixed nodeline tail | |
197 # | |/ / | |/ / | |
198 # o | | o | | | |
199 fix_nodeline_tail = len(log_strings) <= 2 and not add_padding_line | |
200 | |
4583
11cf78983961
Reverted changesets 9d1380e5c8c5 and 1d46169ec197: show @ as glog parent again.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4516
diff
changeset
|
201 # nodeline is the line containing the node character (@ or o). |
4344 | 202 nodeline = ["|", " "] * node_index |
203 if node in repo_parents: | |
4583
11cf78983961
Reverted changesets 9d1380e5c8c5 and 1d46169ec197: show @ as glog parent again.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4516
diff
changeset
|
204 node_ch = "@" |
4344 | 205 else: |
206 node_ch = "o" | |
207 nodeline.extend([node_ch, " "]) | |
208 | |
209 nodeline.extend( | |
210 get_nodeline_edges_tail( | |
211 node_index, prev_node_index, n_columns, n_columns_diff, | |
212 prev_n_columns_diff, fix_nodeline_tail)) | |
213 | |
214 # shift_interline is the line containing the non-vertical | |
215 # edges between this entry and the next. | |
216 shift_interline = ["|", " "] * node_index | |
217 if n_columns_diff == -1: | |
218 n_spaces = 1 | |
219 edge_ch = "/" | |
220 elif n_columns_diff == 0: | |
221 n_spaces = 2 | |
222 edge_ch = "|" | |
223 else: | |
224 n_spaces = 3 | |
225 edge_ch = "\\" | |
226 shift_interline.extend(n_spaces * [" "]) | |
227 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1)) | |
228 | |
229 # Draw edges from the current node to its parents. | |
230 draw_edges(edges, nodeline, shift_interline) | |
231 | |
232 # lines is the list of all graph lines to print. | |
233 lines = [nodeline] | |
234 if add_padding_line: | |
235 lines.append(get_padding_line(node_index, n_columns, edges)) | |
236 lines.append(shift_interline) | |
237 | |
238 # Make sure that there are as many graph lines as there are | |
239 # log strings. | |
240 while len(log_strings) < len(lines): | |
241 log_strings.append("") | |
242 if len(lines) < len(log_strings): | |
243 extra_interline = ["|", " "] * (n_columns + n_columns_diff) | |
244 while len(lines) < len(log_strings): | |
245 lines.append(extra_interline) | |
246 | |
247 # Print lines. | |
248 indentation_level = max(n_columns, n_columns + n_columns_diff) | |
249 for (line, logstr) in zip(lines, log_strings): | |
250 ui.write(format_line(line, indentation_level, logstr)) | |
251 | |
252 # ...and start over. | |
253 prev_node_index = node_index | |
254 prev_n_columns_diff = n_columns_diff | |
255 | |
256 cmdtable = { | |
257 "glog": | |
4730
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
258 (graphlog, |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
259 [('l', 'limit', '', _('limit number of changes displayed')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
260 ('p', 'patch', False, _('show patch')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
261 ('r', 'rev', [], _('show the specified revision or range')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
262 ('', 'style', '', _('display using template map file')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
263 ('', 'template', '', _('display with template'))], |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
264 _('hg glog [OPTION]...')), |
4344 | 265 } |