##// END OF EJS Templates
minirst: improved support for option lists....
Erik Zielke -
r13011:4936a04b default
parent child Browse files
Show More
@@ -1,434 +1,482 b''
1 1 # minirst.py - minimal reStructuredText parser
2 2 #
3 3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """simplified reStructuredText parser.
9 9
10 10 This parser knows just enough about reStructuredText to parse the
11 11 Mercurial docstrings.
12 12
13 13 It cheats in a major way: nested blocks are not really nested. They
14 14 are just indented blocks that look like they are nested. This relies
15 15 on the user to keep the right indentation for the blocks.
16 16
17 17 Remember to update http://mercurial.selenic.com/wiki/HelpStyleGuide
18 18 when adding support for new constructs.
19 19 """
20 20
21 21 import re, sys
22 22 import util, encoding
23 23 from i18n import _
24 24
25 25
26 26 def replace(text, substs):
27 27 utext = text.decode(encoding.encoding)
28 28 for f, t in substs:
29 29 utext = utext.replace(f, t)
30 30 return utext.encode(encoding.encoding)
31 31
32 32
33 33 _blockre = re.compile(r"\n(?:\s*\n)+")
34 34
35 35 def findblocks(text):
36 36 """Find continuous blocks of lines in text.
37 37
38 38 Returns a list of dictionaries representing the blocks. Each block
39 39 has an 'indent' field and a 'lines' field.
40 40 """
41 41 blocks = []
42 42 for b in _blockre.split(text.strip()):
43 43 lines = b.splitlines()
44 44 indent = min((len(l) - len(l.lstrip())) for l in lines)
45 45 lines = [l[indent:] for l in lines]
46 46 blocks.append(dict(indent=indent, lines=lines))
47 47 return blocks
48 48
49 49
50 50 def findliteralblocks(blocks):
51 51 """Finds literal blocks and adds a 'type' field to the blocks.
52 52
53 53 Literal blocks are given the type 'literal', all other blocks are
54 54 given type the 'paragraph'.
55 55 """
56 56 i = 0
57 57 while i < len(blocks):
58 58 # Searching for a block that looks like this:
59 59 #
60 60 # +------------------------------+
61 61 # | paragraph |
62 62 # | (ends with "::") |
63 63 # +------------------------------+
64 64 # +---------------------------+
65 65 # | indented literal block |
66 66 # +---------------------------+
67 67 blocks[i]['type'] = 'paragraph'
68 68 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
69 69 indent = blocks[i]['indent']
70 70 adjustment = blocks[i + 1]['indent'] - indent
71 71
72 72 if blocks[i]['lines'] == ['::']:
73 73 # Expanded form: remove block
74 74 del blocks[i]
75 75 i -= 1
76 76 elif blocks[i]['lines'][-1].endswith(' ::'):
77 77 # Partially minimized form: remove space and both
78 78 # colons.
79 79 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
80 80 else:
81 81 # Fully minimized form: remove just one colon.
82 82 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
83 83
84 84 # List items are formatted with a hanging indent. We must
85 85 # correct for this here while we still have the original
86 86 # information on the indentation of the subsequent literal
87 87 # blocks available.
88 88 m = _bulletre.match(blocks[i]['lines'][0])
89 89 if m:
90 90 indent += m.end()
91 91 adjustment -= m.end()
92 92
93 93 # Mark the following indented blocks.
94 94 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
95 95 blocks[i + 1]['type'] = 'literal'
96 96 blocks[i + 1]['indent'] -= adjustment
97 97 i += 1
98 98 i += 1
99 99 return blocks
100 100
101 101 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
102 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
102 _optionre = re.compile(r'^(-([a-zA-Z0-9]), )?(--[a-z0-9-]+)'
103 r'((.*) +)(.*)$')
103 104 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
104 105 _definitionre = re.compile(r'[^ ]')
105 106
106 107 def splitparagraphs(blocks):
107 108 """Split paragraphs into lists."""
108 109 # Tuples with (list type, item regexp, single line items?). Order
109 110 # matters: definition lists has the least specific regexp and must
110 111 # come last.
111 112 listtypes = [('bullet', _bulletre, True),
112 113 ('option', _optionre, True),
113 114 ('field', _fieldre, True),
114 115 ('definition', _definitionre, False)]
115 116
116 117 def match(lines, i, itemre, singleline):
117 118 """Does itemre match an item at line i?
118 119
119 120 A list item can be followed by an idented line or another list
120 121 item (but only if singleline is True).
121 122 """
122 123 line1 = lines[i]
123 124 line2 = i + 1 < len(lines) and lines[i + 1] or ''
124 125 if not itemre.match(line1):
125 126 return False
126 127 if singleline:
127 128 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
128 129 else:
129 130 return line2.startswith(' ')
130 131
131 132 i = 0
132 133 while i < len(blocks):
133 134 if blocks[i]['type'] == 'paragraph':
134 135 lines = blocks[i]['lines']
135 136 for type, itemre, singleline in listtypes:
136 137 if match(lines, 0, itemre, singleline):
137 138 items = []
138 139 for j, line in enumerate(lines):
139 140 if match(lines, j, itemre, singleline):
140 141 items.append(dict(type=type, lines=[],
141 142 indent=blocks[i]['indent']))
142 143 items[-1]['lines'].append(line)
143 144 blocks[i:i + 1] = items
144 145 break
145 146 i += 1
146 147 return blocks
147 148
148 149
149 150 _fieldwidth = 12
150 151
151 152 def updatefieldlists(blocks):
152 153 """Find key and maximum key width for field lists."""
153 154 i = 0
154 155 while i < len(blocks):
155 156 if blocks[i]['type'] != 'field':
156 157 i += 1
157 158 continue
158 159
159 160 keywidth = 0
160 161 j = i
161 162 while j < len(blocks) and blocks[j]['type'] == 'field':
162 163 m = _fieldre.match(blocks[j]['lines'][0])
163 164 key, rest = m.groups()
164 165 blocks[j]['lines'][0] = rest
165 166 blocks[j]['key'] = key
166 167 keywidth = max(keywidth, len(key))
167 168 j += 1
168 169
169 170 for block in blocks[i:j]:
170 171 block['keywidth'] = keywidth
171 172 i = j + 1
172 173
173 174 return blocks
174 175
175 176
177 def updateoptionlists(blocks):
178 i = 0
179 while i < len(blocks):
180 if blocks[i]['type'] != 'option':
181 i += 1
182 continue
183
184 optstrwidth = 0
185 j = i
186 while j < len(blocks) and blocks[j]['type'] == 'option':
187 m = _optionre.match(blocks[j]['lines'][0])
188
189 shortoption = m.group(2)
190 group3 = m.group(3)
191 longoption = group3[2:].strip()
192 desc = m.group(6).strip()
193 longoptionarg = m.group(5).strip()
194 blocks[j]['lines'][0] = desc
195
196 noshortop = ''
197 if not shortoption:
198 noshortop = ' '
199
200 opt = "%s%s" % (shortoption and "-%s " % shortoption or '',
201 ("%s--%s %s") % (noshortop, longoption,
202 longoptionarg))
203 opt = opt.rstrip()
204 blocks[j]['optstr'] = opt
205 optstrwidth = max(optstrwidth, encoding.colwidth(opt))
206 j += 1
207
208 for block in blocks[i:j]:
209 block['optstrwidth'] = optstrwidth
210 i = j + 1
211 return blocks
212
176 213 def prunecontainers(blocks, keep):
177 214 """Prune unwanted containers.
178 215
179 216 The blocks must have a 'type' field, i.e., they should have been
180 217 run through findliteralblocks first.
181 218 """
182 219 pruned = []
183 220 i = 0
184 221 while i + 1 < len(blocks):
185 222 # Searching for a block that looks like this:
186 223 #
187 224 # +-------+---------------------------+
188 225 # | ".. container ::" type |
189 226 # +---+ |
190 227 # | blocks |
191 228 # +-------------------------------+
192 229 if (blocks[i]['type'] == 'paragraph' and
193 230 blocks[i]['lines'][0].startswith('.. container::')):
194 231 indent = blocks[i]['indent']
195 232 adjustment = blocks[i + 1]['indent'] - indent
196 233 containertype = blocks[i]['lines'][0][15:]
197 234 prune = containertype not in keep
198 235 if prune:
199 236 pruned.append(containertype)
200 237
201 238 # Always delete "..container:: type" block
202 239 del blocks[i]
203 240 j = i
204 241 while j < len(blocks) and blocks[j]['indent'] > indent:
205 242 if prune:
206 243 del blocks[j]
207 244 i -= 1 # adjust outer index
208 245 else:
209 246 blocks[j]['indent'] -= adjustment
210 247 j += 1
211 248 i += 1
212 249 return blocks, pruned
213 250
214 251
215 252 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
216 253
217 254 def findsections(blocks):
218 255 """Finds sections.
219 256
220 257 The blocks must have a 'type' field, i.e., they should have been
221 258 run through findliteralblocks first.
222 259 """
223 260 for block in blocks:
224 261 # Searching for a block that looks like this:
225 262 #
226 263 # +------------------------------+
227 264 # | Section title |
228 265 # | ------------- |
229 266 # +------------------------------+
230 267 if (block['type'] == 'paragraph' and
231 268 len(block['lines']) == 2 and
232 269 encoding.colwidth(block['lines'][0]) == len(block['lines'][1]) and
233 270 _sectionre.match(block['lines'][1])):
234 271 block['underline'] = block['lines'][1][0]
235 272 block['type'] = 'section'
236 273 del block['lines'][1]
237 274 return blocks
238 275
239 276
240 277 def inlineliterals(blocks):
241 278 substs = [('``', '"')]
242 279 for b in blocks:
243 280 if b['type'] in ('paragraph', 'section'):
244 281 b['lines'] = [replace(l, substs) for l in b['lines']]
245 282 return blocks
246 283
247 284
248 285 def hgrole(blocks):
249 286 substs = [(':hg:`', '"hg '), ('`', '"')]
250 287 for b in blocks:
251 288 if b['type'] in ('paragraph', 'section'):
252 289 # Turn :hg:`command` into "hg command". This also works
253 290 # when there is a line break in the command and relies on
254 291 # the fact that we have no stray back-quotes in the input
255 292 # (run the blocks through inlineliterals first).
256 293 b['lines'] = [replace(l, substs) for l in b['lines']]
257 294 return blocks
258 295
259 296
260 297 def addmargins(blocks):
261 298 """Adds empty blocks for vertical spacing.
262 299
263 300 This groups bullets, options, and definitions together with no vertical
264 301 space between them, and adds an empty block between all other blocks.
265 302 """
266 303 i = 1
267 304 while i < len(blocks):
268 305 if (blocks[i]['type'] == blocks[i - 1]['type'] and
269 306 blocks[i]['type'] in ('bullet', 'option', 'field')):
270 307 i += 1
271 308 else:
272 309 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
273 310 i += 2
274 311 return blocks
275 312
276 313 def prunecomments(blocks):
277 314 """Remove comments."""
278 315 i = 0
279 316 while i < len(blocks):
280 317 b = blocks[i]
281 318 if b['type'] == 'paragraph' and (b['lines'][0].startswith('.. ') or
282 319 b['lines'] == ['..']):
283 320 del blocks[i]
284 321 if i < len(blocks) and blocks[i]['type'] == 'margin':
285 322 del blocks[i]
286 323 else:
287 324 i += 1
288 325 return blocks
289 326
290 327 _admonitionre = re.compile(r"\.\. (admonition|attention|caution|danger|"
291 328 r"error|hint|important|note|tip|warning)::",
292 329 flags=re.IGNORECASE)
293 330
294 331 def findadmonitions(blocks):
295 332 """
296 333 Makes the type of the block an admonition block if
297 334 the first line is an admonition directive
298 335 """
299 336 i = 0
300 337 while i < len(blocks):
301 338 m = _admonitionre.match(blocks[i]['lines'][0])
302 339 if m:
303 340 blocks[i]['type'] = 'admonition'
304 341 admonitiontitle = blocks[i]['lines'][0][3:m.end() - 2].lower()
305 342
306 343 firstline = blocks[i]['lines'][0][m.end() + 1:]
307 344 if firstline:
308 345 blocks[i]['lines'].insert(1, ' ' + firstline)
309 346
310 347 blocks[i]['admonitiontitle'] = admonitiontitle
311 348 del blocks[i]['lines'][0]
312 349 i = i + 1
313 350 return blocks
314 351
315 352 _admonitiontitles = {'attention': _('Attention:'),
316 353 'caution': _('Caution:'),
317 354 'danger': _('!Danger!') ,
318 355 'error': _('Error:'),
319 356 'hint': _('Hint:'),
320 357 'important': _('Important:'),
321 358 'note': _('Note:'),
322 359 'tip': _('Tip:'),
323 360 'warning': _('Warning!')}
324 361
362 def formatoption(block, width):
363 desc = ' '.join(map(str.strip, block['lines']))
364 colwidth = encoding.colwidth(block['optstr'])
365 usablewidth = width - 1
366 hanging = block['optstrwidth']
367 initindent = '%s%s ' % (block['optstr'], ' ' * ((hanging - colwidth)))
368 hangindent = ' ' * (encoding.colwidth(initindent) + 1)
369 return ' %s' % (util.wrap(desc, usablewidth,
370 initindent=initindent,
371 hangindent=hangindent))
372
325 373 def formatblock(block, width):
326 374 """Format a block according to width."""
327 375 if width <= 0:
328 376 width = 78
329 377 indent = ' ' * block['indent']
330 378 if block['type'] == 'admonition':
331 379 admonition = _admonitiontitles[block['admonitiontitle']]
332 380 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
333 381
334 382 defindent = indent + hang * ' '
335 383 text = ' '.join(map(str.strip, block['lines']))
336 384 return '%s\n%s' % (indent + admonition, util.wrap(text, width=width,
337 385 initindent=defindent,
338 386 hangindent=defindent))
339 387 if block['type'] == 'margin':
340 388 return ''
341 389 if block['type'] == 'literal':
342 390 indent += ' '
343 391 return indent + ('\n' + indent).join(block['lines'])
344 392 if block['type'] == 'section':
345 393 underline = encoding.colwidth(block['lines'][0]) * block['underline']
346 394 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
347 395 if block['type'] == 'definition':
348 396 term = indent + block['lines'][0]
349 397 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
350 398 defindent = indent + hang * ' '
351 399 text = ' '.join(map(str.strip, block['lines'][1:]))
352 400 return '%s\n%s' % (term, util.wrap(text, width=width,
353 401 initindent=defindent,
354 402 hangindent=defindent))
355 403 subindent = indent
356 404 if block['type'] == 'bullet':
357 405 if block['lines'][0].startswith('| '):
358 406 # Remove bullet for line blocks and add no extra
359 407 # indention.
360 408 block['lines'][0] = block['lines'][0][2:]
361 409 else:
362 410 m = _bulletre.match(block['lines'][0])
363 411 subindent = indent + m.end() * ' '
364 412 elif block['type'] == 'field':
365 413 keywidth = block['keywidth']
366 414 key = block['key']
367 415
368 416 subindent = indent + _fieldwidth * ' '
369 417 if len(key) + 2 > _fieldwidth:
370 418 # key too large, use full line width
371 419 key = key.ljust(width)
372 420 elif keywidth + 2 < _fieldwidth:
373 421 # all keys are small, add only two spaces
374 422 key = key.ljust(keywidth + 2)
375 423 subindent = indent + (keywidth + 2) * ' '
376 424 else:
377 425 # mixed sizes, use fieldwidth for this one
378 426 key = key.ljust(_fieldwidth)
379 427 block['lines'][0] = key + block['lines'][0]
380 428 elif block['type'] == 'option':
381 m = _optionre.match(block['lines'][0])
382 option, arg, rest = m.groups()
383 subindent = indent + (len(option) + len(arg)) * ' '
429 return formatoption(block, width)
384 430
385 431 text = ' '.join(map(str.strip, block['lines']))
386 432 return util.wrap(text, width=width,
387 433 initindent=indent,
388 434 hangindent=subindent)
389 435
390 436
391 437 def format(text, width, indent=0, keep=None):
392 438 """Parse and format the text according to width."""
393 439 blocks = findblocks(text)
394 440 for b in blocks:
395 441 b['indent'] += indent
396 442 blocks = findliteralblocks(blocks)
397 443 blocks, pruned = prunecontainers(blocks, keep or [])
398 444 blocks = findsections(blocks)
399 445 blocks = inlineliterals(blocks)
400 446 blocks = hgrole(blocks)
401 447 blocks = splitparagraphs(blocks)
402 448 blocks = updatefieldlists(blocks)
449 blocks = updateoptionlists(blocks)
403 450 blocks = addmargins(blocks)
404 451 blocks = prunecomments(blocks)
405 452 blocks = findadmonitions(blocks)
406 453 text = '\n'.join(formatblock(b, width) for b in blocks)
407 454 if keep is None:
408 455 return text
409 456 else:
410 457 return text, pruned
411 458
412 459
413 460 if __name__ == "__main__":
414 461 from pprint import pprint
415 462
416 463 def debug(func, *args):
417 464 blocks = func(*args)
418 465 print "*** after %s:" % func.__name__
419 466 pprint(blocks)
420 467 print
421 468 return blocks
422 469
423 470 text = open(sys.argv[1]).read()
424 471 blocks = debug(findblocks, text)
425 472 blocks = debug(findliteralblocks, blocks)
426 473 blocks, pruned = debug(prunecontainers, blocks, sys.argv[2:])
427 474 blocks = debug(inlineliterals, blocks)
428 475 blocks = debug(splitparagraphs, blocks)
429 476 blocks = debug(updatefieldlists, blocks)
477 blocks = debug(updateoptionlists, blocks)
430 478 blocks = debug(findsections, blocks)
431 479 blocks = debug(addmargins, blocks)
432 480 blocks = debug(prunecomments, blocks)
433 481 blocks = debug(findadmonitions, blocks)
434 482 print '\n'.join(formatblock(b, 30) for b in blocks)
@@ -1,392 +1,392 b''
1 1
2 2 $ cat >> $HGRCPATH <<EOF
3 3 > [extensions]
4 4 > convert=
5 5 > [convert]
6 6 > hg.saverev=False
7 7 > EOF
8 8 $ hg help convert
9 9 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
10 10
11 11 convert a foreign SCM repository to a Mercurial one.
12 12
13 13 Accepted source formats [identifiers]:
14 14
15 15 - Mercurial [hg]
16 16 - CVS [cvs]
17 17 - Darcs [darcs]
18 18 - git [git]
19 19 - Subversion [svn]
20 20 - Monotone [mtn]
21 21 - GNU Arch [gnuarch]
22 22 - Bazaar [bzr]
23 23 - Perforce [p4]
24 24
25 25 Accepted destination formats [identifiers]:
26 26
27 27 - Mercurial [hg]
28 28 - Subversion [svn] (history on branches is not preserved)
29 29
30 30 If no revision is given, all revisions will be converted. Otherwise,
31 31 convert will only import up to the named revision (given in a format
32 32 understood by the source).
33 33
34 34 If no destination directory name is specified, it defaults to the basename
35 35 of the source with "-hg" appended. If the destination repository doesn't
36 36 exist, it will be created.
37 37
38 38 By default, all sources except Mercurial will use --branchsort. Mercurial
39 39 uses --sourcesort to preserve original revision numbers order. Sort modes
40 40 have the following effects:
41 41
42 42 --branchsort convert from parent to child revision when possible, which
43 means branches are usually converted one after the other. It
44 generates more compact repositories.
43 means branches are usually converted one after the other.
44 It generates more compact repositories.
45 45 --datesort sort revisions by date. Converted repositories have good-
46 46 looking changelogs but are often an order of magnitude
47 47 larger than the same ones generated by --branchsort.
48 48 --sourcesort try to preserve source revisions order, only supported by
49 49 Mercurial sources.
50 50
51 51 If "REVMAP" isn't given, it will be put in a default location
52 52 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
53 53 maps each source commit ID to the destination ID for that revision, like
54 54 so:
55 55
56 56 <source ID> <destination ID>
57 57
58 58 If the file doesn't exist, it's automatically created. It's updated on
59 59 each commit copied, so "hg convert" can be interrupted and can be run
60 60 repeatedly to copy new commits.
61 61
62 62 The authormap is a simple text file that maps each source commit author to
63 63 a destination commit author. It is handy for source SCMs that use unix
64 64 logins to identify authors (eg: CVS). One line per author mapping and the
65 65 line format is:
66 66
67 67 source author = destination author
68 68
69 69 Empty lines and lines starting with a "#" are ignored.
70 70
71 71 The filemap is a file that allows filtering and remapping of files and
72 72 directories. Each line can contain one of the following directives:
73 73
74 74 include path/to/file-or-dir
75 75
76 76 exclude path/to/file-or-dir
77 77
78 78 rename path/to/source path/to/destination
79 79
80 80 Comment lines start with "#". A specified path matches if it equals the
81 81 full relative name of a file or one of its parent directories. The
82 82 "include" or "exclude" directive with the longest matching path applies,
83 83 so line order does not matter.
84 84
85 85 The "include" directive causes a file, or all files under a directory, to
86 86 be included in the destination repository, and the exclusion of all other
87 87 files and directories not explicitly included. The "exclude" directive
88 88 causes files or directories to be omitted. The "rename" directive renames
89 89 a file or directory if it is converted. To rename from a subdirectory into
90 90 the root of the repository, use "." as the path to rename to.
91 91
92 92 The splicemap is a file that allows insertion of synthetic history,
93 93 letting you specify the parents of a revision. This is useful if you want
94 94 to e.g. give a Subversion merge two parents, or graft two disconnected
95 95 series of history together. Each entry contains a key, followed by a
96 96 space, followed by one or two comma-separated values:
97 97
98 98 key parent1, parent2
99 99
100 100 The key is the revision ID in the source revision control system whose
101 101 parents should be modified (same format as a key in .hg/shamap). The
102 102 values are the revision IDs (in either the source or destination revision
103 103 control system) that should be used as the new parents for that node. For
104 104 example, if you have merged "release-1.0" into "trunk", then you should
105 105 specify the revision on "trunk" as the first parent and the one on the
106 106 "release-1.0" branch as the second.
107 107
108 108 The branchmap is a file that allows you to rename a branch when it is
109 109 being brought in from whatever external repository. When used in
110 110 conjunction with a splicemap, it allows for a powerful combination to help
111 111 fix even the most badly mismanaged repositories and turn them into nicely
112 112 structured Mercurial repositories. The branchmap contains lines of the
113 113 form:
114 114
115 115 original_branch_name new_branch_name
116 116
117 117 where "original_branch_name" is the name of the branch in the source
118 118 repository, and "new_branch_name" is the name of the branch is the
119 119 destination repository. No whitespace is allowed in the branch names. This
120 120 can be used to (for instance) move code in one repository from "default"
121 121 to a named branch.
122 122
123 123 Mercurial Source
124 124 ''''''''''''''''
125 125
126 126 The Mercurial source recognizes the following configuration options, which
127 127 you can set on the command line with "--config":
128 128
129 129 convert.hg.ignoreerrors
130 130 ignore integrity errors when reading. Use it to fix Mercurial
131 131 repositories with missing revlogs, by converting from and to
132 132 Mercurial. Default is False.
133 133 convert.hg.saverev
134 134 store original. revision ID in changeset (forces target IDs to
135 135 change). It takes and boolean argument and defaults to False.
136 136 convert.hg.startrev
137 137 convert start revision and its descendants. It takes a hg
138 138 revision identifier and defaults to 0.
139 139
140 140 CVS Source
141 141 ''''''''''
142 142
143 143 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
144 144 indicate the starting point of what will be converted. Direct access to
145 145 the repository files is not needed, unless of course the repository is
146 146 ":local:". The conversion uses the top level directory in the sandbox to
147 147 find the CVS repository, and then uses CVS rlog commands to find files to
148 148 convert. This means that unless a filemap is given, all files under the
149 149 starting directory will be converted, and that any directory
150 150 reorganization in the CVS sandbox is ignored.
151 151
152 152 The following options can be used with "--config":
153 153
154 154 convert.cvsps.cache
155 155 Set to False to disable remote log caching, for testing and
156 156 debugging purposes. Default is True.
157 157 convert.cvsps.fuzz
158 158 Specify the maximum time (in seconds) that is allowed between
159 159 commits with identical user and log message in a single
160 160 changeset. When very large files were checked in as part of a
161 161 changeset then the default may not be long enough. The default
162 162 is 60.
163 163 convert.cvsps.mergeto
164 164 Specify a regular expression to which commit log messages are
165 165 matched. If a match occurs, then the conversion process will
166 166 insert a dummy revision merging the branch on which this log
167 167 message occurs to the branch indicated in the regex. Default
168 168 is "{{mergetobranch ([-\w]+)}}"
169 169 convert.cvsps.mergefrom
170 170 Specify a regular expression to which commit log messages are
171 171 matched. If a match occurs, then the conversion process will
172 172 add the most recent revision on the branch indicated in the
173 173 regex as the second parent of the changeset. Default is
174 174 "{{mergefrombranch ([-\w]+)}}"
175 175 hook.cvslog
176 176 Specify a Python function to be called at the end of gathering
177 177 the CVS log. The function is passed a list with the log
178 178 entries, and can modify the entries in-place, or add or delete
179 179 them.
180 180 hook.cvschangesets
181 181 Specify a Python function to be called after the changesets
182 182 are calculated from the the CVS log. The function is passed a
183 183 list with the changeset entries, and can modify the changesets
184 184 in-place, or add or delete them.
185 185
186 186 An additional "debugcvsps" Mercurial command allows the builtin changeset
187 187 merging code to be run without doing a conversion. Its parameters and
188 188 output are similar to that of cvsps 2.1. Please see the command help for
189 189 more details.
190 190
191 191 Subversion Source
192 192 '''''''''''''''''
193 193
194 194 Subversion source detects classical trunk/branches/tags layouts. By
195 195 default, the supplied "svn://repo/path/" source URL is converted as a
196 196 single branch. If "svn://repo/path/trunk" exists it replaces the default
197 197 branch. If "svn://repo/path/branches" exists, its subdirectories are
198 198 listed as possible branches. If "svn://repo/path/tags" exists, it is
199 199 looked for tags referencing converted branches. Default "trunk",
200 200 "branches" and "tags" values can be overridden with following options. Set
201 201 them to paths relative to the source URL, or leave them blank to disable
202 202 auto detection.
203 203
204 204 The following options can be set with "--config":
205 205
206 206 convert.svn.branches
207 207 specify the directory containing branches. The defaults is
208 208 "branches".
209 209 convert.svn.tags
210 210 specify the directory containing tags. The default is "tags".
211 211 convert.svn.trunk
212 212 specify the name of the trunk branch The defauls is "trunk".
213 213
214 214 Source history can be retrieved starting at a specific revision, instead
215 215 of being integrally converted. Only single branch conversions are
216 216 supported.
217 217
218 218 convert.svn.startrev
219 219 specify start Subversion revision number. The default is 0.
220 220
221 221 Perforce Source
222 222 '''''''''''''''
223 223
224 224 The Perforce (P4) importer can be given a p4 depot path or a client
225 225 specification as source. It will convert all files in the source to a flat
226 226 Mercurial repository, ignoring labels, branches and integrations. Note
227 227 that when a depot path is given you then usually should specify a target
228 228 directory, because otherwise the target may be named "...-hg".
229 229
230 230 It is possible to limit the amount of source history to be converted by
231 231 specifying an initial Perforce revision:
232 232
233 233 convert.p4.startrev
234 234 specify initial Perforce revision, a Perforce changelist
235 235 number).
236 236
237 237 Mercurial Destination
238 238 '''''''''''''''''''''
239 239
240 240 The following options are supported:
241 241
242 242 convert.hg.clonebranches
243 243 dispatch source branches in separate clones. The default is
244 244 False.
245 245 convert.hg.tagsbranch
246 246 branch name for tag revisions, defaults to "default".
247 247 convert.hg.usebranchnames
248 248 preserve branch names. The default is True
249 249
250 250 options:
251 251
252 252 -s --source-type TYPE source repository type
253 253 -d --dest-type TYPE destination repository type
254 254 -r --rev REV import up to target revision REV
255 255 -A --authormap FILE remap usernames using this file
256 256 --filemap FILE remap file names using contents of file
257 257 --splicemap FILE splice synthesized history into place
258 258 --branchmap FILE change branch names while converting
259 259 --branchsort try to sort changesets by branches
260 260 --datesort try to sort changesets by date
261 261 --sourcesort preserve source changesets order
262 262
263 263 use "hg -v help convert" to show global options
264 264 $ hg init a
265 265 $ cd a
266 266 $ echo a > a
267 267 $ hg ci -d'0 0' -Ama
268 268 adding a
269 269 $ hg cp a b
270 270 $ hg ci -d'1 0' -mb
271 271 $ hg rm a
272 272 $ hg ci -d'2 0' -mc
273 273 $ hg mv b a
274 274 $ hg ci -d'3 0' -md
275 275 $ echo a >> a
276 276 $ hg ci -d'4 0' -me
277 277 $ cd ..
278 278 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
279 279 assuming destination a-hg
280 280 initializing destination a-hg repository
281 281 scanning source...
282 282 sorting...
283 283 converting...
284 284 4 a
285 285 3 b
286 286 2 c
287 287 1 d
288 288 0 e
289 289 $ hg --cwd a-hg pull ../a
290 290 pulling from ../a
291 291 searching for changes
292 292 no changes found
293 293 $ touch bogusfile
294 294
295 295 should fail
296 296
297 297 $ hg convert a bogusfile
298 298 initializing destination bogusfile repository
299 299 abort: cannot create new bundle repository
300 300 [255]
301 301 $ mkdir bogusdir
302 302 $ chmod 000 bogusdir
303 303
304 304 should fail
305 305
306 306 $ hg convert a bogusdir
307 307 abort: Permission denied: bogusdir
308 308 [255]
309 309
310 310 should succeed
311 311
312 312 $ chmod 700 bogusdir
313 313 $ hg convert a bogusdir
314 314 initializing destination bogusdir repository
315 315 scanning source...
316 316 sorting...
317 317 converting...
318 318 4 a
319 319 3 b
320 320 2 c
321 321 1 d
322 322 0 e
323 323
324 324 test pre and post conversion actions
325 325
326 326 $ echo 'include b' > filemap
327 327 $ hg convert --debug --filemap filemap a partialb | \
328 328 > grep 'run hg'
329 329 run hg source pre-conversion action
330 330 run hg sink pre-conversion action
331 331 run hg sink post-conversion action
332 332 run hg source post-conversion action
333 333
334 334 converting empty dir should fail "nicely
335 335
336 336 $ mkdir emptydir
337 337
338 338 override $PATH to ensure p4 not visible; use $PYTHON in case we're
339 339 running from a devel copy, not a temp installation
340 340
341 341 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
342 342 assuming destination emptydir-hg
343 343 initializing destination emptydir-hg repository
344 344 emptydir does not look like a CVS checkout
345 345 emptydir does not look like a Git repository
346 346 emptydir does not look like a Subversion repository
347 347 emptydir is not a local Mercurial repository
348 348 emptydir does not look like a darcs repository
349 349 emptydir does not look like a monotone repository
350 350 emptydir does not look like a GNU Arch repository
351 351 emptydir does not look like a Bazaar repository
352 352 cannot find required "p4" tool
353 353 abort: emptydir: missing or unsupported repository
354 354 [255]
355 355
356 356 convert with imaginary source type
357 357
358 358 $ hg convert --source-type foo a a-foo
359 359 initializing destination a-foo repository
360 360 abort: foo: invalid source repository type
361 361 [255]
362 362
363 363 convert with imaginary sink type
364 364
365 365 $ hg convert --dest-type foo a a-foo
366 366 abort: foo: invalid destination repository type
367 367 [255]
368 368
369 369 testing: convert must not produce duplicate entries in fncache
370 370
371 371 $ hg convert a b
372 372 initializing destination b repository
373 373 scanning source...
374 374 sorting...
375 375 converting...
376 376 4 a
377 377 3 b
378 378 2 c
379 379 1 d
380 380 0 e
381 381
382 382 contents of fncache file:
383 383
384 384 $ cat b/.hg/store/fncache
385 385 data/a.i
386 386 data/b.i
387 387
388 388 test bogus URL
389 389
390 390 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
391 391 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
392 392 [255]
@@ -1,230 +1,233 b''
1 1 from pprint import pprint
2 2 from mercurial import minirst
3 3
4 4 def debugformat(title, text, width, **kwargs):
5 5 print "%s formatted to fit within %d characters:" % (title, width)
6 6 print "-" * 70
7 7 formatted = minirst.format(text, width, **kwargs)
8 8 if type(formatted) == tuple:
9 9 print formatted[0]
10 10 print "-" * 70
11 11 pprint(formatted[1])
12 12 else:
13 13 print formatted
14 14 print "-" * 70
15 15 print
16 16
17 17 paragraphs = """
18 18 This is some text in the first paragraph.
19 19
20 20 A small indented paragraph.
21 21 It is followed by some lines
22 22 containing random whitespace.
23 23 \n \n \nThe third and final paragraph.
24 24 """
25 25
26 26 debugformat('paragraphs', paragraphs, 60)
27 27 debugformat('paragraphs', paragraphs, 30)
28 28
29 29
30 30 definitions = """
31 31 A Term
32 32 Definition. The indented
33 33 lines make up the definition.
34 34 Another Term
35 35 Another definition. The final line in the
36 36 definition determines the indentation, so
37 37 this will be indented with four spaces.
38 38
39 39 A Nested/Indented Term
40 40 Definition.
41 41 """
42 42
43 43 debugformat('definitions', definitions, 60)
44 44 debugformat('definitions', definitions, 30)
45 45
46 46
47 47 literals = r"""
48 48 The fully minimized form is the most
49 49 convenient form::
50 50
51 51 Hello
52 52 literal
53 53 world
54 54
55 55 In the partially minimized form a paragraph
56 56 simply ends with space-double-colon. ::
57 57
58 58 ////////////////////////////////////////
59 59 long un-wrapped line in a literal block
60 60 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
61 61
62 62 ::
63 63
64 64 This literal block is started with '::',
65 65 the so-called expanded form. The paragraph
66 66 with '::' disappears in the final output.
67 67 """
68 68
69 69 debugformat('literals', literals, 60)
70 70 debugformat('literals', literals, 30)
71 71
72 72
73 73 lists = """
74 74 - This is the first list item.
75 75
76 76 Second paragraph in the first list item.
77 77
78 78 - List items need not be separated
79 79 by a blank line.
80 80 - And will be rendered without
81 81 one in any case.
82 82
83 83 We can have indented lists:
84 84
85 85 - This is an indented list item
86 86
87 87 - Another indented list item::
88 88
89 89 - A literal block in the middle
90 90 of an indented list.
91 91
92 92 (The above is not a list item since we are in the literal block.)
93 93
94 94 ::
95 95
96 96 Literal block with no indentation (apart from
97 97 the two spaces added to all literal blocks).
98 98
99 99 1. This is an enumerated list (first item).
100 100 2. Continuing with the second item.
101 101
102 102 (1) foo
103 103 (2) bar
104 104
105 105 1) Another
106 106 2) List
107 107
108 108 Line blocks are also a form of list:
109 109
110 110 | This is the first line.
111 111 The line continues here.
112 112 | This is the second line.
113 113 """
114 114
115 115 debugformat('lists', lists, 60)
116 116 debugformat('lists', lists, 30)
117 117
118 118
119 119 options = """
120 120 There is support for simple option lists,
121 121 but only with long options:
122 122
123 --all Output all.
124 --both Output both (this description is
125 quite long).
126 --long Output all day long.
123 -X, --exclude filter an option with a short and long option with an argument
124 -I, --include an option with both a short option and a long option
125 --all Output all.
126 --both Output both (this description is
127 quite long).
128 --long Output all day long.
127 129
128 --par This option has two paragraphs in its description.
129 This is the first.
130 --par This option has two paragraphs in its description.
131 This is the first.
130 132
131 This is the second. Blank lines may be omitted between
132 options (as above) or left in (as here).
133 This is the second. Blank lines may be omitted between
134 options (as above) or left in (as here).
135
133 136
134 137 The next paragraph looks like an option list, but lacks the two-space
135 138 marker after the option. It is treated as a normal paragraph:
136 139
137 140 --foo bar baz
138 141 """
139 142
140 143 debugformat('options', options, 60)
141 144 debugformat('options', options, 30)
142 145
143 146
144 147 fields = """
145 148 :a: First item.
146 149 :ab: Second item. Indentation and wrapping
147 150 is handled automatically.
148 151
149 152 Next list:
150 153
151 154 :small: The larger key below triggers full indentation here.
152 155 :much too large: This key is big enough to get its own line.
153 156 """
154 157
155 158 debugformat('fields', fields, 60)
156 159 debugformat('fields', fields, 30)
157 160
158 161 containers = """
159 162 Normal output.
160 163
161 164 .. container:: debug
162 165
163 166 Initial debug output.
164 167
165 168 .. container:: verbose
166 169
167 170 Verbose output.
168 171
169 172 .. container:: debug
170 173
171 174 Debug output.
172 175 """
173 176
174 177 debugformat('containers (normal)', containers, 60)
175 178 debugformat('containers (verbose)', containers, 60, keep=['verbose'])
176 179 debugformat('containers (debug)', containers, 60, keep=['debug'])
177 180 debugformat('containers (verbose debug)', containers, 60,
178 181 keep=['verbose', 'debug'])
179 182
180 183 roles = """Please see :hg:`add`."""
181 184 debugformat('roles', roles, 60)
182 185
183 186
184 187 sections = """
185 188 Title
186 189 =====
187 190
188 191 Section
189 192 -------
190 193
191 194 Subsection
192 195 ''''''''''
193 196
194 197 Markup: ``foo`` and :hg:`help`
195 198 ------------------------------
196 199 """
197 200 debugformat('sections', sections, 20)
198 201
199 202
200 203 admonitions = """
201 204 .. note::
202 205 This is a note
203 206
204 207 - Bullet 1
205 208 - Bullet 2
206 209
207 210 .. warning:: This is a warning Second
208 211 input line of warning
209 212
210 213 .. danger::
211 214 This is danger
212 215 """
213 216
214 217 debugformat('admonitions', admonitions, 30)
215 218
216 219 comments = """
217 220 Some text.
218 221
219 222 .. A comment
220 223
221 224 .. An indented comment
222 225
223 226 Some indented text.
224 227
225 228 ..
226 229
227 230 Empty comment above
228 231 """
229 232
230 233 debugformat('comments', comments, 30)
@@ -1,345 +1,390 b''
1 1 paragraphs formatted to fit within 60 characters:
2 2 ----------------------------------------------------------------------
3 3 This is some text in the first paragraph.
4 4
5 5 A small indented paragraph. It is followed by some lines
6 6 containing random whitespace.
7 7
8 8 The third and final paragraph.
9 9 ----------------------------------------------------------------------
10 10
11 11 paragraphs formatted to fit within 30 characters:
12 12 ----------------------------------------------------------------------
13 13 This is some text in the first
14 14 paragraph.
15 15
16 16 A small indented paragraph.
17 17 It is followed by some lines
18 18 containing random
19 19 whitespace.
20 20
21 21 The third and final paragraph.
22 22 ----------------------------------------------------------------------
23 23
24 24 definitions formatted to fit within 60 characters:
25 25 ----------------------------------------------------------------------
26 26 A Term
27 27 Definition. The indented lines make up the definition.
28 28
29 29 Another Term
30 30 Another definition. The final line in the definition
31 31 determines the indentation, so this will be indented
32 32 with four spaces.
33 33
34 34 A Nested/Indented Term
35 35 Definition.
36 36 ----------------------------------------------------------------------
37 37
38 38 definitions formatted to fit within 30 characters:
39 39 ----------------------------------------------------------------------
40 40 A Term
41 41 Definition. The indented
42 42 lines make up the
43 43 definition.
44 44
45 45 Another Term
46 46 Another definition. The
47 47 final line in the
48 48 definition determines the
49 49 indentation, so this will
50 50 be indented with four
51 51 spaces.
52 52
53 53 A Nested/Indented Term
54 54 Definition.
55 55 ----------------------------------------------------------------------
56 56
57 57 literals formatted to fit within 60 characters:
58 58 ----------------------------------------------------------------------
59 59 The fully minimized form is the most convenient form:
60 60
61 61 Hello
62 62 literal
63 63 world
64 64
65 65 In the partially minimized form a paragraph simply ends with
66 66 space-double-colon.
67 67
68 68 ////////////////////////////////////////
69 69 long un-wrapped line in a literal block
70 70 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
71 71
72 72 This literal block is started with '::',
73 73 the so-called expanded form. The paragraph
74 74 with '::' disappears in the final output.
75 75 ----------------------------------------------------------------------
76 76
77 77 literals formatted to fit within 30 characters:
78 78 ----------------------------------------------------------------------
79 79 The fully minimized form is
80 80 the most convenient form:
81 81
82 82 Hello
83 83 literal
84 84 world
85 85
86 86 In the partially minimized
87 87 form a paragraph simply ends
88 88 with space-double-colon.
89 89
90 90 ////////////////////////////////////////
91 91 long un-wrapped line in a literal block
92 92 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
93 93
94 94 This literal block is started with '::',
95 95 the so-called expanded form. The paragraph
96 96 with '::' disappears in the final output.
97 97 ----------------------------------------------------------------------
98 98
99 99 lists formatted to fit within 60 characters:
100 100 ----------------------------------------------------------------------
101 101 - This is the first list item.
102 102
103 103 Second paragraph in the first list item.
104 104
105 105 - List items need not be separated by a blank line.
106 106 - And will be rendered without one in any case.
107 107
108 108 We can have indented lists:
109 109
110 110 - This is an indented list item
111 111 - Another indented list item:
112 112
113 113 - A literal block in the middle
114 114 of an indented list.
115 115
116 116 (The above is not a list item since we are in the literal block.)
117 117
118 118 Literal block with no indentation (apart from
119 119 the two spaces added to all literal blocks).
120 120
121 121 1. This is an enumerated list (first item).
122 122 2. Continuing with the second item.
123 123 (1) foo
124 124 (2) bar
125 125 1) Another
126 126 2) List
127 127
128 128 Line blocks are also a form of list:
129 129
130 130 This is the first line. The line continues here.
131 131 This is the second line.
132 132 ----------------------------------------------------------------------
133 133
134 134 lists formatted to fit within 30 characters:
135 135 ----------------------------------------------------------------------
136 136 - This is the first list item.
137 137
138 138 Second paragraph in the
139 139 first list item.
140 140
141 141 - List items need not be
142 142 separated by a blank line.
143 143 - And will be rendered without
144 144 one in any case.
145 145
146 146 We can have indented lists:
147 147
148 148 - This is an indented list
149 149 item
150 150 - Another indented list
151 151 item:
152 152
153 153 - A literal block in the middle
154 154 of an indented list.
155 155
156 156 (The above is not a list item since we are in the literal block.)
157 157
158 158 Literal block with no indentation (apart from
159 159 the two spaces added to all literal blocks).
160 160
161 161 1. This is an enumerated list
162 162 (first item).
163 163 2. Continuing with the second
164 164 item.
165 165 (1) foo
166 166 (2) bar
167 167 1) Another
168 168 2) List
169 169
170 170 Line blocks are also a form of
171 171 list:
172 172
173 173 This is the first line. The
174 174 line continues here.
175 175 This is the second line.
176 176 ----------------------------------------------------------------------
177 177
178 178 options formatted to fit within 60 characters:
179 179 ----------------------------------------------------------------------
180 180 There is support for simple option lists, but only with long
181 181 options:
182 182
183 --all Output all.
184 --both Output both (this description is quite long).
185 --long Output all day long.
186 --par This option has two paragraphs in its
187 description. This is the first.
183 -X --exclude filter an option with a short and long option
184 with an argument
185 -I --include an option with both a short option and
186 a long option
187 --all Output all.
188 --both Output both (this description is quite
189 long).
190 --long Output all day long.
191 --par This option has two paragraphs in its
192 description. This is the first.
188 193
189 This is the second. Blank lines may be omitted
190 between options (as above) or left in (as here).
194 This is the second. Blank lines may
195 be omitted between options (as above)
196 or left in (as here).
191 197
192 198 The next paragraph looks like an option list, but lacks the
193 199 two-space marker after the option. It is treated as a normal
194 200 paragraph:
195 201
196 202 --foo bar baz
197 203 ----------------------------------------------------------------------
198 204
199 205 options formatted to fit within 30 characters:
200 206 ----------------------------------------------------------------------
201 207 There is support for simple
202 208 option lists, but only with
203 209 long options:
204 210
205 --all Output all.
206 --both Output both (this
207 description is
208 quite long).
209 --long Output all day
210 long.
211 --par This option has two
212 paragraphs in its
213 description. This
214 is the first.
211 -X --exclude filter an
212 option
213 with a
214 short
215 and
216 long
217 option
218 with an
219 argumen
220 t
221 -I --include an
222 option
223 with
224 both a
225 short
226 option
227 and a
228 long
229 option
230 --all Output
231 all.
232 --both Output
233 both
234 (this d
235 escript
236 ion is
237 quite
238 long).
239 --long Output
240 all day
241 long.
242 --par This
243 option
244 has two
245 paragra
246 phs in
247 its des
248 criptio
249 n. This
250 is the
251 first.
215 252
216 This is the second.
217 Blank lines may be
218 omitted between
219 options (as above)
220 or left in (as
221 here).
253 This is
254 the
255 second.
256 Blank
257 lines
258 may be
259 omitted
260 between
261 options
262 (as
263 above)
264 or left
265 in (as
266 here).
222 267
223 268 The next paragraph looks like
224 269 an option list, but lacks the
225 270 two-space marker after the
226 271 option. It is treated as a
227 272 normal paragraph:
228 273
229 274 --foo bar baz
230 275 ----------------------------------------------------------------------
231 276
232 277 fields formatted to fit within 60 characters:
233 278 ----------------------------------------------------------------------
234 279 a First item.
235 280 ab Second item. Indentation and wrapping is handled
236 281 automatically.
237 282
238 283 Next list:
239 284
240 285 small The larger key below triggers full indentation
241 286 here.
242 287 much too large
243 288 This key is big enough to get its own line.
244 289 ----------------------------------------------------------------------
245 290
246 291 fields formatted to fit within 30 characters:
247 292 ----------------------------------------------------------------------
248 293 a First item.
249 294 ab Second item. Indentation
250 295 and wrapping is handled
251 296 automatically.
252 297
253 298 Next list:
254 299
255 300 small The larger key
256 301 below triggers
257 302 full indentation
258 303 here.
259 304 much too large
260 305 This key is big
261 306 enough to get its
262 307 own line.
263 308 ----------------------------------------------------------------------
264 309
265 310 containers (normal) formatted to fit within 60 characters:
266 311 ----------------------------------------------------------------------
267 312 Normal output.
268 313 ----------------------------------------------------------------------
269 314
270 315 containers (verbose) formatted to fit within 60 characters:
271 316 ----------------------------------------------------------------------
272 317 Normal output.
273 318
274 319 Verbose output.
275 320 ----------------------------------------------------------------------
276 321 ['debug', 'debug']
277 322 ----------------------------------------------------------------------
278 323
279 324 containers (debug) formatted to fit within 60 characters:
280 325 ----------------------------------------------------------------------
281 326 Normal output.
282 327
283 328 Initial debug output.
284 329 ----------------------------------------------------------------------
285 330 ['verbose']
286 331 ----------------------------------------------------------------------
287 332
288 333 containers (verbose debug) formatted to fit within 60 characters:
289 334 ----------------------------------------------------------------------
290 335 Normal output.
291 336
292 337 Initial debug output.
293 338
294 339 Verbose output.
295 340
296 341 Debug output.
297 342 ----------------------------------------------------------------------
298 343 []
299 344 ----------------------------------------------------------------------
300 345
301 346 roles formatted to fit within 60 characters:
302 347 ----------------------------------------------------------------------
303 348 Please see "hg add".
304 349 ----------------------------------------------------------------------
305 350
306 351 sections formatted to fit within 20 characters:
307 352 ----------------------------------------------------------------------
308 353 Title
309 354 =====
310 355
311 356 Section
312 357 -------
313 358
314 359 Subsection
315 360 ''''''''''
316 361
317 362 Markup: "foo" and "hg help"
318 363 ---------------------------
319 364 ----------------------------------------------------------------------
320 365
321 366 admonitions formatted to fit within 30 characters:
322 367 ----------------------------------------------------------------------
323 368 Note:
324 369 This is a note
325 370
326 371 - Bullet 1
327 372 - Bullet 2
328 373
329 374 Warning!
330 375 This is a warning Second
331 376 input line of warning
332 377
333 378 !Danger!
334 379 This is danger
335 380 ----------------------------------------------------------------------
336 381
337 382 comments formatted to fit within 30 characters:
338 383 ----------------------------------------------------------------------
339 384 Some text.
340 385
341 386 Some indented text.
342 387
343 388 Empty comment above
344 389 ----------------------------------------------------------------------
345 390
General Comments 0
You need to be logged in to leave comments. Login now