98 c = " " |
98 c = " " |
99 line.extend([c, " "]) |
99 line.extend([c, " "]) |
100 line.extend(["|", " "] * (n_columns - ni - 1)) |
100 line.extend(["|", " "] * (n_columns - ni - 1)) |
101 return line |
101 return line |
102 |
102 |
103 def ascii(ui, dag): |
103 def ascii(ui, base, type, char, text, coldata): |
104 """prints an ASCII graph of the DAG |
104 """prints an ASCII graph of the DAG |
105 |
105 |
106 dag is a generator that emits tuples with the following elements: |
106 takes the following arguments (one call per node in the graph): |
107 |
107 |
|
108 - ui to write to |
|
109 - A list we can keep the needed state in |
108 - Column of the current node in the set of ongoing edges. |
110 - Column of the current node in the set of ongoing edges. |
109 - Type indicator of node data == ASCIIDATA. |
111 - Type indicator of node data == ASCIIDATA. |
110 - Payload: (char, lines): |
112 - Payload: (char, lines): |
111 - Character to use as node's symbol. |
113 - Character to use as node's symbol. |
112 - List of lines to display as the node's text. |
114 - List of lines to display as the node's text. |
117 in the next revision and the number of columns (ongoing edges) |
119 in the next revision and the number of columns (ongoing edges) |
118 in the current revision. That is: -1 means one column removed; |
120 in the current revision. That is: -1 means one column removed; |
119 0 means no columns added or removed; 1 means one column added. |
121 0 means no columns added or removed; 1 means one column added. |
120 """ |
122 """ |
121 |
123 |
122 base = [0, 0] |
124 idx, edges, ncols, coldiff = coldata |
123 for idx, type, (char, text), edges, ncols, coldiff in dag: |
125 assert -2 < coldiff < 2 |
124 |
126 if coldiff == -1: |
125 assert -2 < coldiff < 2 |
127 # Transform |
126 if coldiff == -1: |
|
127 # Transform |
|
128 # |
|
129 # | | | | | | |
|
130 # o | | into o---+ |
|
131 # |X / |/ / |
|
132 # | | | | |
|
133 fix_long_right_edges(edges) |
|
134 |
|
135 # add_padding_line says whether to rewrite |
|
136 # |
128 # |
137 # | | | | | | | | |
129 # | | | | | | |
138 # | o---+ into | o---+ |
130 # o | | into o---+ |
139 # | / / | | | # <--- padding line |
131 # |X / |/ / |
140 # o | | | / / |
132 # | | | | |
141 # o | | |
133 fix_long_right_edges(edges) |
142 add_padding_line = (len(text) > 2 and |
134 |
143 coldiff == -1 and |
135 # add_padding_line says whether to rewrite |
144 [x for (x, y) in edges if x + 1 < y]) |
136 # |
145 |
137 # | | | | | | | | |
146 # fix_nodeline_tail says whether to rewrite |
138 # | o---+ into | o---+ |
147 # |
139 # | / / | | | # <--- padding line |
148 # | | o | | | | o | | |
140 # o | | | / / |
149 # | | |/ / | | |/ / |
141 # o | | |
150 # | o | | into | o / / # <--- fixed nodeline tail |
142 add_padding_line = (len(text) > 2 and coldiff == -1 and |
151 # | |/ / | |/ / |
143 [x for (x, y) in edges if x + 1 < y]) |
152 # o | | o | | |
144 |
153 fix_nodeline_tail = len(text) <= 2 and not add_padding_line |
145 # fix_nodeline_tail says whether to rewrite |
154 |
146 # |
155 # nodeline is the line containing the node character (typically o) |
147 # | | o | | | | o | | |
156 nodeline = ["|", " "] * idx |
148 # | | |/ / | | |/ / |
157 nodeline.extend([char, " "]) |
149 # | o | | into | o / / # <--- fixed nodeline tail |
158 |
150 # | |/ / | |/ / |
159 nodeline.extend( |
151 # o | | o | | |
160 get_nodeline_edges_tail(idx, base[1], ncols, coldiff, |
152 fix_nodeline_tail = len(text) <= 2 and not add_padding_line |
161 base[0], fix_nodeline_tail)) |
153 |
162 |
154 # nodeline is the line containing the node character (typically o) |
163 # shift_interline is the line containing the non-vertical |
155 nodeline = ["|", " "] * idx |
164 # edges between this entry and the next |
156 nodeline.extend([char, " "]) |
165 shift_interline = ["|", " "] * idx |
157 |
166 if coldiff == -1: |
158 nodeline.extend( |
167 n_spaces = 1 |
159 get_nodeline_edges_tail(idx, base[1], ncols, coldiff, |
168 edge_ch = "/" |
160 base[0], fix_nodeline_tail)) |
169 elif coldiff == 0: |
161 |
170 n_spaces = 2 |
162 # shift_interline is the line containing the non-vertical |
171 edge_ch = "|" |
163 # edges between this entry and the next |
172 else: |
164 shift_interline = ["|", " "] * idx |
173 n_spaces = 3 |
165 if coldiff == -1: |
174 edge_ch = "\\" |
166 n_spaces = 1 |
175 shift_interline.extend(n_spaces * [" "]) |
167 edge_ch = "/" |
176 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) |
168 elif coldiff == 0: |
177 |
169 n_spaces = 2 |
178 # draw edges from the current node to its parents |
170 edge_ch = "|" |
179 draw_edges(edges, nodeline, shift_interline) |
171 else: |
180 |
172 n_spaces = 3 |
181 # lines is the list of all graph lines to print |
173 edge_ch = "\\" |
182 lines = [nodeline] |
174 shift_interline.extend(n_spaces * [" "]) |
183 if add_padding_line: |
175 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) |
184 lines.append(get_padding_line(idx, ncols, edges)) |
176 |
185 lines.append(shift_interline) |
177 # draw edges from the current node to its parents |
186 |
178 draw_edges(edges, nodeline, shift_interline) |
187 # make sure that there are as many graph lines as there are |
179 |
188 # log strings |
180 # lines is the list of all graph lines to print |
189 while len(text) < len(lines): |
181 lines = [nodeline] |
190 text.append("") |
182 if add_padding_line: |
191 if len(lines) < len(text): |
183 lines.append(get_padding_line(idx, ncols, edges)) |
192 extra_interline = ["|", " "] * (ncols + coldiff) |
184 lines.append(shift_interline) |
193 while len(lines) < len(text): |
185 |
194 lines.append(extra_interline) |
186 # make sure that there are as many graph lines as there are |
195 |
187 # log strings |
196 # print lines |
188 while len(text) < len(lines): |
197 indentation_level = max(ncols, ncols + coldiff) |
189 text.append("") |
198 for (line, logstr) in zip(lines, text): |
190 if len(lines) < len(text): |
199 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) |
191 extra_interline = ["|", " "] * (ncols + coldiff) |
200 ui.write(ln.rstrip() + '\n') |
192 while len(lines) < len(text): |
201 |
193 lines.append(extra_interline) |
202 # ... and start over |
194 |
203 base[0] = coldiff |
195 # print lines |
204 base[1] = idx |
196 indentation_level = max(ncols, ncols + coldiff) |
|
197 for (line, logstr) in zip(lines, text): |
|
198 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) |
|
199 ui.write(ln.rstrip() + '\n') |
|
200 |
|
201 # ... and start over |
|
202 base[0] = coldiff |
|
203 base[1] = idx |
205 |
204 |
206 def get_revs(repo, rev_opt): |
205 def get_revs(repo, rev_opt): |
207 if rev_opt: |
206 if rev_opt: |
208 revs = revrange(repo, rev_opt) |
207 revs = revrange(repo, rev_opt) |
209 return (max(revs), min(revs)) |
208 return (max(revs), min(revs)) |
215 "only_merges", "user", "only_branch", "prune", "newest_first", |
214 "only_merges", "user", "only_branch", "prune", "newest_first", |
216 "no_merges", "include", "exclude"]: |
215 "no_merges", "include", "exclude"]: |
217 if op in opts and opts[op]: |
216 if op in opts and opts[op]: |
218 raise util.Abort(_("--graph option is incompatible with --%s") % op) |
217 raise util.Abort(_("--graph option is incompatible with --%s") % op) |
219 |
218 |
220 def generate(dag, displayer, showparents, edgefn): |
219 def generate(ui, dag, displayer, showparents, edgefn): |
221 seen = [] |
220 seen, base = [], [0, 0] |
222 for rev, type, ctx, parents in dag: |
221 for rev, type, ctx, parents in dag: |
223 char = ctx.node() in showparents and '@' or 'o' |
222 char = ctx.node() in showparents and '@' or 'o' |
224 displayer.show(ctx) |
223 displayer.show(ctx) |
225 lines = displayer.hunk.pop(rev).split('\n')[:-1] |
224 lines = displayer.hunk.pop(rev).split('\n')[:-1] |
226 cols = edgefn(seen, rev, parents) |
225 ascii(ui, base, type, char, lines, edgefn(seen, rev, parents)) |
227 yield cols[0], type, (char, lines), cols[1], cols[2], cols[3] |
|
228 |
226 |
229 def graphlog(ui, repo, path=None, **opts): |
227 def graphlog(ui, repo, path=None, **opts): |
230 """show revision history alongside an ASCII revision graph |
228 """show revision history alongside an ASCII revision graph |
231 |
229 |
232 Print a revision history alongside a revision graph drawn with |
230 Print a revision history alongside a revision graph drawn with |
250 else: |
248 else: |
251 revdag = graphmod.revisions(repo, start, stop) |
249 revdag = graphmod.revisions(repo, start, stop) |
252 |
250 |
253 displayer = show_changeset(ui, repo, opts, buffered=True) |
251 displayer = show_changeset(ui, repo, opts, buffered=True) |
254 showparents = [ctx.node() for ctx in repo[None].parents()] |
252 showparents = [ctx.node() for ctx in repo[None].parents()] |
255 gen = generate(revdag, displayer, showparents, asciiedges) |
253 generate(ui, revdag, displayer, showparents, asciiedges) |
256 ascii(ui, gen) |
|
257 |
254 |
258 def graphrevs(repo, nodes, opts): |
255 def graphrevs(repo, nodes, opts): |
259 limit = cmdutil.loglimit(opts) |
256 limit = cmdutil.loglimit(opts) |
260 nodes.reverse() |
257 nodes.reverse() |
261 if limit < sys.maxint: |
258 if limit < sys.maxint: |
287 |
284 |
288 o = repo.changelog.nodesbetween(o, revs)[0] |
285 o = repo.changelog.nodesbetween(o, revs)[0] |
289 revdag = graphrevs(repo, o, opts) |
286 revdag = graphrevs(repo, o, opts) |
290 displayer = show_changeset(ui, repo, opts, buffered=True) |
287 displayer = show_changeset(ui, repo, opts, buffered=True) |
291 showparents = [ctx.node() for ctx in repo[None].parents()] |
288 showparents = [ctx.node() for ctx in repo[None].parents()] |
292 gen = generate(revdag, displayer, showparents, asciiedges) |
289 generate(ui, revdag, displayer, showparents, asciiedges) |
293 ascii(ui, gen) |
|
294 |
290 |
295 def gincoming(ui, repo, source="default", **opts): |
291 def gincoming(ui, repo, source="default", **opts): |
296 """show the incoming changesets alongside an ASCII revision graph |
292 """show the incoming changesets alongside an ASCII revision graph |
297 |
293 |
298 Print the incoming changesets alongside a revision graph drawn with |
294 Print the incoming changesets alongside a revision graph drawn with |