##// END OF EJS Templates
drawdag: allow override file contents via comments...
Jun Wu -
r33785:0531ffd5 default
parent child Browse files
Show More
@@ -1,363 +1,376
1 # drawdag.py - convert ASCII revision DAG to actual changesets
1 # drawdag.py - convert ASCII revision DAG to actual changesets
2 #
2 #
3 # Copyright 2016 Facebook, Inc.
3 # Copyright 2016 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """
7 """
8 create changesets from an ASCII graph for testing purpose.
8 create changesets from an ASCII graph for testing purpose.
9
9
10 For example, given the following input::
10 For example, given the following input::
11
11
12 c d
12 c d
13 |/
13 |/
14 b
14 b
15 |
15 |
16 a
16 a
17
17
18 4 changesets and 4 local tags will be created.
18 4 changesets and 4 local tags will be created.
19 `hg log -G -T "{rev} {desc} (tag: {tags})"` will output::
19 `hg log -G -T "{rev} {desc} (tag: {tags})"` will output::
20
20
21 o 3 d (tag: d tip)
21 o 3 d (tag: d tip)
22 |
22 |
23 | o 2 c (tag: c)
23 | o 2 c (tag: c)
24 |/
24 |/
25 o 1 b (tag: b)
25 o 1 b (tag: b)
26 |
26 |
27 o 0 a (tag: a)
27 o 0 a (tag: a)
28
28
29 For root nodes (nodes without parents) in the graph, they can be revsets
29 For root nodes (nodes without parents) in the graph, they can be revsets
30 pointing to existing nodes. The ASCII graph could also have disconnected
30 pointing to existing nodes. The ASCII graph could also have disconnected
31 components with same names referring to the same changeset.
31 components with same names referring to the same changeset.
32
32
33 Therefore, given the repo having the 4 changesets (and tags) above, with the
33 Therefore, given the repo having the 4 changesets (and tags) above, with the
34 following ASCII graph as input::
34 following ASCII graph as input::
35
35
36 foo bar bar foo
36 foo bar bar foo
37 | / | |
37 | / | |
38 ancestor(c,d) a baz
38 ancestor(c,d) a baz
39
39
40 The result (`hg log -G -T "{desc}"`) will look like::
40 The result (`hg log -G -T "{desc}"`) will look like::
41
41
42 o foo
42 o foo
43 |\
43 |\
44 +---o bar
44 +---o bar
45 | | |
45 | | |
46 | o | baz
46 | o | baz
47 | /
47 | /
48 +---o d
48 +---o d
49 | |
49 | |
50 +---o c
50 +---o c
51 | |
51 | |
52 o | b
52 o | b
53 |/
53 |/
54 o a
54 o a
55
55
56 Note that if you take the above `hg log` output directly as input. It will work
56 Note that if you take the above `hg log` output directly as input. It will work
57 as expected - the result would be an isomorphic graph::
57 as expected - the result would be an isomorphic graph::
58
58
59 o foo
59 o foo
60 |\
60 |\
61 | | o d
61 | | o d
62 | |/
62 | |/
63 | | o c
63 | | o c
64 | |/
64 | |/
65 | | o bar
65 | | o bar
66 | |/|
66 | |/|
67 | o | b
67 | o | b
68 | |/
68 | |/
69 o / baz
69 o / baz
70 /
70 /
71 o a
71 o a
72
72
73 This is because 'o' is specially handled in the input: instead of using 'o' as
73 This is because 'o' is specially handled in the input: instead of using 'o' as
74 the node name, the word to the right will be used.
74 the node name, the word to the right will be used.
75
75
76 Some special comments could have side effects:
76 Some special comments could have side effects:
77
77
78 - Create obsmarkers
78 - Create obsmarkers
79 # replace: A -> B -> C -> D # chained 1 to 1 replacements
79 # replace: A -> B -> C -> D # chained 1 to 1 replacements
80 # split: A -> B, C # 1 to many
80 # split: A -> B, C # 1 to many
81 # prune: A, B, C # many to nothing
81 # prune: A, B, C # many to nothing
82 """
82 """
83 from __future__ import absolute_import, print_function
83 from __future__ import absolute_import, print_function
84
84
85 import collections
85 import collections
86 import itertools
86 import itertools
87 import re
87
88
88 from mercurial.i18n import _
89 from mercurial.i18n import _
89 from mercurial import (
90 from mercurial import (
90 context,
91 context,
91 error,
92 error,
92 node,
93 node,
93 obsolete,
94 obsolete,
94 registrar,
95 registrar,
95 scmutil,
96 scmutil,
96 tags as tagsmod,
97 tags as tagsmod,
97 )
98 )
98
99
99 cmdtable = {}
100 cmdtable = {}
100 command = registrar.command(cmdtable)
101 command = registrar.command(cmdtable)
101
102
102 _pipechars = '\\/+-|'
103 _pipechars = '\\/+-|'
103 _nonpipechars = ''.join(chr(i) for i in xrange(33, 127)
104 _nonpipechars = ''.join(chr(i) for i in xrange(33, 127)
104 if chr(i) not in _pipechars)
105 if chr(i) not in _pipechars)
105
106
106 def _isname(ch):
107 def _isname(ch):
107 """char -> bool. return True if ch looks like part of a name, False
108 """char -> bool. return True if ch looks like part of a name, False
108 otherwise"""
109 otherwise"""
109 return ch in _nonpipechars
110 return ch in _nonpipechars
110
111
111 def _parseasciigraph(text):
112 def _parseasciigraph(text):
112 """str -> {str : [str]}. convert the ASCII graph to edges"""
113 """str -> {str : [str]}. convert the ASCII graph to edges"""
113 lines = text.splitlines()
114 lines = text.splitlines()
114 edges = collections.defaultdict(list) # {node: []}
115 edges = collections.defaultdict(list) # {node: []}
115
116
116 def get(y, x):
117 def get(y, x):
117 """(int, int) -> char. give a coordinate, return the char. return a
118 """(int, int) -> char. give a coordinate, return the char. return a
118 space for anything out of range"""
119 space for anything out of range"""
119 if x < 0 or y < 0:
120 if x < 0 or y < 0:
120 return ' '
121 return ' '
121 try:
122 try:
122 return lines[y][x]
123 return lines[y][x]
123 except IndexError:
124 except IndexError:
124 return ' '
125 return ' '
125
126
126 def getname(y, x):
127 def getname(y, x):
127 """(int, int) -> str. like get(y, x) but concatenate left and right
128 """(int, int) -> str. like get(y, x) but concatenate left and right
128 parts. if name is an 'o', try to replace it to the right"""
129 parts. if name is an 'o', try to replace it to the right"""
129 result = ''
130 result = ''
130 for i in itertools.count(0):
131 for i in itertools.count(0):
131 ch = get(y, x - i)
132 ch = get(y, x - i)
132 if not _isname(ch):
133 if not _isname(ch):
133 break
134 break
134 result = ch + result
135 result = ch + result
135 for i in itertools.count(1):
136 for i in itertools.count(1):
136 ch = get(y, x + i)
137 ch = get(y, x + i)
137 if not _isname(ch):
138 if not _isname(ch):
138 break
139 break
139 result += ch
140 result += ch
140 if result == 'o':
141 if result == 'o':
141 # special handling, find the name to the right
142 # special handling, find the name to the right
142 result = ''
143 result = ''
143 for i in itertools.count(2):
144 for i in itertools.count(2):
144 ch = get(y, x + i)
145 ch = get(y, x + i)
145 if ch == ' ' or ch in _pipechars:
146 if ch == ' ' or ch in _pipechars:
146 if result or x + i >= len(lines[y]):
147 if result or x + i >= len(lines[y]):
147 break
148 break
148 else:
149 else:
149 result += ch
150 result += ch
150 return result or 'o'
151 return result or 'o'
151 return result
152 return result
152
153
153 def parents(y, x):
154 def parents(y, x):
154 """(int, int) -> [str]. follow the ASCII edges at given position,
155 """(int, int) -> [str]. follow the ASCII edges at given position,
155 return a list of parents"""
156 return a list of parents"""
156 visited = {(y, x)}
157 visited = {(y, x)}
157 visit = []
158 visit = []
158 result = []
159 result = []
159
160
160 def follow(y, x, expected):
161 def follow(y, x, expected):
161 """conditionally append (y, x) to visit array, if it's a char
162 """conditionally append (y, x) to visit array, if it's a char
162 in excepted. 'o' in expected means an '_isname' test.
163 in excepted. 'o' in expected means an '_isname' test.
163 if '-' (or '+') is not in excepted, and get(y, x) is '-' (or '+'),
164 if '-' (or '+') is not in excepted, and get(y, x) is '-' (or '+'),
164 the next line (y + 1, x) will be checked instead."""
165 the next line (y + 1, x) will be checked instead."""
165 ch = get(y, x)
166 ch = get(y, x)
166 if any(ch == c and c not in expected for c in '-+'):
167 if any(ch == c and c not in expected for c in '-+'):
167 y += 1
168 y += 1
168 return follow(y + 1, x, expected)
169 return follow(y + 1, x, expected)
169 if ch in expected or ('o' in expected and _isname(ch)):
170 if ch in expected or ('o' in expected and _isname(ch)):
170 visit.append((y, x))
171 visit.append((y, x))
171
172
172 # -o- # starting point:
173 # -o- # starting point:
173 # /|\ # follow '-' (horizontally), and '/|\' (to the bottom)
174 # /|\ # follow '-' (horizontally), and '/|\' (to the bottom)
174 follow(y + 1, x, '|')
175 follow(y + 1, x, '|')
175 follow(y + 1, x - 1, '/')
176 follow(y + 1, x - 1, '/')
176 follow(y + 1, x + 1, '\\')
177 follow(y + 1, x + 1, '\\')
177 follow(y, x - 1, '-')
178 follow(y, x - 1, '-')
178 follow(y, x + 1, '-')
179 follow(y, x + 1, '-')
179
180
180 while visit:
181 while visit:
181 y, x = visit.pop()
182 y, x = visit.pop()
182 if (y, x) in visited:
183 if (y, x) in visited:
183 continue
184 continue
184 visited.add((y, x))
185 visited.add((y, x))
185 ch = get(y, x)
186 ch = get(y, x)
186 if _isname(ch):
187 if _isname(ch):
187 result.append(getname(y, x))
188 result.append(getname(y, x))
188 continue
189 continue
189 elif ch == '|':
190 elif ch == '|':
190 follow(y + 1, x, '/|o')
191 follow(y + 1, x, '/|o')
191 follow(y + 1, x - 1, '/')
192 follow(y + 1, x - 1, '/')
192 follow(y + 1, x + 1, '\\')
193 follow(y + 1, x + 1, '\\')
193 elif ch == '+':
194 elif ch == '+':
194 follow(y, x - 1, '-')
195 follow(y, x - 1, '-')
195 follow(y, x + 1, '-')
196 follow(y, x + 1, '-')
196 follow(y + 1, x - 1, '/')
197 follow(y + 1, x - 1, '/')
197 follow(y + 1, x + 1, '\\')
198 follow(y + 1, x + 1, '\\')
198 follow(y + 1, x, '|')
199 follow(y + 1, x, '|')
199 elif ch == '\\':
200 elif ch == '\\':
200 follow(y + 1, x + 1, '\\|o')
201 follow(y + 1, x + 1, '\\|o')
201 elif ch == '/':
202 elif ch == '/':
202 follow(y + 1, x - 1, '/|o')
203 follow(y + 1, x - 1, '/|o')
203 elif ch == '-':
204 elif ch == '-':
204 follow(y, x - 1, '-+o')
205 follow(y, x - 1, '-+o')
205 follow(y, x + 1, '-+o')
206 follow(y, x + 1, '-+o')
206 return result
207 return result
207
208
208 for y, line in enumerate(lines):
209 for y, line in enumerate(lines):
209 for x, ch in enumerate(line):
210 for x, ch in enumerate(line):
210 if ch == '#': # comment
211 if ch == '#': # comment
211 break
212 break
212 if _isname(ch):
213 if _isname(ch):
213 edges[getname(y, x)] += parents(y, x)
214 edges[getname(y, x)] += parents(y, x)
214
215
215 return dict(edges)
216 return dict(edges)
216
217
217 class simplefilectx(object):
218 class simplefilectx(object):
218 def __init__(self, path, data):
219 def __init__(self, path, data):
219 self._data = data
220 self._data = data
220 self._path = path
221 self._path = path
221
222
222 def data(self):
223 def data(self):
223 return self._data
224 return self._data
224
225
225 def filenode(self):
226 def filenode(self):
226 return None
227 return None
227
228
228 def path(self):
229 def path(self):
229 return self._path
230 return self._path
230
231
231 def renamed(self):
232 def renamed(self):
232 return None
233 return None
233
234
234 def flags(self):
235 def flags(self):
235 return ''
236 return ''
236
237
237 class simplecommitctx(context.committablectx):
238 class simplecommitctx(context.committablectx):
238 def __init__(self, repo, name, parentctxs, added):
239 def __init__(self, repo, name, parentctxs, added):
239 opts = {
240 opts = {
240 'changes': scmutil.status([], list(added), [], [], [], [], []),
241 'changes': scmutil.status([], list(added), [], [], [], [], []),
241 'date': '0 0',
242 'date': '0 0',
242 'extra': {'branch': 'default'},
243 'extra': {'branch': 'default'},
243 }
244 }
244 super(simplecommitctx, self).__init__(self, name, **opts)
245 super(simplecommitctx, self).__init__(self, name, **opts)
245 self._repo = repo
246 self._repo = repo
246 self._added = added
247 self._added = added
247 self._parents = parentctxs
248 self._parents = parentctxs
248 while len(self._parents) < 2:
249 while len(self._parents) < 2:
249 self._parents.append(repo[node.nullid])
250 self._parents.append(repo[node.nullid])
250
251
251 def filectx(self, key):
252 def filectx(self, key):
252 return simplefilectx(key, self._added[key])
253 return simplefilectx(key, self._added[key])
253
254
254 def commit(self):
255 def commit(self):
255 return self._repo.commitctx(self)
256 return self._repo.commitctx(self)
256
257
257 def _walkgraph(edges):
258 def _walkgraph(edges):
258 """yield node, parents in topologically order"""
259 """yield node, parents in topologically order"""
259 visible = set(edges.keys())
260 visible = set(edges.keys())
260 remaining = {} # {str: [str]}
261 remaining = {} # {str: [str]}
261 for k, vs in edges.iteritems():
262 for k, vs in edges.iteritems():
262 for v in vs:
263 for v in vs:
263 if v not in remaining:
264 if v not in remaining:
264 remaining[v] = []
265 remaining[v] = []
265 remaining[k] = vs[:]
266 remaining[k] = vs[:]
266 while remaining:
267 while remaining:
267 leafs = [k for k, v in remaining.items() if not v]
268 leafs = [k for k, v in remaining.items() if not v]
268 if not leafs:
269 if not leafs:
269 raise error.Abort(_('the graph has cycles'))
270 raise error.Abort(_('the graph has cycles'))
270 for leaf in sorted(leafs):
271 for leaf in sorted(leafs):
271 if leaf in visible:
272 if leaf in visible:
272 yield leaf, edges[leaf]
273 yield leaf, edges[leaf]
273 del remaining[leaf]
274 del remaining[leaf]
274 for k, v in remaining.iteritems():
275 for k, v in remaining.iteritems():
275 if leaf in v:
276 if leaf in v:
276 v.remove(leaf)
277 v.remove(leaf)
277
278
279 def _getcomments(text):
280 for line in text.splitlines():
281 if ' # ' not in line:
282 continue
283 yield line.split(' # ', 1)[1].split(' # ')[0].strip()
284
278 @command('debugdrawdag', [])
285 @command('debugdrawdag', [])
279 def debugdrawdag(ui, repo, **opts):
286 def debugdrawdag(ui, repo, **opts):
280 """read an ASCII graph from stdin and create changesets
287 """read an ASCII graph from stdin and create changesets
281
288
282 The ASCII graph is like what :hg:`log -G` outputs, with each `o` replaced
289 The ASCII graph is like what :hg:`log -G` outputs, with each `o` replaced
283 to the name of the node. The command will create dummy changesets and local
290 to the name of the node. The command will create dummy changesets and local
284 tags with those names to make the dummy changesets easier to be referred
291 tags with those names to make the dummy changesets easier to be referred
285 to.
292 to.
286
293
287 If the name of a node is a single character 'o', It will be replaced by the
294 If the name of a node is a single character 'o', It will be replaced by the
288 word to the right. This makes it easier to reuse
295 word to the right. This makes it easier to reuse
289 :hg:`log -G -T '{desc}'` outputs.
296 :hg:`log -G -T '{desc}'` outputs.
290
297
291 For root (no parents) nodes, revset can be used to query existing repo.
298 For root (no parents) nodes, revset can be used to query existing repo.
292 Note that the revset cannot have confusing characters which can be seen as
299 Note that the revset cannot have confusing characters which can be seen as
293 the part of the graph edges, like `|/+-\`.
300 the part of the graph edges, like `|/+-\`.
294 """
301 """
295 text = ui.fin.read()
302 text = ui.fin.read()
296
303
297 # parse the graph and make sure len(parents) <= 2 for each node
304 # parse the graph and make sure len(parents) <= 2 for each node
298 edges = _parseasciigraph(text)
305 edges = _parseasciigraph(text)
299 for k, v in edges.iteritems():
306 for k, v in edges.iteritems():
300 if len(v) > 2:
307 if len(v) > 2:
301 raise error.Abort(_('%s: too many parents: %s')
308 raise error.Abort(_('%s: too many parents: %s')
302 % (k, ' '.join(v)))
309 % (k, ' '.join(v)))
303
310
311 # parse comments to get extra file content instructions
312 files = collections.defaultdict(dict) # {(name, path): content}
313 comments = list(_getcomments(text))
314 filere = re.compile(r'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M)
315 for name, path, content in filere.findall('\n'.join(comments)):
316 files[name][path] = content.replace(r'\n', '\n')
317
304 committed = {None: node.nullid} # {name: node}
318 committed = {None: node.nullid} # {name: node}
305
319
306 # for leaf nodes, try to find existing nodes in repo
320 # for leaf nodes, try to find existing nodes in repo
307 for name, parents in edges.iteritems():
321 for name, parents in edges.iteritems():
308 if len(parents) == 0:
322 if len(parents) == 0:
309 try:
323 try:
310 committed[name] = scmutil.revsingle(repo, name)
324 committed[name] = scmutil.revsingle(repo, name)
311 except error.RepoLookupError:
325 except error.RepoLookupError:
312 pass
326 pass
313
327
314 # commit in topological order
328 # commit in topological order
315 for name, parents in _walkgraph(edges):
329 for name, parents in _walkgraph(edges):
316 if name in committed:
330 if name in committed:
317 continue
331 continue
318 pctxs = [repo[committed[n]] for n in parents]
332 pctxs = [repo[committed[n]] for n in parents]
319 pctxs.sort(key=lambda c: c.node())
333 pctxs.sort(key=lambda c: c.node())
320 added = {}
334 added = {}
321 if len(parents) > 1:
335 if len(parents) > 1:
322 # If it's a merge, take the files and contents from the parents
336 # If it's a merge, take the files and contents from the parents
323 for f in pctxs[1].manifest():
337 for f in pctxs[1].manifest():
324 if f not in pctxs[0].manifest():
338 if f not in pctxs[0].manifest():
325 added[f] = pctxs[1][f].data()
339 added[f] = pctxs[1][f].data()
326 else:
340 else:
327 # If it's not a merge, add a single file
341 # If it's not a merge, add a single file
328 added[name] = name
342 added[name] = name
343 # add extra file contents in comments
344 for path, content in files.get(name, {}).items():
345 added[path] = content
329 ctx = simplecommitctx(repo, name, pctxs, added)
346 ctx = simplecommitctx(repo, name, pctxs, added)
330 n = ctx.commit()
347 n = ctx.commit()
331 committed[name] = n
348 committed[name] = n
332 tagsmod.tag(repo, name, n, message=None, user=None, date=None,
349 tagsmod.tag(repo, name, n, message=None, user=None, date=None,
333 local=True)
350 local=True)
334
351
335 # handle special comments
352 # handle special comments
336 with repo.wlock(), repo.lock(), repo.transaction('drawdag'):
353 with repo.wlock(), repo.lock(), repo.transaction('drawdag'):
337 getctx = lambda x: repo.unfiltered()[committed[x.strip()]]
354 getctx = lambda x: repo.unfiltered()[committed[x.strip()]]
338 for line in text.splitlines():
355 for comment in comments:
339 if ' # ' not in line:
340 continue
341
342 rels = [] # obsolete relationships
356 rels = [] # obsolete relationships
343 comment = line.split(' # ', 1)[1].split(' # ')[0].strip()
344 args = comment.split(':', 1)
357 args = comment.split(':', 1)
345 if len(args) <= 1:
358 if len(args) <= 1:
346 continue
359 continue
347
360
348 cmd = args[0].strip()
361 cmd = args[0].strip()
349 arg = args[1].strip()
362 arg = args[1].strip()
350
363
351 if cmd in ('replace', 'rebase', 'amend'):
364 if cmd in ('replace', 'rebase', 'amend'):
352 nodes = [getctx(m) for m in arg.split('->')]
365 nodes = [getctx(m) for m in arg.split('->')]
353 for i in range(len(nodes) - 1):
366 for i in range(len(nodes) - 1):
354 rels.append((nodes[i], (nodes[i + 1],)))
367 rels.append((nodes[i], (nodes[i + 1],)))
355 elif cmd in ('split',):
368 elif cmd in ('split',):
356 pre, succs = arg.split('->')
369 pre, succs = arg.split('->')
357 succs = succs.split(',')
370 succs = succs.split(',')
358 rels.append((getctx(pre), (getctx(s) for s in succs)))
371 rels.append((getctx(pre), (getctx(s) for s in succs)))
359 elif cmd in ('prune',):
372 elif cmd in ('prune',):
360 for n in arg.split(','):
373 for n in arg.split(','):
361 rels.append((getctx(n), ()))
374 rels.append((getctx(n), ()))
362 if rels:
375 if rels:
363 obsolete.createmarkers(repo, rels, date=(0, 0), operation=cmd)
376 obsolete.createmarkers(repo, rels, date=(0, 0), operation=cmd)
@@ -1,234 +1,272
1 $ cat >> $HGRCPATH<<EOF
1 $ cat >> $HGRCPATH<<EOF
2 > [extensions]
2 > [extensions]
3 > drawdag=$TESTDIR/drawdag.py
3 > drawdag=$TESTDIR/drawdag.py
4 > [experimental]
4 > [experimental]
5 > stabilization=all
5 > stabilization=all
6 > EOF
6 > EOF
7
7
8 $ reinit () {
8 $ reinit () {
9 > rm -rf .hg && hg init
9 > rm -rf .hg && hg init
10 > }
10 > }
11
11
12 $ hg init
12 $ hg init
13
13
14 Test what said in drawdag.py docstring
14 Test what said in drawdag.py docstring
15
15
16 $ hg debugdrawdag <<'EOS'
16 $ hg debugdrawdag <<'EOS'
17 > c d
17 > c d
18 > |/
18 > |/
19 > b
19 > b
20 > |
20 > |
21 > a
21 > a
22 > EOS
22 > EOS
23
23
24 $ hg log -G -T '{rev} {desc} ({tags})'
24 $ hg log -G -T '{rev} {desc} ({tags})'
25 o 3 d (d tip)
25 o 3 d (d tip)
26 |
26 |
27 | o 2 c (c)
27 | o 2 c (c)
28 |/
28 |/
29 o 1 b (b)
29 o 1 b (b)
30 |
30 |
31 o 0 a (a)
31 o 0 a (a)
32
32
33 $ hg debugdrawdag <<'EOS'
33 $ hg debugdrawdag <<'EOS'
34 > foo bar bar foo
34 > foo bar bar foo
35 > | / | |
35 > | / | |
36 > ancestor(c,d) a baz
36 > ancestor(c,d) a baz
37 > EOS
37 > EOS
38
38
39 $ hg log -G -T '{desc}'
39 $ hg log -G -T '{desc}'
40 o foo
40 o foo
41 |\
41 |\
42 +---o bar
42 +---o bar
43 | | |
43 | | |
44 | o | baz
44 | o | baz
45 | /
45 | /
46 +---o d
46 +---o d
47 | |
47 | |
48 +---o c
48 +---o c
49 | |
49 | |
50 o | b
50 o | b
51 |/
51 |/
52 o a
52 o a
53
53
54 $ reinit
54 $ reinit
55
55
56 $ hg debugdrawdag <<'EOS'
56 $ hg debugdrawdag <<'EOS'
57 > o foo
57 > o foo
58 > |\
58 > |\
59 > +---o bar
59 > +---o bar
60 > | | |
60 > | | |
61 > | o | baz
61 > | o | baz
62 > | /
62 > | /
63 > +---o d
63 > +---o d
64 > | |
64 > | |
65 > +---o c
65 > +---o c
66 > | |
66 > | |
67 > o | b
67 > o | b
68 > |/
68 > |/
69 > o a
69 > o a
70 > EOS
70 > EOS
71
71
72 $ hg log -G -T '{desc}'
72 $ hg log -G -T '{desc}'
73 o foo
73 o foo
74 |\
74 |\
75 | | o d
75 | | o d
76 | |/
76 | |/
77 | | o c
77 | | o c
78 | |/
78 | |/
79 | | o bar
79 | | o bar
80 | |/|
80 | |/|
81 | o | b
81 | o | b
82 | |/
82 | |/
83 o / baz
83 o / baz
84 /
84 /
85 o a
85 o a
86
86
87 $ reinit
87 $ reinit
88
88
89 $ hg debugdrawdag <<'EOS'
89 $ hg debugdrawdag <<'EOS'
90 > o foo
90 > o foo
91 > |\
91 > |\
92 > | | o d
92 > | | o d
93 > | |/
93 > | |/
94 > | | o c
94 > | | o c
95 > | |/
95 > | |/
96 > | | o bar
96 > | | o bar
97 > | |/|
97 > | |/|
98 > | o | b
98 > | o | b
99 > | |/
99 > | |/
100 > o / baz
100 > o / baz
101 > /
101 > /
102 > o a
102 > o a
103 > EOS
103 > EOS
104
104
105 $ hg log -G -T '{desc}'
105 $ hg log -G -T '{desc}'
106 o foo
106 o foo
107 |\
107 |\
108 | | o d
108 | | o d
109 | |/
109 | |/
110 | | o c
110 | | o c
111 | |/
111 | |/
112 | | o bar
112 | | o bar
113 | |/|
113 | |/|
114 | o | b
114 | o | b
115 | |/
115 | |/
116 o / baz
116 o / baz
117 /
117 /
118 o a
118 o a
119
119
120 $ hg manifest -r a
120 $ hg manifest -r a
121 a
121 a
122 $ hg manifest -r b
122 $ hg manifest -r b
123 a
123 a
124 b
124 b
125 $ hg manifest -r bar
125 $ hg manifest -r bar
126 a
126 a
127 b
127 b
128 $ hg manifest -r foo
128 $ hg manifest -r foo
129 a
129 a
130 b
130 b
131 baz
131 baz
132
132
133 Edges existed in repo are no-ops
133 Edges existed in repo are no-ops
134
134
135 $ reinit
135 $ reinit
136 $ hg debugdrawdag <<'EOS'
136 $ hg debugdrawdag <<'EOS'
137 > B C C
137 > B C C
138 > | | |
138 > | | |
139 > A A B
139 > A A B
140 > EOS
140 > EOS
141
141
142 $ hg log -G -T '{desc}'
142 $ hg log -G -T '{desc}'
143 o C
143 o C
144 |\
144 |\
145 | o B
145 | o B
146 |/
146 |/
147 o A
147 o A
148
148
149
149
150 $ hg debugdrawdag <<'EOS'
150 $ hg debugdrawdag <<'EOS'
151 > C D C
151 > C D C
152 > | | |
152 > | | |
153 > B B A
153 > B B A
154 > EOS
154 > EOS
155
155
156 $ hg log -G -T '{desc}'
156 $ hg log -G -T '{desc}'
157 o D
157 o D
158 |
158 |
159 | o C
159 | o C
160 |/|
160 |/|
161 o | B
161 o | B
162 |/
162 |/
163 o A
163 o A
164
164
165
165
166 Node with more than 2 parents are disallowed
166 Node with more than 2 parents are disallowed
167
167
168 $ hg debugdrawdag <<'EOS'
168 $ hg debugdrawdag <<'EOS'
169 > A
169 > A
170 > /|\
170 > /|\
171 > D B C
171 > D B C
172 > EOS
172 > EOS
173 abort: A: too many parents: C D B
173 abort: A: too many parents: C D B
174 [255]
174 [255]
175
175
176 Cycles are disallowed
176 Cycles are disallowed
177
177
178 $ hg debugdrawdag <<'EOS'
178 $ hg debugdrawdag <<'EOS'
179 > A
179 > A
180 > |
180 > |
181 > A
181 > A
182 > EOS
182 > EOS
183 abort: the graph has cycles
183 abort: the graph has cycles
184 [255]
184 [255]
185
185
186 $ hg debugdrawdag <<'EOS'
186 $ hg debugdrawdag <<'EOS'
187 > A
187 > A
188 > |
188 > |
189 > B
189 > B
190 > |
190 > |
191 > A
191 > A
192 > EOS
192 > EOS
193 abort: the graph has cycles
193 abort: the graph has cycles
194 [255]
194 [255]
195
195
196 Create obsmarkers via comments
196 Create obsmarkers via comments
197
197
198 $ reinit
198 $ reinit
199
199
200 $ hg debugdrawdag <<'EOS'
200 $ hg debugdrawdag <<'EOS'
201 > G
201 > G
202 > |
202 > |
203 > I D C F # split: B -> E, F, G
203 > I D C F # split: B -> E, F, G
204 > \ \| | # replace: C -> D -> H
204 > \ \| | # replace: C -> D -> H
205 > H B E # prune: F, I
205 > H B E # prune: F, I
206 > \|/
206 > \|/
207 > A
207 > A
208 > EOS
208 > EOS
209
209
210 $ hg log -r 'sort(all(), topo)' -G --hidden -T '{desc} {node}'
210 $ hg log -r 'sort(all(), topo)' -G --hidden -T '{desc} {node}'
211 o G 711f53bbef0bebd12eb6f0511d5e2e998b984846
211 o G 711f53bbef0bebd12eb6f0511d5e2e998b984846
212 |
212 |
213 x F 64a8289d249234b9886244d379f15e6b650b28e3
213 x F 64a8289d249234b9886244d379f15e6b650b28e3
214 |
214 |
215 o E 7fb047a69f220c21711122dfd94305a9efb60cba
215 o E 7fb047a69f220c21711122dfd94305a9efb60cba
216 |
216 |
217 | x D be0ef73c17ade3fc89dc41701eb9fc3a91b58282
217 | x D be0ef73c17ade3fc89dc41701eb9fc3a91b58282
218 | |
218 | |
219 | | x C 26805aba1e600a82e93661149f2313866a221a7b
219 | | x C 26805aba1e600a82e93661149f2313866a221a7b
220 | |/
220 | |/
221 | x B 112478962961147124edd43549aedd1a335e44bf
221 | x B 112478962961147124edd43549aedd1a335e44bf
222 |/
222 |/
223 | x I 58e6b987bf7045fcd9c54f496396ca1d1fc81047
223 | x I 58e6b987bf7045fcd9c54f496396ca1d1fc81047
224 | |
224 | |
225 | o H 575c4b5ec114d64b681d33f8792853568bfb2b2c
225 | o H 575c4b5ec114d64b681d33f8792853568bfb2b2c
226 |/
226 |/
227 o A 426bada5c67598ca65036d57d9e4b64b0c1ce7a0
227 o A 426bada5c67598ca65036d57d9e4b64b0c1ce7a0
228
228
229 $ hg debugobsolete
229 $ hg debugobsolete
230 112478962961147124edd43549aedd1a335e44bf 7fb047a69f220c21711122dfd94305a9efb60cba 64a8289d249234b9886244d379f15e6b650b28e3 711f53bbef0bebd12eb6f0511d5e2e998b984846 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
230 112478962961147124edd43549aedd1a335e44bf 7fb047a69f220c21711122dfd94305a9efb60cba 64a8289d249234b9886244d379f15e6b650b28e3 711f53bbef0bebd12eb6f0511d5e2e998b984846 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
231 26805aba1e600a82e93661149f2313866a221a7b be0ef73c17ade3fc89dc41701eb9fc3a91b58282 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
231 26805aba1e600a82e93661149f2313866a221a7b be0ef73c17ade3fc89dc41701eb9fc3a91b58282 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
232 be0ef73c17ade3fc89dc41701eb9fc3a91b58282 575c4b5ec114d64b681d33f8792853568bfb2b2c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
232 be0ef73c17ade3fc89dc41701eb9fc3a91b58282 575c4b5ec114d64b681d33f8792853568bfb2b2c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
233 64a8289d249234b9886244d379f15e6b650b28e3 0 {7fb047a69f220c21711122dfd94305a9efb60cba} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
233 64a8289d249234b9886244d379f15e6b650b28e3 0 {7fb047a69f220c21711122dfd94305a9efb60cba} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
234 58e6b987bf7045fcd9c54f496396ca1d1fc81047 0 {575c4b5ec114d64b681d33f8792853568bfb2b2c} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
234 58e6b987bf7045fcd9c54f496396ca1d1fc81047 0 {575c4b5ec114d64b681d33f8792853568bfb2b2c} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
235
236 Change file contents via comments
237
238 $ reinit
239 $ hg debugdrawdag <<'EOS'
240 > C # A/dir1/a = 1\n2
241 > |\ # B/dir2/b = 34
242 > A B # C/dir1/c = 5
243 > # C/dir2/c = 6
244 > # C/A = a
245 > # C/B = b
246 > EOS
247
248 $ hg log -G -T '{desc} {files}'
249 o C A B dir1/c dir2/c
250 |\
251 | o B B dir2/b
252 |
253 o A A dir1/a
254
255 $ for f in `hg files -r C`; do
256 > echo FILE "$f"
257 > hg cat -r C "$f"
258 > echo
259 > done
260 FILE A
261 a
262 FILE B
263 b
264 FILE dir1/a
265 1
266 2
267 FILE dir1/c
268 5
269 FILE dir2/b
270 34
271 FILE dir2/c
272 6
General Comments 0
You need to be logged in to leave comments. Login now