##// END OF EJS Templates
convert: option to set date and time for svn commits...
Nikita Slyusarev -
r47129:7525e77b default
parent child Browse files
Show More
@@ -1,587 +1,603 b''
1 1 # convert.py Foreign SCM converter
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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 '''import revisions from foreign VCS repositories into Mercurial'''
9 9
10 10 from __future__ import absolute_import
11 11
12 12 from mercurial.i18n import _
13 13 from mercurial import registrar
14 14
15 15 from . import (
16 16 convcmd,
17 17 cvsps,
18 18 subversion,
19 19 )
20 20
21 21 cmdtable = {}
22 22 command = registrar.command(cmdtable)
23 23 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
24 24 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
25 25 # be specifying the version(s) of Mercurial they are tested with, or
26 26 # leave the attribute unspecified.
27 27 testedwith = b'ships-with-hg-core'
28 28
29 29 # Commands definition was moved elsewhere to ease demandload job.
30 30
31 31
32 32 @command(
33 33 b'convert',
34 34 [
35 35 (
36 36 b'',
37 37 b'authors',
38 38 b'',
39 39 _(
40 40 b'username mapping filename (DEPRECATED) (use --authormap instead)'
41 41 ),
42 42 _(b'FILE'),
43 43 ),
44 44 (b's', b'source-type', b'', _(b'source repository type'), _(b'TYPE')),
45 45 (
46 46 b'd',
47 47 b'dest-type',
48 48 b'',
49 49 _(b'destination repository type'),
50 50 _(b'TYPE'),
51 51 ),
52 52 (b'r', b'rev', [], _(b'import up to source revision REV'), _(b'REV')),
53 53 (
54 54 b'A',
55 55 b'authormap',
56 56 b'',
57 57 _(b'remap usernames using this file'),
58 58 _(b'FILE'),
59 59 ),
60 60 (
61 61 b'',
62 62 b'filemap',
63 63 b'',
64 64 _(b'remap file names using contents of file'),
65 65 _(b'FILE'),
66 66 ),
67 67 (
68 68 b'',
69 69 b'full',
70 70 None,
71 71 _(b'apply filemap changes by converting all files again'),
72 72 ),
73 73 (
74 74 b'',
75 75 b'splicemap',
76 76 b'',
77 77 _(b'splice synthesized history into place'),
78 78 _(b'FILE'),
79 79 ),
80 80 (
81 81 b'',
82 82 b'branchmap',
83 83 b'',
84 84 _(b'change branch names while converting'),
85 85 _(b'FILE'),
86 86 ),
87 87 (b'', b'branchsort', None, _(b'try to sort changesets by branches')),
88 88 (b'', b'datesort', None, _(b'try to sort changesets by date')),
89 89 (b'', b'sourcesort', None, _(b'preserve source changesets order')),
90 90 (b'', b'closesort', None, _(b'try to reorder closed revisions')),
91 91 ],
92 92 _(b'hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
93 93 norepo=True,
94 94 )
95 95 def convert(ui, src, dest=None, revmapfile=None, **opts):
96 96 """convert a foreign SCM repository to a Mercurial one.
97 97
98 98 Accepted source formats [identifiers]:
99 99
100 100 - Mercurial [hg]
101 101 - CVS [cvs]
102 102 - Darcs [darcs]
103 103 - git [git]
104 104 - Subversion [svn]
105 105 - Monotone [mtn]
106 106 - GNU Arch [gnuarch]
107 107 - Bazaar [bzr]
108 108 - Perforce [p4]
109 109
110 110 Accepted destination formats [identifiers]:
111 111
112 112 - Mercurial [hg]
113 113 - Subversion [svn] (history on branches is not preserved)
114 114
115 115 If no revision is given, all revisions will be converted.
116 116 Otherwise, convert will only import up to the named revision
117 117 (given in a format understood by the source).
118 118
119 119 If no destination directory name is specified, it defaults to the
120 120 basename of the source with ``-hg`` appended. If the destination
121 121 repository doesn't exist, it will be created.
122 122
123 123 By default, all sources except Mercurial will use --branchsort.
124 124 Mercurial uses --sourcesort to preserve original revision numbers
125 125 order. Sort modes have the following effects:
126 126
127 127 --branchsort convert from parent to child revision when possible,
128 128 which means branches are usually converted one after
129 129 the other. It generates more compact repositories.
130 130
131 131 --datesort sort revisions by date. Converted repositories have
132 132 good-looking changelogs but are often an order of
133 133 magnitude larger than the same ones generated by
134 134 --branchsort.
135 135
136 136 --sourcesort try to preserve source revisions order, only
137 137 supported by Mercurial sources.
138 138
139 139 --closesort try to move closed revisions as close as possible
140 140 to parent branches, only supported by Mercurial
141 141 sources.
142 142
143 143 If ``REVMAP`` isn't given, it will be put in a default location
144 144 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
145 145 text file that maps each source commit ID to the destination ID
146 146 for that revision, like so::
147 147
148 148 <source ID> <destination ID>
149 149
150 150 If the file doesn't exist, it's automatically created. It's
151 151 updated on each commit copied, so :hg:`convert` can be interrupted
152 152 and can be run repeatedly to copy new commits.
153 153
154 154 The authormap is a simple text file that maps each source commit
155 155 author to a destination commit author. It is handy for source SCMs
156 156 that use unix logins to identify authors (e.g.: CVS). One line per
157 157 author mapping and the line format is::
158 158
159 159 source author = destination author
160 160
161 161 Empty lines and lines starting with a ``#`` are ignored.
162 162
163 163 The filemap is a file that allows filtering and remapping of files
164 164 and directories. Each line can contain one of the following
165 165 directives::
166 166
167 167 include path/to/file-or-dir
168 168
169 169 exclude path/to/file-or-dir
170 170
171 171 rename path/to/source path/to/destination
172 172
173 173 Comment lines start with ``#``. A specified path matches if it
174 174 equals the full relative name of a file or one of its parent
175 175 directories. The ``include`` or ``exclude`` directive with the
176 176 longest matching path applies, so line order does not matter.
177 177
178 178 The ``include`` directive causes a file, or all files under a
179 179 directory, to be included in the destination repository. The default
180 180 if there are no ``include`` statements is to include everything.
181 181 If there are any ``include`` statements, nothing else is included.
182 182 The ``exclude`` directive causes files or directories to
183 183 be omitted. The ``rename`` directive renames a file or directory if
184 184 it is converted. To rename from a subdirectory into the root of
185 185 the repository, use ``.`` as the path to rename to.
186 186
187 187 ``--full`` will make sure the converted changesets contain exactly
188 188 the right files with the right content. It will make a full
189 189 conversion of all files, not just the ones that have
190 190 changed. Files that already are correct will not be changed. This
191 191 can be used to apply filemap changes when converting
192 192 incrementally. This is currently only supported for Mercurial and
193 193 Subversion.
194 194
195 195 The splicemap is a file that allows insertion of synthetic
196 196 history, letting you specify the parents of a revision. This is
197 197 useful if you want to e.g. give a Subversion merge two parents, or
198 198 graft two disconnected series of history together. Each entry
199 199 contains a key, followed by a space, followed by one or two
200 200 comma-separated values::
201 201
202 202 key parent1, parent2
203 203
204 204 The key is the revision ID in the source
205 205 revision control system whose parents should be modified (same
206 206 format as a key in .hg/shamap). The values are the revision IDs
207 207 (in either the source or destination revision control system) that
208 208 should be used as the new parents for that node. For example, if
209 209 you have merged "release-1.0" into "trunk", then you should
210 210 specify the revision on "trunk" as the first parent and the one on
211 211 the "release-1.0" branch as the second.
212 212
213 213 The branchmap is a file that allows you to rename a branch when it is
214 214 being brought in from whatever external repository. When used in
215 215 conjunction with a splicemap, it allows for a powerful combination
216 216 to help fix even the most badly mismanaged repositories and turn them
217 217 into nicely structured Mercurial repositories. The branchmap contains
218 218 lines of the form::
219 219
220 220 original_branch_name new_branch_name
221 221
222 222 where "original_branch_name" is the name of the branch in the
223 223 source repository, and "new_branch_name" is the name of the branch
224 224 is the destination repository. No whitespace is allowed in the new
225 225 branch name. This can be used to (for instance) move code in one
226 226 repository from "default" to a named branch.
227 227
228 228 Mercurial Source
229 229 ################
230 230
231 231 The Mercurial source recognizes the following configuration
232 232 options, which you can set on the command line with ``--config``:
233 233
234 234 :convert.hg.ignoreerrors: ignore integrity errors when reading.
235 235 Use it to fix Mercurial repositories with missing revlogs, by
236 236 converting from and to Mercurial. Default is False.
237 237
238 238 :convert.hg.saverev: store original revision ID in changeset
239 239 (forces target IDs to change). It takes a boolean argument and
240 240 defaults to False.
241 241
242 242 :convert.hg.startrev: specify the initial Mercurial revision.
243 243 The default is 0.
244 244
245 245 :convert.hg.revs: revset specifying the source revisions to convert.
246 246
247 247 Bazaar Source
248 248 #############
249 249
250 250 The following options can be used with ``--config``:
251 251
252 252 :convert.bzr.saverev: whether to store the original Bazaar commit ID in
253 253 the metadata of the destination commit. The default is True.
254 254
255 255 CVS Source
256 256 ##########
257 257
258 258 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
259 259 to indicate the starting point of what will be converted. Direct
260 260 access to the repository files is not needed, unless of course the
261 261 repository is ``:local:``. The conversion uses the top level
262 262 directory in the sandbox to find the CVS repository, and then uses
263 263 CVS rlog commands to find files to convert. This means that unless
264 264 a filemap is given, all files under the starting directory will be
265 265 converted, and that any directory reorganization in the CVS
266 266 sandbox is ignored.
267 267
268 268 The following options can be used with ``--config``:
269 269
270 270 :convert.cvsps.cache: Set to False to disable remote log caching,
271 271 for testing and debugging purposes. Default is True.
272 272
273 273 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
274 274 allowed between commits with identical user and log message in
275 275 a single changeset. When very large files were checked in as
276 276 part of a changeset then the default may not be long enough.
277 277 The default is 60.
278 278
279 279 :convert.cvsps.logencoding: Specify encoding name to be used for
280 280 transcoding CVS log messages. Multiple encoding names can be
281 281 specified as a list (see :hg:`help config.Syntax`), but only
282 282 the first acceptable encoding in the list is used per CVS log
283 283 entries. This transcoding is executed before cvslog hook below.
284 284
285 285 :convert.cvsps.mergeto: Specify a regular expression to which
286 286 commit log messages are matched. If a match occurs, then the
287 287 conversion process will insert a dummy revision merging the
288 288 branch on which this log message occurs to the branch
289 289 indicated in the regex. Default is ``{{mergetobranch
290 290 ([-\\w]+)}}``
291 291
292 292 :convert.cvsps.mergefrom: Specify a regular expression to which
293 293 commit log messages are matched. If a match occurs, then the
294 294 conversion process will add the most recent revision on the
295 295 branch indicated in the regex as the second parent of the
296 296 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
297 297
298 298 :convert.localtimezone: use local time (as determined by the TZ
299 299 environment variable) for changeset date/times. The default
300 300 is False (use UTC).
301 301
302 302 :hooks.cvslog: Specify a Python function to be called at the end of
303 303 gathering the CVS log. The function is passed a list with the
304 304 log entries, and can modify the entries in-place, or add or
305 305 delete them.
306 306
307 307 :hooks.cvschangesets: Specify a Python function to be called after
308 308 the changesets are calculated from the CVS log. The
309 309 function is passed a list with the changeset entries, and can
310 310 modify the changesets in-place, or add or delete them.
311 311
312 312 An additional "debugcvsps" Mercurial command allows the builtin
313 313 changeset merging code to be run without doing a conversion. Its
314 314 parameters and output are similar to that of cvsps 2.1. Please see
315 315 the command help for more details.
316 316
317 317 Subversion Source
318 318 #################
319 319
320 320 Subversion source detects classical trunk/branches/tags layouts.
321 321 By default, the supplied ``svn://repo/path/`` source URL is
322 322 converted as a single branch. If ``svn://repo/path/trunk`` exists
323 323 it replaces the default branch. If ``svn://repo/path/branches``
324 324 exists, its subdirectories are listed as possible branches. If
325 325 ``svn://repo/path/tags`` exists, it is looked for tags referencing
326 326 converted branches. Default ``trunk``, ``branches`` and ``tags``
327 327 values can be overridden with following options. Set them to paths
328 328 relative to the source URL, or leave them blank to disable auto
329 329 detection.
330 330
331 331 The following options can be set with ``--config``:
332 332
333 333 :convert.svn.branches: specify the directory containing branches.
334 334 The default is ``branches``.
335 335
336 336 :convert.svn.tags: specify the directory containing tags. The
337 337 default is ``tags``.
338 338
339 339 :convert.svn.trunk: specify the name of the trunk branch. The
340 340 default is ``trunk``.
341 341
342 342 :convert.localtimezone: use local time (as determined by the TZ
343 343 environment variable) for changeset date/times. The default
344 344 is False (use UTC).
345 345
346 346 Source history can be retrieved starting at a specific revision,
347 347 instead of being integrally converted. Only single branch
348 348 conversions are supported.
349 349
350 350 :convert.svn.startrev: specify start Subversion revision number.
351 351 The default is 0.
352 352
353 353 Git Source
354 354 ##########
355 355
356 356 The Git importer converts commits from all reachable branches (refs
357 357 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
358 358 Branches are converted to bookmarks with the same name, with the
359 359 leading 'refs/heads' stripped. Git submodules are converted to Git
360 360 subrepos in Mercurial.
361 361
362 362 The following options can be set with ``--config``:
363 363
364 364 :convert.git.similarity: specify how similar files modified in a
365 365 commit must be to be imported as renames or copies, as a
366 366 percentage between ``0`` (disabled) and ``100`` (files must be
367 367 identical). For example, ``90`` means that a delete/add pair will
368 368 be imported as a rename if more than 90% of the file hasn't
369 369 changed. The default is ``50``.
370 370
371 371 :convert.git.findcopiesharder: while detecting copies, look at all
372 372 files in the working copy instead of just changed ones. This
373 373 is very expensive for large projects, and is only effective when
374 374 ``convert.git.similarity`` is greater than 0. The default is False.
375 375
376 376 :convert.git.renamelimit: perform rename and copy detection up to this
377 377 many changed files in a commit. Increasing this will make rename
378 378 and copy detection more accurate but will significantly slow down
379 379 computation on large projects. The option is only relevant if
380 380 ``convert.git.similarity`` is greater than 0. The default is
381 381 ``400``.
382 382
383 383 :convert.git.committeractions: list of actions to take when processing
384 384 author and committer values.
385 385
386 386 Git commits have separate author (who wrote the commit) and committer
387 387 (who applied the commit) fields. Not all destinations support separate
388 388 author and committer fields (including Mercurial). This config option
389 389 controls what to do with these author and committer fields during
390 390 conversion.
391 391
392 392 A value of ``messagedifferent`` will append a ``committer: ...``
393 393 line to the commit message if the Git committer is different from the
394 394 author. The prefix of that line can be specified using the syntax
395 395 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
396 396 When a prefix is specified, a space will always be inserted between the
397 397 prefix and the value.
398 398
399 399 ``messagealways`` behaves like ``messagedifferent`` except it will
400 400 always result in a ``committer: ...`` line being appended to the commit
401 401 message. This value is mutually exclusive with ``messagedifferent``.
402 402
403 403 ``dropcommitter`` will remove references to the committer. Only
404 404 references to the author will remain. Actions that add references
405 405 to the committer will have no effect when this is set.
406 406
407 407 ``replaceauthor`` will replace the value of the author field with
408 408 the committer. Other actions that add references to the committer
409 409 will still take effect when this is set.
410 410
411 411 The default is ``messagedifferent``.
412 412
413 413 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
414 414 the destination. Some Git repositories store extra metadata in commits.
415 415 By default, this non-default metadata will be lost during conversion.
416 416 Setting this config option can retain that metadata. Some built-in
417 417 keys such as ``parent`` and ``branch`` are not allowed to be copied.
418 418
419 419 :convert.git.remoteprefix: remote refs are converted as bookmarks with
420 420 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
421 421 is 'remote'.
422 422
423 423 :convert.git.saverev: whether to store the original Git commit ID in the
424 424 metadata of the destination commit. The default is True.
425 425
426 426 :convert.git.skipsubmodules: does not convert root level .gitmodules files
427 427 or files with 160000 mode indicating a submodule. Default is False.
428 428
429 429 Perforce Source
430 430 ###############
431 431
432 432 The Perforce (P4) importer can be given a p4 depot path or a
433 433 client specification as source. It will convert all files in the
434 434 source to a flat Mercurial repository, ignoring labels, branches
435 435 and integrations. Note that when a depot path is given you then
436 436 usually should specify a target directory, because otherwise the
437 437 target may be named ``...-hg``.
438 438
439 439 The following options can be set with ``--config``:
440 440
441 441 :convert.p4.encoding: specify the encoding to use when decoding standard
442 442 output of the Perforce command line tool. The default is default system
443 443 encoding.
444 444
445 445 :convert.p4.startrev: specify initial Perforce revision (a
446 446 Perforce changelist number).
447 447
448 448 Mercurial Destination
449 449 #####################
450 450
451 451 The Mercurial destination will recognize Mercurial subrepositories in the
452 452 destination directory, and update the .hgsubstate file automatically if the
453 453 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
454 454 Converting a repository with subrepositories requires converting a single
455 455 repository at a time, from the bottom up.
456 456
457 457 .. container:: verbose
458 458
459 459 An example showing how to convert a repository with subrepositories::
460 460
461 461 # so convert knows the type when it sees a non empty destination
462 462 $ hg init converted
463 463
464 464 $ hg convert orig/sub1 converted/sub1
465 465 $ hg convert orig/sub2 converted/sub2
466 466 $ hg convert orig converted
467 467
468 468 The following options are supported:
469 469
470 470 :convert.hg.clonebranches: dispatch source branches in separate
471 471 clones. The default is False.
472 472
473 473 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
474 474 ``default``.
475 475
476 476 :convert.hg.usebranchnames: preserve branch names. The default is
477 477 True.
478 478
479 479 :convert.hg.sourcename: records the given string as a 'convert_source' extra
480 480 value on each commit made in the target repository. The default is None.
481 481
482 482 :convert.hg.preserve-hash: only works with mercurial sources. Make convert
483 483 prevent performance improvement to the list of modified files in commits
484 484 when such an improvement would cause the hash of a commit to change.
485 485 The default is False.
486 486
487 487 All Destinations
488 488 ################
489 489
490 490 All destination types accept the following options:
491 491
492 492 :convert.skiptags: does not convert tags from the source repo to the target
493 493 repo. The default is False.
494
495 Subversion Destination
496 ######################
497
498 Original commit dates are not preserved by default.
499
500 :convert.svn.dangerous-set-commit-dates: preserve original commit dates,
501 forcefully setting ``svn:date`` revision properties. This option is
502 DANGEROUS and may break some subversion functionality for the resulting
503 repository (e.g. filtering revisions with date ranges in ``svn log``),
504 as original commit dates are not guaranteed to be monotonically
505 increasing.
506
507 For commit dates setting to work destination repository must have
508 ``pre-revprop-change`` hook configured to allow setting of ``svn:date``
509 revision properties. See Subversion documentation for more details.
494 510 """
495 511 return convcmd.convert(ui, src, dest, revmapfile, **opts)
496 512
497 513
498 514 @command(b'debugsvnlog', [], b'hg debugsvnlog', norepo=True)
499 515 def debugsvnlog(ui, **opts):
500 516 return subversion.debugsvnlog(ui, **opts)
501 517
502 518
503 519 @command(
504 520 b'debugcvsps',
505 521 [
506 522 # Main options shared with cvsps-2.1
507 523 (
508 524 b'b',
509 525 b'branches',
510 526 [],
511 527 _(b'only return changes on specified branches'),
512 528 ),
513 529 (b'p', b'prefix', b'', _(b'prefix to remove from file names')),
514 530 (
515 531 b'r',
516 532 b'revisions',
517 533 [],
518 534 _(b'only return changes after or between specified tags'),
519 535 ),
520 536 (b'u', b'update-cache', None, _(b"update cvs log cache")),
521 537 (b'x', b'new-cache', None, _(b"create new cvs log cache")),
522 538 (b'z', b'fuzz', 60, _(b'set commit time fuzz in seconds')),
523 539 (b'', b'root', b'', _(b'specify cvsroot')),
524 540 # Options specific to builtin cvsps
525 541 (b'', b'parents', b'', _(b'show parent changesets')),
526 542 (
527 543 b'',
528 544 b'ancestors',
529 545 b'',
530 546 _(b'show current changeset in ancestor branches'),
531 547 ),
532 548 # Options that are ignored for compatibility with cvsps-2.1
533 549 (b'A', b'cvs-direct', None, _(b'ignored for compatibility')),
534 550 ],
535 551 _(b'hg debugcvsps [OPTION]... [PATH]...'),
536 552 norepo=True,
537 553 )
538 554 def debugcvsps(ui, *args, **opts):
539 555 """create changeset information from CVS
540 556
541 557 This command is intended as a debugging tool for the CVS to
542 558 Mercurial converter, and can be used as a direct replacement for
543 559 cvsps.
544 560
545 561 Hg debugcvsps reads the CVS rlog for current directory (or any
546 562 named directory) in the CVS repository, and converts the log to a
547 563 series of changesets based on matching commit log entries and
548 564 dates."""
549 565 return cvsps.debugcvsps(ui, *args, **opts)
550 566
551 567
552 568 def kwconverted(context, mapping, name):
553 569 ctx = context.resource(mapping, b'ctx')
554 570 rev = ctx.extra().get(b'convert_revision', b'')
555 571 if rev.startswith(b'svn:'):
556 572 if name == b'svnrev':
557 573 return b"%d" % subversion.revsplit(rev)[2]
558 574 elif name == b'svnpath':
559 575 return subversion.revsplit(rev)[1]
560 576 elif name == b'svnuuid':
561 577 return subversion.revsplit(rev)[0]
562 578 return rev
563 579
564 580
565 581 templatekeyword = registrar.templatekeyword()
566 582
567 583
568 584 @templatekeyword(b'svnrev', requires={b'ctx'})
569 585 def kwsvnrev(context, mapping):
570 586 """String. Converted subversion revision number."""
571 587 return kwconverted(context, mapping, b'svnrev')
572 588
573 589
574 590 @templatekeyword(b'svnpath', requires={b'ctx'})
575 591 def kwsvnpath(context, mapping):
576 592 """String. Converted subversion revision project path."""
577 593 return kwconverted(context, mapping, b'svnpath')
578 594
579 595
580 596 @templatekeyword(b'svnuuid', requires={b'ctx'})
581 597 def kwsvnuuid(context, mapping):
582 598 """String. Converted subversion revision repository identifier."""
583 599 return kwconverted(context, mapping, b'svnuuid')
584 600
585 601
586 602 # tell hggettext to extract docstrings from these functions:
587 603 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
@@ -1,1696 +1,1741 b''
1 1 # Subversion 1.4/1.5 Python API backend
2 2 #
3 3 # Copyright(C) 2007 Daniel Holth et al
4 4 from __future__ import absolute_import
5 5
6 6 import codecs
7 7 import locale
8 8 import os
9 9 import re
10 10 import xml.dom.minidom
11 11
12 12 from mercurial.i18n import _
13 13 from mercurial.pycompat import open
14 14 from mercurial import (
15 15 encoding,
16 16 error,
17 17 pycompat,
18 18 util,
19 19 vfs as vfsmod,
20 20 )
21 21 from mercurial.utils import (
22 22 dateutil,
23 23 procutil,
24 24 stringutil,
25 25 )
26 26
27 27 from . import common
28 28
29 29 pickle = util.pickle
30 30 stringio = util.stringio
31 31 propertycache = util.propertycache
32 32 urlerr = util.urlerr
33 33 urlreq = util.urlreq
34 34
35 35 commandline = common.commandline
36 36 commit = common.commit
37 37 converter_sink = common.converter_sink
38 38 converter_source = common.converter_source
39 39 decodeargs = common.decodeargs
40 40 encodeargs = common.encodeargs
41 41 makedatetimestamp = common.makedatetimestamp
42 42 mapfile = common.mapfile
43 43 MissingTool = common.MissingTool
44 44 NoRepo = common.NoRepo
45 45
46 46 # Subversion stuff. Works best with very recent Python SVN bindings
47 47 # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
48 48 # these bindings.
49 49
50 50 try:
51 51 import svn
52 52 import svn.client
53 53 import svn.core
54 54 import svn.ra
55 55 import svn.delta
56 56 from . import transport
57 57 import warnings
58 58
59 59 warnings.filterwarnings(
60 60 'ignore', module='svn.core', category=DeprecationWarning
61 61 )
62 62 svn.core.SubversionException # trigger import to catch error
63 63
64 64 except ImportError:
65 65 svn = None
66 66
67 67
68 68 # In Subversion, paths and URLs are Unicode (encoded as UTF-8), which
69 69 # Subversion converts from / to native strings when interfacing with the OS.
70 70 # When passing paths and URLs to Subversion, we have to recode them such that
71 71 # it roundstrips with what Subversion is doing.
72 72
73 73 fsencoding = None
74 74
75 75
76 76 def init_fsencoding():
77 77 global fsencoding, fsencoding_is_utf8
78 78 if fsencoding is not None:
79 79 return
80 80 if pycompat.iswindows:
81 81 # On Windows, filenames are Unicode, but we store them using the MBCS
82 82 # encoding.
83 83 fsencoding = 'mbcs'
84 84 else:
85 85 # This is the encoding used to convert UTF-8 back to natively-encoded
86 86 # strings in Subversion 1.14.0 or earlier with APR 1.7.0 or earlier.
87 87 with util.with_lc_ctype():
88 88 fsencoding = locale.nl_langinfo(locale.CODESET) or 'ISO-8859-1'
89 89 fsencoding = codecs.lookup(fsencoding).name
90 90 fsencoding_is_utf8 = fsencoding == codecs.lookup('utf-8').name
91 91
92 92
93 93 def fs2svn(s):
94 94 if fsencoding_is_utf8:
95 95 return s
96 96 else:
97 97 return s.decode(fsencoding).encode('utf-8')
98 98
99 99
100 def formatsvndate(date):
101 return dateutil.datestr(date, b'%Y-%m-%dT%H:%M:%S.000000Z')
102
103
104 def parsesvndate(s):
105 # Example SVN datetime. Includes microseconds.
106 # ISO-8601 conformant
107 # '2007-01-04T17:35:00.902377Z'
108 return dateutil.parsedate(s[:19] + b' UTC', [b'%Y-%m-%dT%H:%M:%S'])
109
110
100 111 class SvnPathNotFound(Exception):
101 112 pass
102 113
103 114
104 115 def revsplit(rev):
105 116 """Parse a revision string and return (uuid, path, revnum).
106 117 >>> revsplit(b'svn:a2147622-4a9f-4db4-a8d3-13562ff547b2'
107 118 ... b'/proj%20B/mytrunk/mytrunk@1')
108 119 ('a2147622-4a9f-4db4-a8d3-13562ff547b2', '/proj%20B/mytrunk/mytrunk', 1)
109 120 >>> revsplit(b'svn:8af66a51-67f5-4354-b62c-98d67cc7be1d@1')
110 121 ('', '', 1)
111 122 >>> revsplit(b'@7')
112 123 ('', '', 7)
113 124 >>> revsplit(b'7')
114 125 ('', '', 0)
115 126 >>> revsplit(b'bad')
116 127 ('', '', 0)
117 128 """
118 129 parts = rev.rsplit(b'@', 1)
119 130 revnum = 0
120 131 if len(parts) > 1:
121 132 revnum = int(parts[1])
122 133 parts = parts[0].split(b'/', 1)
123 134 uuid = b''
124 135 mod = b''
125 136 if len(parts) > 1 and parts[0].startswith(b'svn:'):
126 137 uuid = parts[0][4:]
127 138 mod = b'/' + parts[1]
128 139 return uuid, mod, revnum
129 140
130 141
131 142 def quote(s):
132 143 # As of svn 1.7, many svn calls expect "canonical" paths. In
133 144 # theory, we should call svn.core.*canonicalize() on all paths
134 145 # before passing them to the API. Instead, we assume the base url
135 146 # is canonical and copy the behaviour of svn URL encoding function
136 147 # so we can extend it safely with new components. The "safe"
137 148 # characters were taken from the "svn_uri__char_validity" table in
138 149 # libsvn_subr/path.c.
139 150 return urlreq.quote(s, b"!$&'()*+,-./:=@_~")
140 151
141 152
142 153 def geturl(path):
143 154 """Convert path or URL to a SVN URL, encoded in UTF-8.
144 155
145 156 This can raise UnicodeDecodeError if the path or URL can't be converted to
146 157 unicode using `fsencoding`.
147 158 """
148 159 try:
149 160 return svn.client.url_from_path(
150 161 svn.core.svn_path_canonicalize(fs2svn(path))
151 162 )
152 163 except svn.core.SubversionException:
153 164 # svn.client.url_from_path() fails with local repositories
154 165 pass
155 166 if os.path.isdir(path):
156 167 path = os.path.normpath(os.path.abspath(path))
157 168 if pycompat.iswindows:
158 169 path = b'/' + util.normpath(path)
159 170 # Module URL is later compared with the repository URL returned
160 171 # by svn API, which is UTF-8.
161 172 path = fs2svn(path)
162 173 path = b'file://%s' % quote(path)
163 174 return svn.core.svn_path_canonicalize(path)
164 175
165 176
166 177 def optrev(number):
167 178 optrev = svn.core.svn_opt_revision_t()
168 179 optrev.kind = svn.core.svn_opt_revision_number
169 180 optrev.value.number = number
170 181 return optrev
171 182
172 183
173 184 class changedpath(object):
174 185 def __init__(self, p):
175 186 self.copyfrom_path = p.copyfrom_path
176 187 self.copyfrom_rev = p.copyfrom_rev
177 188 self.action = p.action
178 189
179 190
180 191 def get_log_child(
181 192 fp,
182 193 url,
183 194 paths,
184 195 start,
185 196 end,
186 197 limit=0,
187 198 discover_changed_paths=True,
188 199 strict_node_history=False,
189 200 ):
190 201 protocol = -1
191 202
192 203 def receiver(orig_paths, revnum, author, date, message, pool):
193 204 paths = {}
194 205 if orig_paths is not None:
195 206 for k, v in pycompat.iteritems(orig_paths):
196 207 paths[k] = changedpath(v)
197 208 pickle.dump((paths, revnum, author, date, message), fp, protocol)
198 209
199 210 try:
200 211 # Use an ra of our own so that our parent can consume
201 212 # our results without confusing the server.
202 213 t = transport.SvnRaTransport(url=url)
203 214 svn.ra.get_log(
204 215 t.ra,
205 216 paths,
206 217 start,
207 218 end,
208 219 limit,
209 220 discover_changed_paths,
210 221 strict_node_history,
211 222 receiver,
212 223 )
213 224 except IOError:
214 225 # Caller may interrupt the iteration
215 226 pickle.dump(None, fp, protocol)
216 227 except Exception as inst:
217 228 pickle.dump(stringutil.forcebytestr(inst), fp, protocol)
218 229 else:
219 230 pickle.dump(None, fp, protocol)
220 231 fp.flush()
221 232 # With large history, cleanup process goes crazy and suddenly
222 233 # consumes *huge* amount of memory. The output file being closed,
223 234 # there is no need for clean termination.
224 235 os._exit(0)
225 236
226 237
227 238 def debugsvnlog(ui, **opts):
228 239 """Fetch SVN log in a subprocess and channel them back to parent to
229 240 avoid memory collection issues.
230 241 """
231 242 with util.with_lc_ctype():
232 243 if svn is None:
233 244 raise error.Abort(
234 245 _(b'debugsvnlog could not load Subversion python bindings')
235 246 )
236 247
237 248 args = decodeargs(ui.fin.read())
238 249 get_log_child(ui.fout, *args)
239 250
240 251
241 252 class logstream(object):
242 253 """Interruptible revision log iterator."""
243 254
244 255 def __init__(self, stdout):
245 256 self._stdout = stdout
246 257
247 258 def __iter__(self):
248 259 while True:
249 260 try:
250 261 entry = pickle.load(self._stdout)
251 262 except EOFError:
252 263 raise error.Abort(
253 264 _(
254 265 b'Mercurial failed to run itself, check'
255 266 b' hg executable is in PATH'
256 267 )
257 268 )
258 269 try:
259 270 orig_paths, revnum, author, date, message = entry
260 271 except (TypeError, ValueError):
261 272 if entry is None:
262 273 break
263 274 raise error.Abort(_(b"log stream exception '%s'") % entry)
264 275 yield entry
265 276
266 277 def close(self):
267 278 if self._stdout:
268 279 self._stdout.close()
269 280 self._stdout = None
270 281
271 282
272 283 class directlogstream(list):
273 284 """Direct revision log iterator.
274 285 This can be used for debugging and development but it will probably leak
275 286 memory and is not suitable for real conversions."""
276 287
277 288 def __init__(
278 289 self,
279 290 url,
280 291 paths,
281 292 start,
282 293 end,
283 294 limit=0,
284 295 discover_changed_paths=True,
285 296 strict_node_history=False,
286 297 ):
287 298 def receiver(orig_paths, revnum, author, date, message, pool):
288 299 paths = {}
289 300 if orig_paths is not None:
290 301 for k, v in pycompat.iteritems(orig_paths):
291 302 paths[k] = changedpath(v)
292 303 self.append((paths, revnum, author, date, message))
293 304
294 305 # Use an ra of our own so that our parent can consume
295 306 # our results without confusing the server.
296 307 t = transport.SvnRaTransport(url=url)
297 308 svn.ra.get_log(
298 309 t.ra,
299 310 paths,
300 311 start,
301 312 end,
302 313 limit,
303 314 discover_changed_paths,
304 315 strict_node_history,
305 316 receiver,
306 317 )
307 318
308 319 def close(self):
309 320 pass
310 321
311 322
312 323 # Check to see if the given path is a local Subversion repo. Verify this by
313 324 # looking for several svn-specific files and directories in the given
314 325 # directory.
315 326 def filecheck(ui, path, proto):
316 327 for x in (b'locks', b'hooks', b'format', b'db'):
317 328 if not os.path.exists(os.path.join(path, x)):
318 329 return False
319 330 return True
320 331
321 332
322 333 # Check to see if a given path is the root of an svn repo over http. We verify
323 334 # this by requesting a version-controlled URL we know can't exist and looking
324 335 # for the svn-specific "not found" XML.
325 336 def httpcheck(ui, path, proto):
326 337 try:
327 338 opener = urlreq.buildopener()
328 339 rsp = opener.open(
329 340 pycompat.strurl(b'%s://%s/!svn/ver/0/.svn' % (proto, path)), b'rb'
330 341 )
331 342 data = rsp.read()
332 343 except urlerr.httperror as inst:
333 344 if inst.code != 404:
334 345 # Except for 404 we cannot know for sure this is not an svn repo
335 346 ui.warn(
336 347 _(
337 348 b'svn: cannot probe remote repository, assume it could '
338 349 b'be a subversion repository. Use --source-type if you '
339 350 b'know better.\n'
340 351 )
341 352 )
342 353 return True
343 354 data = inst.fp.read()
344 355 except Exception:
345 356 # Could be urlerr.urlerror if the URL is invalid or anything else.
346 357 return False
347 358 return b'<m:human-readable errcode="160013">' in data
348 359
349 360
350 361 protomap = {
351 362 b'http': httpcheck,
352 363 b'https': httpcheck,
353 364 b'file': filecheck,
354 365 }
355 366
356 367
357 368 class NonUtf8PercentEncodedBytes(Exception):
358 369 pass
359 370
360 371
361 372 # Subversion paths are Unicode. Since the percent-decoding is done on
362 373 # UTF-8-encoded strings, percent-encoded bytes are interpreted as UTF-8.
363 374 def url2pathname_like_subversion(unicodepath):
364 375 if pycompat.ispy3:
365 376 # On Python 3, we have to pass unicode to urlreq.url2pathname().
366 377 # Percent-decoded bytes get decoded using UTF-8 and the 'replace' error
367 378 # handler.
368 379 unicodepath = urlreq.url2pathname(unicodepath)
369 380 if u'\N{REPLACEMENT CHARACTER}' in unicodepath:
370 381 raise NonUtf8PercentEncodedBytes
371 382 else:
372 383 return unicodepath
373 384 else:
374 385 # If we passed unicode on Python 2, it would be converted using the
375 386 # latin-1 encoding. Therefore, we pass UTF-8-encoded bytes.
376 387 unicodepath = urlreq.url2pathname(unicodepath.encode('utf-8'))
377 388 try:
378 389 return unicodepath.decode('utf-8')
379 390 except UnicodeDecodeError:
380 391 raise NonUtf8PercentEncodedBytes
381 392
382 393
383 394 def issvnurl(ui, url):
384 395 try:
385 396 proto, path = url.split(b'://', 1)
386 397 if proto == b'file':
387 398 if (
388 399 pycompat.iswindows
389 400 and path[:1] == b'/'
390 401 and path[1:2].isalpha()
391 402 and path[2:6].lower() == b'%3a/'
392 403 ):
393 404 path = path[:2] + b':/' + path[6:]
394 405 try:
395 406 unicodepath = path.decode(fsencoding)
396 407 except UnicodeDecodeError:
397 408 ui.warn(
398 409 _(
399 410 b'Subversion requires that file URLs can be converted '
400 411 b'to Unicode using the current locale encoding (%s)\n'
401 412 )
402 413 % pycompat.sysbytes(fsencoding)
403 414 )
404 415 return False
405 416 try:
406 417 unicodepath = url2pathname_like_subversion(unicodepath)
407 418 except NonUtf8PercentEncodedBytes:
408 419 ui.warn(
409 420 _(
410 421 b'Subversion does not support non-UTF-8 '
411 422 b'percent-encoded bytes in file URLs\n'
412 423 )
413 424 )
414 425 return False
415 426 # Below, we approximate how Subversion checks the path. On Unix, we
416 427 # should therefore convert the path to bytes using `fsencoding`
417 428 # (like Subversion does). On Windows, the right thing would
418 429 # actually be to leave the path as unicode. For now, we restrict
419 430 # the path to MBCS.
420 431 path = unicodepath.encode(fsencoding)
421 432 except ValueError:
422 433 proto = b'file'
423 434 path = os.path.abspath(url)
424 435 try:
425 436 path.decode(fsencoding)
426 437 except UnicodeDecodeError:
427 438 ui.warn(
428 439 _(
429 440 b'Subversion requires that paths can be converted to '
430 441 b'Unicode using the current locale encoding (%s)\n'
431 442 )
432 443 % pycompat.sysbytes(fsencoding)
433 444 )
434 445 return False
435 446 if proto == b'file':
436 447 path = util.pconvert(path)
437 448 elif proto in (b'http', 'https'):
438 449 if not encoding.isasciistr(path):
439 450 ui.warn(
440 451 _(
441 452 b"Subversion sources don't support non-ASCII characters in "
442 453 b"HTTP(S) URLs. Please percent-encode them.\n"
443 454 )
444 455 )
445 456 return False
446 457 check = protomap.get(proto, lambda *args: False)
447 458 while b'/' in path:
448 459 if check(ui, path, proto):
449 460 return True
450 461 path = path.rsplit(b'/', 1)[0]
451 462 return False
452 463
453 464
454 465 # SVN conversion code stolen from bzr-svn and tailor
455 466 #
456 467 # Subversion looks like a versioned filesystem, branches structures
457 468 # are defined by conventions and not enforced by the tool. First,
458 469 # we define the potential branches (modules) as "trunk" and "branches"
459 470 # children directories. Revisions are then identified by their
460 471 # module and revision number (and a repository identifier).
461 472 #
462 473 # The revision graph is really a tree (or a forest). By default, a
463 474 # revision parent is the previous revision in the same module. If the
464 475 # module directory is copied/moved from another module then the
465 476 # revision is the module root and its parent the source revision in
466 477 # the parent module. A revision has at most one parent.
467 478 #
468 479 class svn_source(converter_source):
469 480 def __init__(self, ui, repotype, url, revs=None):
470 481 super(svn_source, self).__init__(ui, repotype, url, revs=revs)
471 482
472 483 init_fsencoding()
473 484 if not (
474 485 url.startswith(b'svn://')
475 486 or url.startswith(b'svn+ssh://')
476 487 or (
477 488 os.path.exists(url)
478 489 and os.path.exists(os.path.join(url, b'.svn'))
479 490 )
480 491 or issvnurl(ui, url)
481 492 ):
482 493 raise NoRepo(
483 494 _(b"%s does not look like a Subversion repository") % url
484 495 )
485 496 if svn is None:
486 497 raise MissingTool(_(b'could not load Subversion python bindings'))
487 498
488 499 try:
489 500 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
490 501 if version < (1, 4):
491 502 raise MissingTool(
492 503 _(
493 504 b'Subversion python bindings %d.%d found, '
494 505 b'1.4 or later required'
495 506 )
496 507 % version
497 508 )
498 509 except AttributeError:
499 510 raise MissingTool(
500 511 _(
501 512 b'Subversion python bindings are too old, 1.4 '
502 513 b'or later required'
503 514 )
504 515 )
505 516
506 517 self.lastrevs = {}
507 518
508 519 latest = None
509 520 try:
510 521 # Support file://path@rev syntax. Useful e.g. to convert
511 522 # deleted branches.
512 523 at = url.rfind(b'@')
513 524 if at >= 0:
514 525 latest = int(url[at + 1 :])
515 526 url = url[:at]
516 527 except ValueError:
517 528 pass
518 529 self.url = geturl(url)
519 530 self.encoding = b'UTF-8' # Subversion is always nominal UTF-8
520 531 try:
521 532 with util.with_lc_ctype():
522 533 self.transport = transport.SvnRaTransport(url=self.url)
523 534 self.ra = self.transport.ra
524 535 self.ctx = self.transport.client
525 536 self.baseurl = svn.ra.get_repos_root(self.ra)
526 537 # Module is either empty or a repository path starting with
527 538 # a slash and not ending with a slash.
528 539 self.module = urlreq.unquote(self.url[len(self.baseurl) :])
529 540 self.prevmodule = None
530 541 self.rootmodule = self.module
531 542 self.commits = {}
532 543 self.paths = {}
533 544 self.uuid = svn.ra.get_uuid(self.ra)
534 545 except svn.core.SubversionException:
535 546 ui.traceback()
536 547 svnversion = b'%d.%d.%d' % (
537 548 svn.core.SVN_VER_MAJOR,
538 549 svn.core.SVN_VER_MINOR,
539 550 svn.core.SVN_VER_MICRO,
540 551 )
541 552 raise NoRepo(
542 553 _(
543 554 b"%s does not look like a Subversion repository "
544 555 b"to libsvn version %s"
545 556 )
546 557 % (self.url, svnversion)
547 558 )
548 559
549 560 if revs:
550 561 if len(revs) > 1:
551 562 raise error.Abort(
552 563 _(
553 564 b'subversion source does not support '
554 565 b'specifying multiple revisions'
555 566 )
556 567 )
557 568 try:
558 569 latest = int(revs[0])
559 570 except ValueError:
560 571 raise error.Abort(
561 572 _(b'svn: revision %s is not an integer') % revs[0]
562 573 )
563 574
564 575 trunkcfg = self.ui.config(b'convert', b'svn.trunk')
565 576 if trunkcfg is None:
566 577 trunkcfg = b'trunk'
567 578 self.trunkname = trunkcfg.strip(b'/')
568 579 self.startrev = self.ui.config(b'convert', b'svn.startrev')
569 580 try:
570 581 self.startrev = int(self.startrev)
571 582 if self.startrev < 0:
572 583 self.startrev = 0
573 584 except ValueError:
574 585 raise error.Abort(
575 586 _(b'svn: start revision %s is not an integer') % self.startrev
576 587 )
577 588
578 589 try:
579 590 with util.with_lc_ctype():
580 591 self.head = self.latest(self.module, latest)
581 592 except SvnPathNotFound:
582 593 self.head = None
583 594 if not self.head:
584 595 raise error.Abort(
585 596 _(b'no revision found in module %s') % self.module
586 597 )
587 598 self.last_changed = self.revnum(self.head)
588 599
589 600 self._changescache = (None, None)
590 601
591 602 if os.path.exists(os.path.join(url, b'.svn/entries')):
592 603 self.wc = url
593 604 else:
594 605 self.wc = None
595 606 self.convertfp = None
596 607
597 608 def before(self):
598 609 self.with_lc_ctype = util.with_lc_ctype()
599 610 self.with_lc_ctype.__enter__()
600 611
601 612 def after(self):
602 613 self.with_lc_ctype.__exit__(None, None, None)
603 614
604 615 def setrevmap(self, revmap):
605 616 lastrevs = {}
606 617 for revid in revmap:
607 618 uuid, module, revnum = revsplit(revid)
608 619 lastrevnum = lastrevs.setdefault(module, revnum)
609 620 if revnum > lastrevnum:
610 621 lastrevs[module] = revnum
611 622 self.lastrevs = lastrevs
612 623
613 624 def exists(self, path, optrev):
614 625 try:
615 626 svn.client.ls(
616 627 self.url.rstrip(b'/') + b'/' + quote(path),
617 628 optrev,
618 629 False,
619 630 self.ctx,
620 631 )
621 632 return True
622 633 except svn.core.SubversionException:
623 634 return False
624 635
625 636 def getheads(self):
626 637 def isdir(path, revnum):
627 638 kind = self._checkpath(path, revnum)
628 639 return kind == svn.core.svn_node_dir
629 640
630 641 def getcfgpath(name, rev):
631 642 cfgpath = self.ui.config(b'convert', b'svn.' + name)
632 643 if cfgpath is not None and cfgpath.strip() == b'':
633 644 return None
634 645 path = (cfgpath or name).strip(b'/')
635 646 if not self.exists(path, rev):
636 647 if self.module.endswith(path) and name == b'trunk':
637 648 # we are converting from inside this directory
638 649 return None
639 650 if cfgpath:
640 651 raise error.Abort(
641 652 _(b'expected %s to be at %r, but not found')
642 653 % (name, path)
643 654 )
644 655 return None
645 656 self.ui.note(
646 657 _(b'found %s at %r\n') % (name, pycompat.bytestr(path))
647 658 )
648 659 return path
649 660
650 661 rev = optrev(self.last_changed)
651 662 oldmodule = b''
652 663 trunk = getcfgpath(b'trunk', rev)
653 664 self.tags = getcfgpath(b'tags', rev)
654 665 branches = getcfgpath(b'branches', rev)
655 666
656 667 # If the project has a trunk or branches, we will extract heads
657 668 # from them. We keep the project root otherwise.
658 669 if trunk:
659 670 oldmodule = self.module or b''
660 671 self.module += b'/' + trunk
661 672 self.head = self.latest(self.module, self.last_changed)
662 673 if not self.head:
663 674 raise error.Abort(
664 675 _(b'no revision found in module %s') % self.module
665 676 )
666 677
667 678 # First head in the list is the module's head
668 679 self.heads = [self.head]
669 680 if self.tags is not None:
670 681 self.tags = b'%s/%s' % (oldmodule, (self.tags or b'tags'))
671 682
672 683 # Check if branches bring a few more heads to the list
673 684 if branches:
674 685 rpath = self.url.strip(b'/')
675 686 branchnames = svn.client.ls(
676 687 rpath + b'/' + quote(branches), rev, False, self.ctx
677 688 )
678 689 for branch in sorted(branchnames):
679 690 module = b'%s/%s/%s' % (oldmodule, branches, branch)
680 691 if not isdir(module, self.last_changed):
681 692 continue
682 693 brevid = self.latest(module, self.last_changed)
683 694 if not brevid:
684 695 self.ui.note(_(b'ignoring empty branch %s\n') % branch)
685 696 continue
686 697 self.ui.note(
687 698 _(b'found branch %s at %d\n')
688 699 % (branch, self.revnum(brevid))
689 700 )
690 701 self.heads.append(brevid)
691 702
692 703 if self.startrev and self.heads:
693 704 if len(self.heads) > 1:
694 705 raise error.Abort(
695 706 _(
696 707 b'svn: start revision is not supported '
697 708 b'with more than one branch'
698 709 )
699 710 )
700 711 revnum = self.revnum(self.heads[0])
701 712 if revnum < self.startrev:
702 713 raise error.Abort(
703 714 _(b'svn: no revision found after start revision %d')
704 715 % self.startrev
705 716 )
706 717
707 718 return self.heads
708 719
709 720 def _getchanges(self, rev, full):
710 721 (paths, parents) = self.paths[rev]
711 722 copies = {}
712 723 if parents:
713 724 files, self.removed, copies = self.expandpaths(rev, paths, parents)
714 725 if full or not parents:
715 726 # Perform a full checkout on roots
716 727 uuid, module, revnum = revsplit(rev)
717 728 entries = svn.client.ls(
718 729 self.baseurl + quote(module), optrev(revnum), True, self.ctx
719 730 )
720 731 files = [
721 732 n
722 733 for n, e in pycompat.iteritems(entries)
723 734 if e.kind == svn.core.svn_node_file
724 735 ]
725 736 self.removed = set()
726 737
727 738 files.sort()
728 739 files = pycompat.ziplist(files, [rev] * len(files))
729 740 return (files, copies)
730 741
731 742 def getchanges(self, rev, full):
732 743 # reuse cache from getchangedfiles
733 744 if self._changescache[0] == rev and not full:
734 745 (files, copies) = self._changescache[1]
735 746 else:
736 747 (files, copies) = self._getchanges(rev, full)
737 748 # caller caches the result, so free it here to release memory
738 749 del self.paths[rev]
739 750 return (files, copies, set())
740 751
741 752 def getchangedfiles(self, rev, i):
742 753 # called from filemap - cache computed values for reuse in getchanges
743 754 (files, copies) = self._getchanges(rev, False)
744 755 self._changescache = (rev, (files, copies))
745 756 return [f[0] for f in files]
746 757
747 758 def getcommit(self, rev):
748 759 if rev not in self.commits:
749 760 uuid, module, revnum = revsplit(rev)
750 761 self.module = module
751 762 self.reparent(module)
752 763 # We assume that:
753 764 # - requests for revisions after "stop" come from the
754 765 # revision graph backward traversal. Cache all of them
755 766 # down to stop, they will be used eventually.
756 767 # - requests for revisions before "stop" come to get
757 768 # isolated branches parents. Just fetch what is needed.
758 769 stop = self.lastrevs.get(module, 0)
759 770 if revnum < stop:
760 771 stop = revnum + 1
761 772 self._fetch_revisions(revnum, stop)
762 773 if rev not in self.commits:
763 774 raise error.Abort(_(b'svn: revision %s not found') % revnum)
764 775 revcommit = self.commits[rev]
765 776 # caller caches the result, so free it here to release memory
766 777 del self.commits[rev]
767 778 return revcommit
768 779
769 780 def checkrevformat(self, revstr, mapname=b'splicemap'):
770 781 """ fails if revision format does not match the correct format"""
771 782 if not re.match(
772 783 br'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-'
773 784 br'[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]'
774 785 br'{12,12}(.*)@[0-9]+$',
775 786 revstr,
776 787 ):
777 788 raise error.Abort(
778 789 _(b'%s entry %s is not a valid revision identifier')
779 790 % (mapname, revstr)
780 791 )
781 792
782 793 def numcommits(self):
783 794 return int(self.head.rsplit(b'@', 1)[1]) - self.startrev
784 795
785 796 def gettags(self):
786 797 tags = {}
787 798 if self.tags is None:
788 799 return tags
789 800
790 801 # svn tags are just a convention, project branches left in a
791 802 # 'tags' directory. There is no other relationship than
792 803 # ancestry, which is expensive to discover and makes them hard
793 804 # to update incrementally. Worse, past revisions may be
794 805 # referenced by tags far away in the future, requiring a deep
795 806 # history traversal on every calculation. Current code
796 807 # performs a single backward traversal, tracking moves within
797 808 # the tags directory (tag renaming) and recording a new tag
798 809 # everytime a project is copied from outside the tags
799 810 # directory. It also lists deleted tags, this behaviour may
800 811 # change in the future.
801 812 pendings = []
802 813 tagspath = self.tags
803 814 start = svn.ra.get_latest_revnum(self.ra)
804 815 stream = self._getlog([self.tags], start, self.startrev)
805 816 try:
806 817 for entry in stream:
807 818 origpaths, revnum, author, date, message = entry
808 819 if not origpaths:
809 820 origpaths = []
810 821 copies = [
811 822 (e.copyfrom_path, e.copyfrom_rev, p)
812 823 for p, e in pycompat.iteritems(origpaths)
813 824 if e.copyfrom_path
814 825 ]
815 826 # Apply moves/copies from more specific to general
816 827 copies.sort(reverse=True)
817 828
818 829 srctagspath = tagspath
819 830 if copies and copies[-1][2] == tagspath:
820 831 # Track tags directory moves
821 832 srctagspath = copies.pop()[0]
822 833
823 834 for source, sourcerev, dest in copies:
824 835 if not dest.startswith(tagspath + b'/'):
825 836 continue
826 837 for tag in pendings:
827 838 if tag[0].startswith(dest):
828 839 tagpath = source + tag[0][len(dest) :]
829 840 tag[:2] = [tagpath, sourcerev]
830 841 break
831 842 else:
832 843 pendings.append([source, sourcerev, dest])
833 844
834 845 # Filter out tags with children coming from different
835 846 # parts of the repository like:
836 847 # /tags/tag.1 (from /trunk:10)
837 848 # /tags/tag.1/foo (from /branches/foo:12)
838 849 # Here/tags/tag.1 discarded as well as its children.
839 850 # It happens with tools like cvs2svn. Such tags cannot
840 851 # be represented in mercurial.
841 852 addeds = {
842 853 p: e.copyfrom_path
843 854 for p, e in pycompat.iteritems(origpaths)
844 855 if e.action == b'A' and e.copyfrom_path
845 856 }
846 857 badroots = set()
847 858 for destroot in addeds:
848 859 for source, sourcerev, dest in pendings:
849 860 if not dest.startswith(
850 861 destroot + b'/'
851 862 ) or source.startswith(addeds[destroot] + b'/'):
852 863 continue
853 864 badroots.add(destroot)
854 865 break
855 866
856 867 for badroot in badroots:
857 868 pendings = [
858 869 p
859 870 for p in pendings
860 871 if p[2] != badroot
861 872 and not p[2].startswith(badroot + b'/')
862 873 ]
863 874
864 875 # Tell tag renamings from tag creations
865 876 renamings = []
866 877 for source, sourcerev, dest in pendings:
867 878 tagname = dest.split(b'/')[-1]
868 879 if source.startswith(srctagspath):
869 880 renamings.append([source, sourcerev, tagname])
870 881 continue
871 882 if tagname in tags:
872 883 # Keep the latest tag value
873 884 continue
874 885 # From revision may be fake, get one with changes
875 886 try:
876 887 tagid = self.latest(source, sourcerev)
877 888 if tagid and tagname not in tags:
878 889 tags[tagname] = tagid
879 890 except SvnPathNotFound:
880 891 # It happens when we are following directories
881 892 # we assumed were copied with their parents
882 893 # but were really created in the tag
883 894 # directory.
884 895 pass
885 896 pendings = renamings
886 897 tagspath = srctagspath
887 898 finally:
888 899 stream.close()
889 900 return tags
890 901
891 902 def converted(self, rev, destrev):
892 903 if not self.wc:
893 904 return
894 905 if self.convertfp is None:
895 906 self.convertfp = open(
896 907 os.path.join(self.wc, b'.svn', b'hg-shamap'), b'ab'
897 908 )
898 909 self.convertfp.write(
899 910 util.tonativeeol(b'%s %d\n' % (destrev, self.revnum(rev)))
900 911 )
901 912 self.convertfp.flush()
902 913
903 914 def revid(self, revnum, module=None):
904 915 return b'svn:%s%s@%d' % (self.uuid, module or self.module, revnum)
905 916
906 917 def revnum(self, rev):
907 918 return int(rev.split(b'@')[-1])
908 919
909 920 def latest(self, path, stop=None):
910 921 """Find the latest revid affecting path, up to stop revision
911 922 number. If stop is None, default to repository latest
912 923 revision. It may return a revision in a different module,
913 924 since a branch may be moved without a change being
914 925 reported. Return None if computed module does not belong to
915 926 rootmodule subtree.
916 927 """
917 928
918 929 def findchanges(path, start, stop=None):
919 930 stream = self._getlog([path], start, stop or 1)
920 931 try:
921 932 for entry in stream:
922 933 paths, revnum, author, date, message = entry
923 934 if stop is None and paths:
924 935 # We do not know the latest changed revision,
925 936 # keep the first one with changed paths.
926 937 break
927 938 if stop is not None and revnum <= stop:
928 939 break
929 940
930 941 for p in paths:
931 942 if not path.startswith(p) or not paths[p].copyfrom_path:
932 943 continue
933 944 newpath = paths[p].copyfrom_path + path[len(p) :]
934 945 self.ui.debug(
935 946 b"branch renamed from %s to %s at %d\n"
936 947 % (path, newpath, revnum)
937 948 )
938 949 path = newpath
939 950 break
940 951 if not paths:
941 952 revnum = None
942 953 return revnum, path
943 954 finally:
944 955 stream.close()
945 956
946 957 if not path.startswith(self.rootmodule):
947 958 # Requests on foreign branches may be forbidden at server level
948 959 self.ui.debug(b'ignoring foreign branch %r\n' % path)
949 960 return None
950 961
951 962 if stop is None:
952 963 stop = svn.ra.get_latest_revnum(self.ra)
953 964 try:
954 965 prevmodule = self.reparent(b'')
955 966 dirent = svn.ra.stat(self.ra, path.strip(b'/'), stop)
956 967 self.reparent(prevmodule)
957 968 except svn.core.SubversionException:
958 969 dirent = None
959 970 if not dirent:
960 971 raise SvnPathNotFound(
961 972 _(b'%s not found up to revision %d') % (path, stop)
962 973 )
963 974
964 975 # stat() gives us the previous revision on this line of
965 976 # development, but it might be in *another module*. Fetch the
966 977 # log and detect renames down to the latest revision.
967 978 revnum, realpath = findchanges(path, stop, dirent.created_rev)
968 979 if revnum is None:
969 980 # Tools like svnsync can create empty revision, when
970 981 # synchronizing only a subtree for instance. These empty
971 982 # revisions created_rev still have their original values
972 983 # despite all changes having disappeared and can be
973 984 # returned by ra.stat(), at least when stating the root
974 985 # module. In that case, do not trust created_rev and scan
975 986 # the whole history.
976 987 revnum, realpath = findchanges(path, stop)
977 988 if revnum is None:
978 989 self.ui.debug(b'ignoring empty branch %r\n' % realpath)
979 990 return None
980 991
981 992 if not realpath.startswith(self.rootmodule):
982 993 self.ui.debug(b'ignoring foreign branch %r\n' % realpath)
983 994 return None
984 995 return self.revid(revnum, realpath)
985 996
986 997 def reparent(self, module):
987 998 """Reparent the svn transport and return the previous parent."""
988 999 if self.prevmodule == module:
989 1000 return module
990 1001 svnurl = self.baseurl + quote(module)
991 1002 prevmodule = self.prevmodule
992 1003 if prevmodule is None:
993 1004 prevmodule = b''
994 1005 self.ui.debug(b"reparent to %s\n" % svnurl)
995 1006 svn.ra.reparent(self.ra, svnurl)
996 1007 self.prevmodule = module
997 1008 return prevmodule
998 1009
999 1010 def expandpaths(self, rev, paths, parents):
1000 1011 changed, removed = set(), set()
1001 1012 copies = {}
1002 1013
1003 1014 new_module, revnum = revsplit(rev)[1:]
1004 1015 if new_module != self.module:
1005 1016 self.module = new_module
1006 1017 self.reparent(self.module)
1007 1018
1008 1019 progress = self.ui.makeprogress(
1009 1020 _(b'scanning paths'), unit=_(b'paths'), total=len(paths)
1010 1021 )
1011 1022 for i, (path, ent) in enumerate(paths):
1012 1023 progress.update(i, item=path)
1013 1024 entrypath = self.getrelpath(path)
1014 1025
1015 1026 kind = self._checkpath(entrypath, revnum)
1016 1027 if kind == svn.core.svn_node_file:
1017 1028 changed.add(self.recode(entrypath))
1018 1029 if not ent.copyfrom_path or not parents:
1019 1030 continue
1020 1031 # Copy sources not in parent revisions cannot be
1021 1032 # represented, ignore their origin for now
1022 1033 pmodule, prevnum = revsplit(parents[0])[1:]
1023 1034 if ent.copyfrom_rev < prevnum:
1024 1035 continue
1025 1036 copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
1026 1037 if not copyfrom_path:
1027 1038 continue
1028 1039 self.ui.debug(
1029 1040 b"copied to %s from %s@%d\n"
1030 1041 % (entrypath, copyfrom_path, ent.copyfrom_rev)
1031 1042 )
1032 1043 copies[self.recode(entrypath)] = self.recode(copyfrom_path)
1033 1044 elif kind == 0: # gone, but had better be a deleted *file*
1034 1045 self.ui.debug(b"gone from %d\n" % ent.copyfrom_rev)
1035 1046 pmodule, prevnum = revsplit(parents[0])[1:]
1036 1047 parentpath = pmodule + b"/" + entrypath
1037 1048 fromkind = self._checkpath(entrypath, prevnum, pmodule)
1038 1049
1039 1050 if fromkind == svn.core.svn_node_file:
1040 1051 removed.add(self.recode(entrypath))
1041 1052 elif fromkind == svn.core.svn_node_dir:
1042 1053 oroot = parentpath.strip(b'/')
1043 1054 nroot = path.strip(b'/')
1044 1055 children = self._iterfiles(oroot, prevnum)
1045 1056 for childpath in children:
1046 1057 childpath = childpath.replace(oroot, nroot)
1047 1058 childpath = self.getrelpath(b"/" + childpath, pmodule)
1048 1059 if childpath:
1049 1060 removed.add(self.recode(childpath))
1050 1061 else:
1051 1062 self.ui.debug(
1052 1063 b'unknown path in revision %d: %s\n' % (revnum, path)
1053 1064 )
1054 1065 elif kind == svn.core.svn_node_dir:
1055 1066 if ent.action == b'M':
1056 1067 # If the directory just had a prop change,
1057 1068 # then we shouldn't need to look for its children.
1058 1069 continue
1059 1070 if ent.action == b'R' and parents:
1060 1071 # If a directory is replacing a file, mark the previous
1061 1072 # file as deleted
1062 1073 pmodule, prevnum = revsplit(parents[0])[1:]
1063 1074 pkind = self._checkpath(entrypath, prevnum, pmodule)
1064 1075 if pkind == svn.core.svn_node_file:
1065 1076 removed.add(self.recode(entrypath))
1066 1077 elif pkind == svn.core.svn_node_dir:
1067 1078 # We do not know what files were kept or removed,
1068 1079 # mark them all as changed.
1069 1080 for childpath in self._iterfiles(pmodule, prevnum):
1070 1081 childpath = self.getrelpath(b"/" + childpath)
1071 1082 if childpath:
1072 1083 changed.add(self.recode(childpath))
1073 1084
1074 1085 for childpath in self._iterfiles(path, revnum):
1075 1086 childpath = self.getrelpath(b"/" + childpath)
1076 1087 if childpath:
1077 1088 changed.add(self.recode(childpath))
1078 1089
1079 1090 # Handle directory copies
1080 1091 if not ent.copyfrom_path or not parents:
1081 1092 continue
1082 1093 # Copy sources not in parent revisions cannot be
1083 1094 # represented, ignore their origin for now
1084 1095 pmodule, prevnum = revsplit(parents[0])[1:]
1085 1096 if ent.copyfrom_rev < prevnum:
1086 1097 continue
1087 1098 copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
1088 1099 if not copyfrompath:
1089 1100 continue
1090 1101 self.ui.debug(
1091 1102 b"mark %s came from %s:%d\n"
1092 1103 % (path, copyfrompath, ent.copyfrom_rev)
1093 1104 )
1094 1105 children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev)
1095 1106 for childpath in children:
1096 1107 childpath = self.getrelpath(b"/" + childpath, pmodule)
1097 1108 if not childpath:
1098 1109 continue
1099 1110 copytopath = path + childpath[len(copyfrompath) :]
1100 1111 copytopath = self.getrelpath(copytopath)
1101 1112 copies[self.recode(copytopath)] = self.recode(childpath)
1102 1113
1103 1114 progress.complete()
1104 1115 changed.update(removed)
1105 1116 return (list(changed), removed, copies)
1106 1117
1107 1118 def _fetch_revisions(self, from_revnum, to_revnum):
1108 1119 if from_revnum < to_revnum:
1109 1120 from_revnum, to_revnum = to_revnum, from_revnum
1110 1121
1111 1122 self.child_cset = None
1112 1123
1113 1124 def parselogentry(orig_paths, revnum, author, date, message):
1114 1125 """Return the parsed commit object or None, and True if
1115 1126 the revision is a branch root.
1116 1127 """
1117 1128 self.ui.debug(
1118 1129 b"parsing revision %d (%d changes)\n"
1119 1130 % (revnum, len(orig_paths))
1120 1131 )
1121 1132
1122 1133 branched = False
1123 1134 rev = self.revid(revnum)
1124 1135 # branch log might return entries for a parent we already have
1125 1136
1126 1137 if rev in self.commits or revnum < to_revnum:
1127 1138 return None, branched
1128 1139
1129 1140 parents = []
1130 1141 # check whether this revision is the start of a branch or part
1131 1142 # of a branch renaming
1132 1143 orig_paths = sorted(pycompat.iteritems(orig_paths))
1133 1144 root_paths = [
1134 1145 (p, e) for p, e in orig_paths if self.module.startswith(p)
1135 1146 ]
1136 1147 if root_paths:
1137 1148 path, ent = root_paths[-1]
1138 1149 if ent.copyfrom_path:
1139 1150 branched = True
1140 1151 newpath = ent.copyfrom_path + self.module[len(path) :]
1141 1152 # ent.copyfrom_rev may not be the actual last revision
1142 1153 previd = self.latest(newpath, ent.copyfrom_rev)
1143 1154 if previd is not None:
1144 1155 prevmodule, prevnum = revsplit(previd)[1:]
1145 1156 if prevnum >= self.startrev:
1146 1157 parents = [previd]
1147 1158 self.ui.note(
1148 1159 _(b'found parent of branch %s at %d: %s\n')
1149 1160 % (self.module, prevnum, prevmodule)
1150 1161 )
1151 1162 else:
1152 1163 self.ui.debug(b"no copyfrom path, don't know what to do.\n")
1153 1164
1154 1165 paths = []
1155 1166 # filter out unrelated paths
1156 1167 for path, ent in orig_paths:
1157 1168 if self.getrelpath(path) is None:
1158 1169 continue
1159 1170 paths.append((path, ent))
1160 1171
1161 # Example SVN datetime. Includes microseconds.
1162 # ISO-8601 conformant
1163 # '2007-01-04T17:35:00.902377Z'
1164 date = dateutil.parsedate(
1165 date[:19] + b" UTC", [b"%Y-%m-%dT%H:%M:%S"]
1166 )
1172 date = parsesvndate(date)
1167 1173 if self.ui.configbool(b'convert', b'localtimezone'):
1168 1174 date = makedatetimestamp(date[0])
1169 1175
1170 1176 if message:
1171 1177 log = self.recode(message)
1172 1178 else:
1173 1179 log = b''
1174 1180
1175 1181 if author:
1176 1182 author = self.recode(author)
1177 1183 else:
1178 1184 author = b''
1179 1185
1180 1186 try:
1181 1187 branch = self.module.split(b"/")[-1]
1182 1188 if branch == self.trunkname:
1183 1189 branch = None
1184 1190 except IndexError:
1185 1191 branch = None
1186 1192
1187 1193 cset = commit(
1188 1194 author=author,
1189 1195 date=dateutil.datestr(date, b'%Y-%m-%d %H:%M:%S %1%2'),
1190 1196 desc=log,
1191 1197 parents=parents,
1192 1198 branch=branch,
1193 1199 rev=rev,
1194 1200 )
1195 1201
1196 1202 self.commits[rev] = cset
1197 1203 # The parents list is *shared* among self.paths and the
1198 1204 # commit object. Both will be updated below.
1199 1205 self.paths[rev] = (paths, cset.parents)
1200 1206 if self.child_cset and not self.child_cset.parents:
1201 1207 self.child_cset.parents[:] = [rev]
1202 1208 self.child_cset = cset
1203 1209 return cset, branched
1204 1210
1205 1211 self.ui.note(
1206 1212 _(b'fetching revision log for "%s" from %d to %d\n')
1207 1213 % (self.module, from_revnum, to_revnum)
1208 1214 )
1209 1215
1210 1216 try:
1211 1217 firstcset = None
1212 1218 lastonbranch = False
1213 1219 stream = self._getlog([self.module], from_revnum, to_revnum)
1214 1220 try:
1215 1221 for entry in stream:
1216 1222 paths, revnum, author, date, message = entry
1217 1223 if revnum < self.startrev:
1218 1224 lastonbranch = True
1219 1225 break
1220 1226 if not paths:
1221 1227 self.ui.debug(b'revision %d has no entries\n' % revnum)
1222 1228 # If we ever leave the loop on an empty
1223 1229 # revision, do not try to get a parent branch
1224 1230 lastonbranch = lastonbranch or revnum == 0
1225 1231 continue
1226 1232 cset, lastonbranch = parselogentry(
1227 1233 paths, revnum, author, date, message
1228 1234 )
1229 1235 if cset:
1230 1236 firstcset = cset
1231 1237 if lastonbranch:
1232 1238 break
1233 1239 finally:
1234 1240 stream.close()
1235 1241
1236 1242 if not lastonbranch and firstcset and not firstcset.parents:
1237 1243 # The first revision of the sequence (the last fetched one)
1238 1244 # has invalid parents if not a branch root. Find the parent
1239 1245 # revision now, if any.
1240 1246 try:
1241 1247 firstrevnum = self.revnum(firstcset.rev)
1242 1248 if firstrevnum > 1:
1243 1249 latest = self.latest(self.module, firstrevnum - 1)
1244 1250 if latest:
1245 1251 firstcset.parents.append(latest)
1246 1252 except SvnPathNotFound:
1247 1253 pass
1248 1254 except svn.core.SubversionException as xxx_todo_changeme:
1249 1255 (inst, num) = xxx_todo_changeme.args
1250 1256 if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
1251 1257 raise error.Abort(
1252 1258 _(b'svn: branch has no revision %s') % to_revnum
1253 1259 )
1254 1260 raise
1255 1261
1256 1262 def getfile(self, file, rev):
1257 1263 # TODO: ra.get_file transmits the whole file instead of diffs.
1258 1264 if file in self.removed:
1259 1265 return None, None
1260 1266 try:
1261 1267 new_module, revnum = revsplit(rev)[1:]
1262 1268 if self.module != new_module:
1263 1269 self.module = new_module
1264 1270 self.reparent(self.module)
1265 1271 io = stringio()
1266 1272 info = svn.ra.get_file(self.ra, file, revnum, io)
1267 1273 data = io.getvalue()
1268 1274 # ra.get_file() seems to keep a reference on the input buffer
1269 1275 # preventing collection. Release it explicitly.
1270 1276 io.close()
1271 1277 if isinstance(info, list):
1272 1278 info = info[-1]
1273 1279 mode = (b"svn:executable" in info) and b'x' or b''
1274 1280 mode = (b"svn:special" in info) and b'l' or mode
1275 1281 except svn.core.SubversionException as e:
1276 1282 notfound = (
1277 1283 svn.core.SVN_ERR_FS_NOT_FOUND,
1278 1284 svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND,
1279 1285 )
1280 1286 if e.apr_err in notfound: # File not found
1281 1287 return None, None
1282 1288 raise
1283 1289 if mode == b'l':
1284 1290 link_prefix = b"link "
1285 1291 if data.startswith(link_prefix):
1286 1292 data = data[len(link_prefix) :]
1287 1293 return data, mode
1288 1294
1289 1295 def _iterfiles(self, path, revnum):
1290 1296 """Enumerate all files in path at revnum, recursively."""
1291 1297 path = path.strip(b'/')
1292 1298 pool = svn.core.Pool()
1293 1299 rpath = b'/'.join([self.baseurl, quote(path)]).strip(b'/')
1294 1300 entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool)
1295 1301 if path:
1296 1302 path += b'/'
1297 1303 return (
1298 1304 (path + p)
1299 1305 for p, e in pycompat.iteritems(entries)
1300 1306 if e.kind == svn.core.svn_node_file
1301 1307 )
1302 1308
1303 1309 def getrelpath(self, path, module=None):
1304 1310 if module is None:
1305 1311 module = self.module
1306 1312 # Given the repository url of this wc, say
1307 1313 # "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
1308 1314 # extract the "entry" portion (a relative path) from what
1309 1315 # svn log --xml says, i.e.
1310 1316 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
1311 1317 # that is to say "tests/PloneTestCase.py"
1312 1318 if path.startswith(module):
1313 1319 relative = path.rstrip(b'/')[len(module) :]
1314 1320 if relative.startswith(b'/'):
1315 1321 return relative[1:]
1316 1322 elif relative == b'':
1317 1323 return relative
1318 1324
1319 1325 # The path is outside our tracked tree...
1320 1326 self.ui.debug(
1321 1327 b'%r is not under %r, ignoring\n'
1322 1328 % (pycompat.bytestr(path), pycompat.bytestr(module))
1323 1329 )
1324 1330 return None
1325 1331
1326 1332 def _checkpath(self, path, revnum, module=None):
1327 1333 if module is not None:
1328 1334 prevmodule = self.reparent(b'')
1329 1335 path = module + b'/' + path
1330 1336 try:
1331 1337 # ra.check_path does not like leading slashes very much, it leads
1332 1338 # to PROPFIND subversion errors
1333 1339 return svn.ra.check_path(self.ra, path.strip(b'/'), revnum)
1334 1340 finally:
1335 1341 if module is not None:
1336 1342 self.reparent(prevmodule)
1337 1343
1338 1344 def _getlog(
1339 1345 self,
1340 1346 paths,
1341 1347 start,
1342 1348 end,
1343 1349 limit=0,
1344 1350 discover_changed_paths=True,
1345 1351 strict_node_history=False,
1346 1352 ):
1347 1353 # Normalize path names, svn >= 1.5 only wants paths relative to
1348 1354 # supplied URL
1349 1355 relpaths = []
1350 1356 for p in paths:
1351 1357 if not p.startswith(b'/'):
1352 1358 p = self.module + b'/' + p
1353 1359 relpaths.append(p.strip(b'/'))
1354 1360 args = [
1355 1361 self.baseurl,
1356 1362 relpaths,
1357 1363 start,
1358 1364 end,
1359 1365 limit,
1360 1366 discover_changed_paths,
1361 1367 strict_node_history,
1362 1368 ]
1363 1369 # developer config: convert.svn.debugsvnlog
1364 1370 if not self.ui.configbool(b'convert', b'svn.debugsvnlog'):
1365 1371 return directlogstream(*args)
1366 1372 arg = encodeargs(args)
1367 1373 hgexe = procutil.hgexecutable()
1368 1374 cmd = b'%s debugsvnlog' % procutil.shellquote(hgexe)
1369 1375 stdin, stdout = procutil.popen2(cmd)
1370 1376 stdin.write(arg)
1371 1377 try:
1372 1378 stdin.close()
1373 1379 except IOError:
1374 1380 raise error.Abort(
1375 1381 _(
1376 1382 b'Mercurial failed to run itself, check'
1377 1383 b' hg executable is in PATH'
1378 1384 )
1379 1385 )
1380 1386 return logstream(stdout)
1381 1387
1382 1388
1383 pre_revprop_change = b'''#!/bin/sh
1389 pre_revprop_change_template = b'''#!/bin/sh
1384 1390
1385 1391 REPOS="$1"
1386 1392 REV="$2"
1387 1393 USER="$3"
1388 1394 PROPNAME="$4"
1389 1395 ACTION="$5"
1390 1396
1391 if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
1392 if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi
1393 if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi
1397 %(rules)s
1394 1398
1395 1399 echo "Changing prohibited revision property" >&2
1396 1400 exit 1
1397 1401 '''
1398 1402
1399 1403
1404 def gen_pre_revprop_change_hook(prop_actions_allowed):
1405 rules = []
1406 for action, propname in prop_actions_allowed:
1407 rules.append(
1408 (
1409 b'if [ "$ACTION" = "%s" -a "$PROPNAME" = "%s" ]; '
1410 b'then exit 0; fi'
1411 )
1412 % (action, propname)
1413 )
1414 return pre_revprop_change_template % {b'rules': b'\n'.join(rules)}
1415
1416
1400 1417 class svn_sink(converter_sink, commandline):
1401 1418 commit_re = re.compile(br'Committed revision (\d+).', re.M)
1402 1419 uuid_re = re.compile(br'Repository UUID:\s*(\S+)', re.M)
1403 1420
1404 1421 def prerun(self):
1405 1422 if self.wc:
1406 1423 os.chdir(self.wc)
1407 1424
1408 1425 def postrun(self):
1409 1426 if self.wc:
1410 1427 os.chdir(self.cwd)
1411 1428
1412 1429 def join(self, name):
1413 1430 return os.path.join(self.wc, b'.svn', name)
1414 1431
1415 1432 def revmapfile(self):
1416 1433 return self.join(b'hg-shamap')
1417 1434
1418 1435 def authorfile(self):
1419 1436 return self.join(b'hg-authormap')
1420 1437
1421 1438 def __init__(self, ui, repotype, path):
1422 1439
1423 1440 converter_sink.__init__(self, ui, repotype, path)
1424 1441 commandline.__init__(self, ui, b'svn')
1425 1442 self.delete = []
1426 1443 self.setexec = []
1427 1444 self.delexec = []
1428 1445 self.copies = []
1429 1446 self.wc = None
1430 1447 self.cwd = encoding.getcwd()
1431 1448
1432 1449 created = False
1433 1450 if os.path.isfile(os.path.join(path, b'.svn', b'entries')):
1434 1451 self.wc = os.path.realpath(path)
1435 1452 self.run0(b'update')
1436 1453 else:
1437 1454 if not re.search(br'^(file|http|https|svn|svn\+ssh)://', path):
1438 1455 path = os.path.realpath(path)
1439 1456 if os.path.isdir(os.path.dirname(path)):
1440 1457 if not os.path.exists(
1441 1458 os.path.join(path, b'db', b'fs-type')
1442 1459 ):
1443 1460 ui.status(
1444 1461 _(b"initializing svn repository '%s'\n")
1445 1462 % os.path.basename(path)
1446 1463 )
1447 1464 commandline(ui, b'svnadmin').run0(b'create', path)
1448 1465 created = path
1449 1466 path = util.normpath(path)
1450 1467 if not path.startswith(b'/'):
1451 1468 path = b'/' + path
1452 1469 path = b'file://' + path
1453 1470
1454 1471 wcpath = os.path.join(
1455 1472 encoding.getcwd(), os.path.basename(path) + b'-wc'
1456 1473 )
1457 1474 ui.status(
1458 1475 _(b"initializing svn working copy '%s'\n")
1459 1476 % os.path.basename(wcpath)
1460 1477 )
1461 1478 self.run0(b'checkout', path, wcpath)
1462 1479
1463 1480 self.wc = wcpath
1464 1481 self.opener = vfsmod.vfs(self.wc)
1465 1482 self.wopener = vfsmod.vfs(self.wc)
1466 1483 self.childmap = mapfile(ui, self.join(b'hg-childmap'))
1467 1484 if util.checkexec(self.wc):
1468 1485 self.is_exec = util.isexec
1469 1486 else:
1470 1487 self.is_exec = None
1471 1488
1472 1489 if created:
1490 prop_actions_allowed = [
1491 (b'M', b'svn:log'),
1492 (b'A', b'hg:convert-branch'),
1493 (b'A', b'hg:convert-rev'),
1494 ]
1495
1496 if self.ui.configbool(
1497 b'convert', b'svn.dangerous-set-commit-dates'
1498 ):
1499 prop_actions_allowed.append((b'M', b'svn:date'))
1500
1473 1501 hook = os.path.join(created, b'hooks', b'pre-revprop-change')
1474 1502 fp = open(hook, b'wb')
1475 fp.write(pre_revprop_change)
1503 fp.write(gen_pre_revprop_change_hook(prop_actions_allowed))
1476 1504 fp.close()
1477 1505 util.setflags(hook, False, True)
1478 1506
1479 1507 output = self.run0(b'info')
1480 1508 self.uuid = self.uuid_re.search(output).group(1).strip()
1481 1509
1482 1510 def wjoin(self, *names):
1483 1511 return os.path.join(self.wc, *names)
1484 1512
1485 1513 @propertycache
1486 1514 def manifest(self):
1487 1515 # As of svn 1.7, the "add" command fails when receiving
1488 1516 # already tracked entries, so we have to track and filter them
1489 1517 # ourselves.
1490 1518 m = set()
1491 1519 output = self.run0(b'ls', recursive=True, xml=True)
1492 1520 doc = xml.dom.minidom.parseString(output)
1493 1521 for e in doc.getElementsByTagName('entry'):
1494 1522 for n in e.childNodes:
1495 1523 if n.nodeType != n.ELEMENT_NODE or n.tagName != 'name':
1496 1524 continue
1497 1525 name = ''.join(
1498 1526 c.data for c in n.childNodes if c.nodeType == c.TEXT_NODE
1499 1527 )
1500 1528 # Entries are compared with names coming from
1501 1529 # mercurial, so bytes with undefined encoding. Our
1502 1530 # best bet is to assume they are in local
1503 1531 # encoding. They will be passed to command line calls
1504 1532 # later anyway, so they better be.
1505 1533 m.add(encoding.unitolocal(name))
1506 1534 break
1507 1535 return m
1508 1536
1509 1537 def putfile(self, filename, flags, data):
1510 1538 if b'l' in flags:
1511 1539 self.wopener.symlink(data, filename)
1512 1540 else:
1513 1541 try:
1514 1542 if os.path.islink(self.wjoin(filename)):
1515 1543 os.unlink(filename)
1516 1544 except OSError:
1517 1545 pass
1518 1546
1519 1547 if self.is_exec:
1520 1548 # We need to check executability of the file before the change,
1521 1549 # because `vfs.write` is able to reset exec bit.
1522 1550 wasexec = False
1523 1551 if os.path.exists(self.wjoin(filename)):
1524 1552 wasexec = self.is_exec(self.wjoin(filename))
1525 1553
1526 1554 self.wopener.write(filename, data)
1527 1555
1528 1556 if self.is_exec:
1529 1557 if wasexec:
1530 1558 if b'x' not in flags:
1531 1559 self.delexec.append(filename)
1532 1560 else:
1533 1561 if b'x' in flags:
1534 1562 self.setexec.append(filename)
1535 1563 util.setflags(self.wjoin(filename), False, b'x' in flags)
1536 1564
1537 1565 def _copyfile(self, source, dest):
1538 1566 # SVN's copy command pukes if the destination file exists, but
1539 1567 # our copyfile method expects to record a copy that has
1540 1568 # already occurred. Cross the semantic gap.
1541 1569 wdest = self.wjoin(dest)
1542 1570 exists = os.path.lexists(wdest)
1543 1571 if exists:
1544 1572 fd, tempname = pycompat.mkstemp(
1545 1573 prefix=b'hg-copy-', dir=os.path.dirname(wdest)
1546 1574 )
1547 1575 os.close(fd)
1548 1576 os.unlink(tempname)
1549 1577 os.rename(wdest, tempname)
1550 1578 try:
1551 1579 self.run0(b'copy', source, dest)
1552 1580 finally:
1553 1581 self.manifest.add(dest)
1554 1582 if exists:
1555 1583 try:
1556 1584 os.unlink(wdest)
1557 1585 except OSError:
1558 1586 pass
1559 1587 os.rename(tempname, wdest)
1560 1588
1561 1589 def dirs_of(self, files):
1562 1590 dirs = set()
1563 1591 for f in files:
1564 1592 if os.path.isdir(self.wjoin(f)):
1565 1593 dirs.add(f)
1566 1594 i = len(f)
1567 1595 for i in iter(lambda: f.rfind(b'/', 0, i), -1):
1568 1596 dirs.add(f[:i])
1569 1597 return dirs
1570 1598
1571 1599 def add_dirs(self, files):
1572 1600 add_dirs = [
1573 1601 d for d in sorted(self.dirs_of(files)) if d not in self.manifest
1574 1602 ]
1575 1603 if add_dirs:
1576 1604 self.manifest.update(add_dirs)
1577 1605 self.xargs(add_dirs, b'add', non_recursive=True, quiet=True)
1578 1606 return add_dirs
1579 1607
1580 1608 def add_files(self, files):
1581 1609 files = [f for f in files if f not in self.manifest]
1582 1610 if files:
1583 1611 self.manifest.update(files)
1584 1612 self.xargs(files, b'add', quiet=True)
1585 1613 return files
1586 1614
1587 1615 def addchild(self, parent, child):
1588 1616 self.childmap[parent] = child
1589 1617
1590 1618 def revid(self, rev):
1591 1619 return b"svn:%s@%s" % (self.uuid, rev)
1592 1620
1593 1621 def putcommit(
1594 1622 self, files, copies, parents, commit, source, revmap, full, cleanp2
1595 1623 ):
1596 1624 for parent in parents:
1597 1625 try:
1598 1626 return self.revid(self.childmap[parent])
1599 1627 except KeyError:
1600 1628 pass
1601 1629
1602 1630 # Apply changes to working copy
1603 1631 for f, v in files:
1604 1632 data, mode = source.getfile(f, v)
1605 1633 if data is None:
1606 1634 self.delete.append(f)
1607 1635 else:
1608 1636 self.putfile(f, mode, data)
1609 1637 if f in copies:
1610 1638 self.copies.append([copies[f], f])
1611 1639 if full:
1612 1640 self.delete.extend(sorted(self.manifest.difference(files)))
1613 1641 files = [f[0] for f in files]
1614 1642
1615 1643 entries = set(self.delete)
1616 1644 files = frozenset(files)
1617 1645 entries.update(self.add_dirs(files.difference(entries)))
1618 1646 if self.copies:
1619 1647 for s, d in self.copies:
1620 1648 self._copyfile(s, d)
1621 1649 self.copies = []
1622 1650 if self.delete:
1623 1651 self.xargs(self.delete, b'delete')
1624 1652 for f in self.delete:
1625 1653 self.manifest.remove(f)
1626 1654 self.delete = []
1627 1655 entries.update(self.add_files(files.difference(entries)))
1628 1656 if self.delexec:
1629 1657 self.xargs(self.delexec, b'propdel', b'svn:executable')
1630 1658 self.delexec = []
1631 1659 if self.setexec:
1632 1660 self.xargs(self.setexec, b'propset', b'svn:executable', b'*')
1633 1661 self.setexec = []
1634 1662
1635 1663 fd, messagefile = pycompat.mkstemp(prefix=b'hg-convert-')
1636 1664 fp = os.fdopen(fd, 'wb')
1637 1665 fp.write(util.tonativeeol(commit.desc))
1638 1666 fp.close()
1639 1667 try:
1640 1668 output = self.run0(
1641 1669 b'commit',
1642 1670 username=stringutil.shortuser(commit.author),
1643 1671 file=messagefile,
1644 1672 encoding=b'utf-8',
1645 1673 )
1646 1674 try:
1647 1675 rev = self.commit_re.search(output).group(1)
1648 1676 except AttributeError:
1649 1677 if not files:
1650 1678 return parents[0] if parents else b'None'
1651 1679 self.ui.warn(_(b'unexpected svn output:\n'))
1652 1680 self.ui.warn(output)
1653 1681 raise error.Abort(_(b'unable to cope with svn output'))
1654 1682 if commit.rev:
1655 1683 self.run(
1656 1684 b'propset',
1657 1685 b'hg:convert-rev',
1658 1686 commit.rev,
1659 1687 revprop=True,
1660 1688 revision=rev,
1661 1689 )
1662 1690 if commit.branch and commit.branch != b'default':
1663 1691 self.run(
1664 1692 b'propset',
1665 1693 b'hg:convert-branch',
1666 1694 commit.branch,
1667 1695 revprop=True,
1668 1696 revision=rev,
1669 1697 )
1698
1699 if self.ui.configbool(
1700 b'convert', b'svn.dangerous-set-commit-dates'
1701 ):
1702 # Subverson always uses UTC to represent date and time
1703 date = dateutil.parsedate(commit.date)
1704 date = (date[0], 0)
1705
1706 # The only way to set date and time for svn commit is to use propset after commit is done
1707 self.run(
1708 b'propset',
1709 b'svn:date',
1710 formatsvndate(date),
1711 revprop=True,
1712 revision=rev,
1713 )
1714
1670 1715 for parent in parents:
1671 1716 self.addchild(parent, rev)
1672 1717 return self.revid(rev)
1673 1718 finally:
1674 1719 os.unlink(messagefile)
1675 1720
1676 1721 def puttags(self, tags):
1677 1722 self.ui.warn(_(b'writing Subversion tags is not yet implemented\n'))
1678 1723 return None, None
1679 1724
1680 1725 def hascommitfrommap(self, rev):
1681 1726 # We trust that revisions referenced in a map still is present
1682 1727 # TODO: implement something better if necessary and feasible
1683 1728 return True
1684 1729
1685 1730 def hascommitforsplicemap(self, rev):
1686 1731 # This is not correct as one can convert to an existing subversion
1687 1732 # repository and childmap would not list all revisions. Too bad.
1688 1733 if rev in self.childmap:
1689 1734 return True
1690 1735 raise error.Abort(
1691 1736 _(
1692 1737 b'splice map revision %s not found in subversion '
1693 1738 b'child map (revision lookups are not implemented)'
1694 1739 )
1695 1740 % rev
1696 1741 )
@@ -1,2594 +1,2599 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 from __future__ import absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18
19 19 def loadconfigtable(ui, extname, configtable):
20 20 """update config item known to the ui with the extension ones"""
21 21 for section, items in sorted(configtable.items()):
22 22 knownitems = ui._knownconfig.setdefault(section, itemregister())
23 23 knownkeys = set(knownitems)
24 24 newkeys = set(items)
25 25 for key in sorted(knownkeys & newkeys):
26 26 msg = b"extension '%s' overwrite config item '%s.%s'"
27 27 msg %= (extname, section, key)
28 28 ui.develwarn(msg, config=b'warn-config')
29 29
30 30 knownitems.update(items)
31 31
32 32
33 33 class configitem(object):
34 34 """represent a known config item
35 35
36 36 :section: the official config section where to find this item,
37 37 :name: the official name within the section,
38 38 :default: default value for this item,
39 39 :alias: optional list of tuples as alternatives,
40 40 :generic: this is a generic definition, match name using regular expression.
41 41 """
42 42
43 43 def __init__(
44 44 self,
45 45 section,
46 46 name,
47 47 default=None,
48 48 alias=(),
49 49 generic=False,
50 50 priority=0,
51 51 experimental=False,
52 52 ):
53 53 self.section = section
54 54 self.name = name
55 55 self.default = default
56 56 self.alias = list(alias)
57 57 self.generic = generic
58 58 self.priority = priority
59 59 self.experimental = experimental
60 60 self._re = None
61 61 if generic:
62 62 self._re = re.compile(self.name)
63 63
64 64
65 65 class itemregister(dict):
66 66 """A specialized dictionary that can handle wild-card selection"""
67 67
68 68 def __init__(self):
69 69 super(itemregister, self).__init__()
70 70 self._generics = set()
71 71
72 72 def update(self, other):
73 73 super(itemregister, self).update(other)
74 74 self._generics.update(other._generics)
75 75
76 76 def __setitem__(self, key, item):
77 77 super(itemregister, self).__setitem__(key, item)
78 78 if item.generic:
79 79 self._generics.add(item)
80 80
81 81 def get(self, key):
82 82 baseitem = super(itemregister, self).get(key)
83 83 if baseitem is not None and not baseitem.generic:
84 84 return baseitem
85 85
86 86 # search for a matching generic item
87 87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
88 88 for item in generics:
89 89 # we use 'match' instead of 'search' to make the matching simpler
90 90 # for people unfamiliar with regular expression. Having the match
91 91 # rooted to the start of the string will produce less surprising
92 92 # result for user writing simple regex for sub-attribute.
93 93 #
94 94 # For example using "color\..*" match produces an unsurprising
95 95 # result, while using search could suddenly match apparently
96 96 # unrelated configuration that happens to contains "color."
97 97 # anywhere. This is a tradeoff where we favor requiring ".*" on
98 98 # some match to avoid the need to prefix most pattern with "^".
99 99 # The "^" seems more error prone.
100 100 if item._re.match(key):
101 101 return item
102 102
103 103 return None
104 104
105 105
106 106 coreitems = {}
107 107
108 108
109 109 def _register(configtable, *args, **kwargs):
110 110 item = configitem(*args, **kwargs)
111 111 section = configtable.setdefault(item.section, itemregister())
112 112 if item.name in section:
113 113 msg = b"duplicated config item registration for '%s.%s'"
114 114 raise error.ProgrammingError(msg % (item.section, item.name))
115 115 section[item.name] = item
116 116
117 117
118 118 # special value for case where the default is derived from other values
119 119 dynamicdefault = object()
120 120
121 121 # Registering actual config items
122 122
123 123
124 124 def getitemregister(configtable):
125 125 f = functools.partial(_register, configtable)
126 126 # export pseudo enum as configitem.*
127 127 f.dynamicdefault = dynamicdefault
128 128 return f
129 129
130 130
131 131 coreconfigitem = getitemregister(coreitems)
132 132
133 133
134 134 def _registerdiffopts(section, configprefix=b''):
135 135 coreconfigitem(
136 136 section,
137 137 configprefix + b'nodates',
138 138 default=False,
139 139 )
140 140 coreconfigitem(
141 141 section,
142 142 configprefix + b'showfunc',
143 143 default=False,
144 144 )
145 145 coreconfigitem(
146 146 section,
147 147 configprefix + b'unified',
148 148 default=None,
149 149 )
150 150 coreconfigitem(
151 151 section,
152 152 configprefix + b'git',
153 153 default=False,
154 154 )
155 155 coreconfigitem(
156 156 section,
157 157 configprefix + b'ignorews',
158 158 default=False,
159 159 )
160 160 coreconfigitem(
161 161 section,
162 162 configprefix + b'ignorewsamount',
163 163 default=False,
164 164 )
165 165 coreconfigitem(
166 166 section,
167 167 configprefix + b'ignoreblanklines',
168 168 default=False,
169 169 )
170 170 coreconfigitem(
171 171 section,
172 172 configprefix + b'ignorewseol',
173 173 default=False,
174 174 )
175 175 coreconfigitem(
176 176 section,
177 177 configprefix + b'nobinary',
178 178 default=False,
179 179 )
180 180 coreconfigitem(
181 181 section,
182 182 configprefix + b'noprefix',
183 183 default=False,
184 184 )
185 185 coreconfigitem(
186 186 section,
187 187 configprefix + b'word-diff',
188 188 default=False,
189 189 )
190 190
191 191
192 192 coreconfigitem(
193 193 b'alias',
194 194 b'.*',
195 195 default=dynamicdefault,
196 196 generic=True,
197 197 )
198 198 coreconfigitem(
199 199 b'auth',
200 200 b'cookiefile',
201 201 default=None,
202 202 )
203 203 _registerdiffopts(section=b'annotate')
204 204 # bookmarks.pushing: internal hack for discovery
205 205 coreconfigitem(
206 206 b'bookmarks',
207 207 b'pushing',
208 208 default=list,
209 209 )
210 210 # bundle.mainreporoot: internal hack for bundlerepo
211 211 coreconfigitem(
212 212 b'bundle',
213 213 b'mainreporoot',
214 214 default=b'',
215 215 )
216 216 coreconfigitem(
217 217 b'censor',
218 218 b'policy',
219 219 default=b'abort',
220 220 experimental=True,
221 221 )
222 222 coreconfigitem(
223 223 b'chgserver',
224 224 b'idletimeout',
225 225 default=3600,
226 226 )
227 227 coreconfigitem(
228 228 b'chgserver',
229 229 b'skiphash',
230 230 default=False,
231 231 )
232 232 coreconfigitem(
233 233 b'cmdserver',
234 234 b'log',
235 235 default=None,
236 236 )
237 237 coreconfigitem(
238 238 b'cmdserver',
239 239 b'max-log-files',
240 240 default=7,
241 241 )
242 242 coreconfigitem(
243 243 b'cmdserver',
244 244 b'max-log-size',
245 245 default=b'1 MB',
246 246 )
247 247 coreconfigitem(
248 248 b'cmdserver',
249 249 b'max-repo-cache',
250 250 default=0,
251 251 experimental=True,
252 252 )
253 253 coreconfigitem(
254 254 b'cmdserver',
255 255 b'message-encodings',
256 256 default=list,
257 257 )
258 258 coreconfigitem(
259 259 b'cmdserver',
260 260 b'track-log',
261 261 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
262 262 )
263 263 coreconfigitem(
264 264 b'cmdserver',
265 265 b'shutdown-on-interrupt',
266 266 default=True,
267 267 )
268 268 coreconfigitem(
269 269 b'color',
270 270 b'.*',
271 271 default=None,
272 272 generic=True,
273 273 )
274 274 coreconfigitem(
275 275 b'color',
276 276 b'mode',
277 277 default=b'auto',
278 278 )
279 279 coreconfigitem(
280 280 b'color',
281 281 b'pagermode',
282 282 default=dynamicdefault,
283 283 )
284 284 coreconfigitem(
285 285 b'command-templates',
286 286 b'graphnode',
287 287 default=None,
288 288 alias=[(b'ui', b'graphnodetemplate')],
289 289 )
290 290 coreconfigitem(
291 291 b'command-templates',
292 292 b'log',
293 293 default=None,
294 294 alias=[(b'ui', b'logtemplate')],
295 295 )
296 296 coreconfigitem(
297 297 b'command-templates',
298 298 b'mergemarker',
299 299 default=(
300 300 b'{node|short} '
301 301 b'{ifeq(tags, "tip", "", '
302 302 b'ifeq(tags, "", "", "{tags} "))}'
303 303 b'{if(bookmarks, "{bookmarks} ")}'
304 304 b'{ifeq(branch, "default", "", "{branch} ")}'
305 305 b'- {author|user}: {desc|firstline}'
306 306 ),
307 307 alias=[(b'ui', b'mergemarkertemplate')],
308 308 )
309 309 coreconfigitem(
310 310 b'command-templates',
311 311 b'pre-merge-tool-output',
312 312 default=None,
313 313 alias=[(b'ui', b'pre-merge-tool-output-template')],
314 314 )
315 315 coreconfigitem(
316 316 b'command-templates',
317 317 b'oneline-summary',
318 318 default=None,
319 319 )
320 320 coreconfigitem(
321 321 b'command-templates',
322 322 b'oneline-summary.*',
323 323 default=dynamicdefault,
324 324 generic=True,
325 325 )
326 326 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
327 327 coreconfigitem(
328 328 b'commands',
329 329 b'commit.post-status',
330 330 default=False,
331 331 )
332 332 coreconfigitem(
333 333 b'commands',
334 334 b'grep.all-files',
335 335 default=False,
336 336 experimental=True,
337 337 )
338 338 coreconfigitem(
339 339 b'commands',
340 340 b'merge.require-rev',
341 341 default=False,
342 342 )
343 343 coreconfigitem(
344 344 b'commands',
345 345 b'push.require-revs',
346 346 default=False,
347 347 )
348 348 coreconfigitem(
349 349 b'commands',
350 350 b'resolve.confirm',
351 351 default=False,
352 352 )
353 353 coreconfigitem(
354 354 b'commands',
355 355 b'resolve.explicit-re-merge',
356 356 default=False,
357 357 )
358 358 coreconfigitem(
359 359 b'commands',
360 360 b'resolve.mark-check',
361 361 default=b'none',
362 362 )
363 363 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
364 364 coreconfigitem(
365 365 b'commands',
366 366 b'show.aliasprefix',
367 367 default=list,
368 368 )
369 369 coreconfigitem(
370 370 b'commands',
371 371 b'status.relative',
372 372 default=False,
373 373 )
374 374 coreconfigitem(
375 375 b'commands',
376 376 b'status.skipstates',
377 377 default=[],
378 378 experimental=True,
379 379 )
380 380 coreconfigitem(
381 381 b'commands',
382 382 b'status.terse',
383 383 default=b'',
384 384 )
385 385 coreconfigitem(
386 386 b'commands',
387 387 b'status.verbose',
388 388 default=False,
389 389 )
390 390 coreconfigitem(
391 391 b'commands',
392 392 b'update.check',
393 393 default=None,
394 394 )
395 395 coreconfigitem(
396 396 b'commands',
397 397 b'update.requiredest',
398 398 default=False,
399 399 )
400 400 coreconfigitem(
401 401 b'committemplate',
402 402 b'.*',
403 403 default=None,
404 404 generic=True,
405 405 )
406 406 coreconfigitem(
407 407 b'convert',
408 408 b'bzr.saverev',
409 409 default=True,
410 410 )
411 411 coreconfigitem(
412 412 b'convert',
413 413 b'cvsps.cache',
414 414 default=True,
415 415 )
416 416 coreconfigitem(
417 417 b'convert',
418 418 b'cvsps.fuzz',
419 419 default=60,
420 420 )
421 421 coreconfigitem(
422 422 b'convert',
423 423 b'cvsps.logencoding',
424 424 default=None,
425 425 )
426 426 coreconfigitem(
427 427 b'convert',
428 428 b'cvsps.mergefrom',
429 429 default=None,
430 430 )
431 431 coreconfigitem(
432 432 b'convert',
433 433 b'cvsps.mergeto',
434 434 default=None,
435 435 )
436 436 coreconfigitem(
437 437 b'convert',
438 438 b'git.committeractions',
439 439 default=lambda: [b'messagedifferent'],
440 440 )
441 441 coreconfigitem(
442 442 b'convert',
443 443 b'git.extrakeys',
444 444 default=list,
445 445 )
446 446 coreconfigitem(
447 447 b'convert',
448 448 b'git.findcopiesharder',
449 449 default=False,
450 450 )
451 451 coreconfigitem(
452 452 b'convert',
453 453 b'git.remoteprefix',
454 454 default=b'remote',
455 455 )
456 456 coreconfigitem(
457 457 b'convert',
458 458 b'git.renamelimit',
459 459 default=400,
460 460 )
461 461 coreconfigitem(
462 462 b'convert',
463 463 b'git.saverev',
464 464 default=True,
465 465 )
466 466 coreconfigitem(
467 467 b'convert',
468 468 b'git.similarity',
469 469 default=50,
470 470 )
471 471 coreconfigitem(
472 472 b'convert',
473 473 b'git.skipsubmodules',
474 474 default=False,
475 475 )
476 476 coreconfigitem(
477 477 b'convert',
478 478 b'hg.clonebranches',
479 479 default=False,
480 480 )
481 481 coreconfigitem(
482 482 b'convert',
483 483 b'hg.ignoreerrors',
484 484 default=False,
485 485 )
486 486 coreconfigitem(
487 487 b'convert',
488 488 b'hg.preserve-hash',
489 489 default=False,
490 490 )
491 491 coreconfigitem(
492 492 b'convert',
493 493 b'hg.revs',
494 494 default=None,
495 495 )
496 496 coreconfigitem(
497 497 b'convert',
498 498 b'hg.saverev',
499 499 default=False,
500 500 )
501 501 coreconfigitem(
502 502 b'convert',
503 503 b'hg.sourcename',
504 504 default=None,
505 505 )
506 506 coreconfigitem(
507 507 b'convert',
508 508 b'hg.startrev',
509 509 default=None,
510 510 )
511 511 coreconfigitem(
512 512 b'convert',
513 513 b'hg.tagsbranch',
514 514 default=b'default',
515 515 )
516 516 coreconfigitem(
517 517 b'convert',
518 518 b'hg.usebranchnames',
519 519 default=True,
520 520 )
521 521 coreconfigitem(
522 522 b'convert',
523 523 b'ignoreancestorcheck',
524 524 default=False,
525 525 experimental=True,
526 526 )
527 527 coreconfigitem(
528 528 b'convert',
529 529 b'localtimezone',
530 530 default=False,
531 531 )
532 532 coreconfigitem(
533 533 b'convert',
534 534 b'p4.encoding',
535 535 default=dynamicdefault,
536 536 )
537 537 coreconfigitem(
538 538 b'convert',
539 539 b'p4.startrev',
540 540 default=0,
541 541 )
542 542 coreconfigitem(
543 543 b'convert',
544 544 b'skiptags',
545 545 default=False,
546 546 )
547 547 coreconfigitem(
548 548 b'convert',
549 549 b'svn.debugsvnlog',
550 550 default=True,
551 551 )
552 552 coreconfigitem(
553 553 b'convert',
554 554 b'svn.trunk',
555 555 default=None,
556 556 )
557 557 coreconfigitem(
558 558 b'convert',
559 559 b'svn.tags',
560 560 default=None,
561 561 )
562 562 coreconfigitem(
563 563 b'convert',
564 564 b'svn.branches',
565 565 default=None,
566 566 )
567 567 coreconfigitem(
568 568 b'convert',
569 569 b'svn.startrev',
570 570 default=0,
571 571 )
572 572 coreconfigitem(
573 b'convert',
574 b'svn.dangerous-set-commit-dates',
575 default=False,
576 )
577 coreconfigitem(
573 578 b'debug',
574 579 b'dirstate.delaywrite',
575 580 default=0,
576 581 )
577 582 coreconfigitem(
578 583 b'defaults',
579 584 b'.*',
580 585 default=None,
581 586 generic=True,
582 587 )
583 588 coreconfigitem(
584 589 b'devel',
585 590 b'all-warnings',
586 591 default=False,
587 592 )
588 593 coreconfigitem(
589 594 b'devel',
590 595 b'bundle2.debug',
591 596 default=False,
592 597 )
593 598 coreconfigitem(
594 599 b'devel',
595 600 b'bundle.delta',
596 601 default=b'',
597 602 )
598 603 coreconfigitem(
599 604 b'devel',
600 605 b'cache-vfs',
601 606 default=None,
602 607 )
603 608 coreconfigitem(
604 609 b'devel',
605 610 b'check-locks',
606 611 default=False,
607 612 )
608 613 coreconfigitem(
609 614 b'devel',
610 615 b'check-relroot',
611 616 default=False,
612 617 )
613 618 coreconfigitem(
614 619 b'devel',
615 620 b'default-date',
616 621 default=None,
617 622 )
618 623 coreconfigitem(
619 624 b'devel',
620 625 b'deprec-warn',
621 626 default=False,
622 627 )
623 628 coreconfigitem(
624 629 b'devel',
625 630 b'disableloaddefaultcerts',
626 631 default=False,
627 632 )
628 633 coreconfigitem(
629 634 b'devel',
630 635 b'warn-empty-changegroup',
631 636 default=False,
632 637 )
633 638 coreconfigitem(
634 639 b'devel',
635 640 b'legacy.exchange',
636 641 default=list,
637 642 )
638 643 # When True, revlogs use a special reference version of the nodemap, that is not
639 644 # performant but is "known" to behave properly.
640 645 coreconfigitem(
641 646 b'devel',
642 647 b'persistent-nodemap',
643 648 default=False,
644 649 )
645 650 coreconfigitem(
646 651 b'devel',
647 652 b'servercafile',
648 653 default=b'',
649 654 )
650 655 coreconfigitem(
651 656 b'devel',
652 657 b'serverexactprotocol',
653 658 default=b'',
654 659 )
655 660 coreconfigitem(
656 661 b'devel',
657 662 b'serverrequirecert',
658 663 default=False,
659 664 )
660 665 coreconfigitem(
661 666 b'devel',
662 667 b'strip-obsmarkers',
663 668 default=True,
664 669 )
665 670 coreconfigitem(
666 671 b'devel',
667 672 b'warn-config',
668 673 default=None,
669 674 )
670 675 coreconfigitem(
671 676 b'devel',
672 677 b'warn-config-default',
673 678 default=None,
674 679 )
675 680 coreconfigitem(
676 681 b'devel',
677 682 b'user.obsmarker',
678 683 default=None,
679 684 )
680 685 coreconfigitem(
681 686 b'devel',
682 687 b'warn-config-unknown',
683 688 default=None,
684 689 )
685 690 coreconfigitem(
686 691 b'devel',
687 692 b'debug.copies',
688 693 default=False,
689 694 )
690 695 coreconfigitem(
691 696 b'devel',
692 697 b'debug.extensions',
693 698 default=False,
694 699 )
695 700 coreconfigitem(
696 701 b'devel',
697 702 b'debug.repo-filters',
698 703 default=False,
699 704 )
700 705 coreconfigitem(
701 706 b'devel',
702 707 b'debug.peer-request',
703 708 default=False,
704 709 )
705 710 # If discovery.exchange-heads is False, the discovery will not start with
706 711 # remote head fetching and local head querying.
707 712 coreconfigitem(
708 713 b'devel',
709 714 b'discovery.exchange-heads',
710 715 default=True,
711 716 )
712 717 # If discovery.grow-sample is False, the sample size used in set discovery will
713 718 # not be increased through the process
714 719 coreconfigitem(
715 720 b'devel',
716 721 b'discovery.grow-sample',
717 722 default=True,
718 723 )
719 724 # discovery.grow-sample.rate control the rate at which the sample grow
720 725 coreconfigitem(
721 726 b'devel',
722 727 b'discovery.grow-sample.rate',
723 728 default=1.05,
724 729 )
725 730 # If discovery.randomize is False, random sampling during discovery are
726 731 # deterministic. It is meant for integration tests.
727 732 coreconfigitem(
728 733 b'devel',
729 734 b'discovery.randomize',
730 735 default=True,
731 736 )
732 737 # Control the initial size of the discovery sample
733 738 coreconfigitem(
734 739 b'devel',
735 740 b'discovery.sample-size',
736 741 default=200,
737 742 )
738 743 # Control the initial size of the discovery for initial change
739 744 coreconfigitem(
740 745 b'devel',
741 746 b'discovery.sample-size.initial',
742 747 default=100,
743 748 )
744 749 _registerdiffopts(section=b'diff')
745 750 coreconfigitem(
746 751 b'email',
747 752 b'bcc',
748 753 default=None,
749 754 )
750 755 coreconfigitem(
751 756 b'email',
752 757 b'cc',
753 758 default=None,
754 759 )
755 760 coreconfigitem(
756 761 b'email',
757 762 b'charsets',
758 763 default=list,
759 764 )
760 765 coreconfigitem(
761 766 b'email',
762 767 b'from',
763 768 default=None,
764 769 )
765 770 coreconfigitem(
766 771 b'email',
767 772 b'method',
768 773 default=b'smtp',
769 774 )
770 775 coreconfigitem(
771 776 b'email',
772 777 b'reply-to',
773 778 default=None,
774 779 )
775 780 coreconfigitem(
776 781 b'email',
777 782 b'to',
778 783 default=None,
779 784 )
780 785 coreconfigitem(
781 786 b'experimental',
782 787 b'archivemetatemplate',
783 788 default=dynamicdefault,
784 789 )
785 790 coreconfigitem(
786 791 b'experimental',
787 792 b'auto-publish',
788 793 default=b'publish',
789 794 )
790 795 coreconfigitem(
791 796 b'experimental',
792 797 b'bundle-phases',
793 798 default=False,
794 799 )
795 800 coreconfigitem(
796 801 b'experimental',
797 802 b'bundle2-advertise',
798 803 default=True,
799 804 )
800 805 coreconfigitem(
801 806 b'experimental',
802 807 b'bundle2-output-capture',
803 808 default=False,
804 809 )
805 810 coreconfigitem(
806 811 b'experimental',
807 812 b'bundle2.pushback',
808 813 default=False,
809 814 )
810 815 coreconfigitem(
811 816 b'experimental',
812 817 b'bundle2lazylocking',
813 818 default=False,
814 819 )
815 820 coreconfigitem(
816 821 b'experimental',
817 822 b'bundlecomplevel',
818 823 default=None,
819 824 )
820 825 coreconfigitem(
821 826 b'experimental',
822 827 b'bundlecomplevel.bzip2',
823 828 default=None,
824 829 )
825 830 coreconfigitem(
826 831 b'experimental',
827 832 b'bundlecomplevel.gzip',
828 833 default=None,
829 834 )
830 835 coreconfigitem(
831 836 b'experimental',
832 837 b'bundlecomplevel.none',
833 838 default=None,
834 839 )
835 840 coreconfigitem(
836 841 b'experimental',
837 842 b'bundlecomplevel.zstd',
838 843 default=None,
839 844 )
840 845 coreconfigitem(
841 846 b'experimental',
842 847 b'changegroup3',
843 848 default=False,
844 849 )
845 850 coreconfigitem(
846 851 b'experimental',
847 852 b'cleanup-as-archived',
848 853 default=False,
849 854 )
850 855 coreconfigitem(
851 856 b'experimental',
852 857 b'clientcompressionengines',
853 858 default=list,
854 859 )
855 860 coreconfigitem(
856 861 b'experimental',
857 862 b'copytrace',
858 863 default=b'on',
859 864 )
860 865 coreconfigitem(
861 866 b'experimental',
862 867 b'copytrace.movecandidateslimit',
863 868 default=100,
864 869 )
865 870 coreconfigitem(
866 871 b'experimental',
867 872 b'copytrace.sourcecommitlimit',
868 873 default=100,
869 874 )
870 875 coreconfigitem(
871 876 b'experimental',
872 877 b'copies.read-from',
873 878 default=b"filelog-only",
874 879 )
875 880 coreconfigitem(
876 881 b'experimental',
877 882 b'copies.write-to',
878 883 default=b'filelog-only',
879 884 )
880 885 coreconfigitem(
881 886 b'experimental',
882 887 b'crecordtest',
883 888 default=None,
884 889 )
885 890 coreconfigitem(
886 891 b'experimental',
887 892 b'directaccess',
888 893 default=False,
889 894 )
890 895 coreconfigitem(
891 896 b'experimental',
892 897 b'directaccess.revnums',
893 898 default=False,
894 899 )
895 900 coreconfigitem(
896 901 b'experimental',
897 902 b'editortmpinhg',
898 903 default=False,
899 904 )
900 905 coreconfigitem(
901 906 b'experimental',
902 907 b'evolution',
903 908 default=list,
904 909 )
905 910 coreconfigitem(
906 911 b'experimental',
907 912 b'evolution.allowdivergence',
908 913 default=False,
909 914 alias=[(b'experimental', b'allowdivergence')],
910 915 )
911 916 coreconfigitem(
912 917 b'experimental',
913 918 b'evolution.allowunstable',
914 919 default=None,
915 920 )
916 921 coreconfigitem(
917 922 b'experimental',
918 923 b'evolution.createmarkers',
919 924 default=None,
920 925 )
921 926 coreconfigitem(
922 927 b'experimental',
923 928 b'evolution.effect-flags',
924 929 default=True,
925 930 alias=[(b'experimental', b'effect-flags')],
926 931 )
927 932 coreconfigitem(
928 933 b'experimental',
929 934 b'evolution.exchange',
930 935 default=None,
931 936 )
932 937 coreconfigitem(
933 938 b'experimental',
934 939 b'evolution.bundle-obsmarker',
935 940 default=False,
936 941 )
937 942 coreconfigitem(
938 943 b'experimental',
939 944 b'evolution.bundle-obsmarker:mandatory',
940 945 default=True,
941 946 )
942 947 coreconfigitem(
943 948 b'experimental',
944 949 b'log.topo',
945 950 default=False,
946 951 )
947 952 coreconfigitem(
948 953 b'experimental',
949 954 b'evolution.report-instabilities',
950 955 default=True,
951 956 )
952 957 coreconfigitem(
953 958 b'experimental',
954 959 b'evolution.track-operation',
955 960 default=True,
956 961 )
957 962 # repo-level config to exclude a revset visibility
958 963 #
959 964 # The target use case is to use `share` to expose different subset of the same
960 965 # repository, especially server side. See also `server.view`.
961 966 coreconfigitem(
962 967 b'experimental',
963 968 b'extra-filter-revs',
964 969 default=None,
965 970 )
966 971 coreconfigitem(
967 972 b'experimental',
968 973 b'maxdeltachainspan',
969 974 default=-1,
970 975 )
971 976 # tracks files which were undeleted (merge might delete them but we explicitly
972 977 # kept/undeleted them) and creates new filenodes for them
973 978 coreconfigitem(
974 979 b'experimental',
975 980 b'merge-track-salvaged',
976 981 default=False,
977 982 )
978 983 coreconfigitem(
979 984 b'experimental',
980 985 b'mergetempdirprefix',
981 986 default=None,
982 987 )
983 988 coreconfigitem(
984 989 b'experimental',
985 990 b'mmapindexthreshold',
986 991 default=None,
987 992 )
988 993 coreconfigitem(
989 994 b'experimental',
990 995 b'narrow',
991 996 default=False,
992 997 )
993 998 coreconfigitem(
994 999 b'experimental',
995 1000 b'nonnormalparanoidcheck',
996 1001 default=False,
997 1002 )
998 1003 coreconfigitem(
999 1004 b'experimental',
1000 1005 b'exportableenviron',
1001 1006 default=list,
1002 1007 )
1003 1008 coreconfigitem(
1004 1009 b'experimental',
1005 1010 b'extendedheader.index',
1006 1011 default=None,
1007 1012 )
1008 1013 coreconfigitem(
1009 1014 b'experimental',
1010 1015 b'extendedheader.similarity',
1011 1016 default=False,
1012 1017 )
1013 1018 coreconfigitem(
1014 1019 b'experimental',
1015 1020 b'graphshorten',
1016 1021 default=False,
1017 1022 )
1018 1023 coreconfigitem(
1019 1024 b'experimental',
1020 1025 b'graphstyle.parent',
1021 1026 default=dynamicdefault,
1022 1027 )
1023 1028 coreconfigitem(
1024 1029 b'experimental',
1025 1030 b'graphstyle.missing',
1026 1031 default=dynamicdefault,
1027 1032 )
1028 1033 coreconfigitem(
1029 1034 b'experimental',
1030 1035 b'graphstyle.grandparent',
1031 1036 default=dynamicdefault,
1032 1037 )
1033 1038 coreconfigitem(
1034 1039 b'experimental',
1035 1040 b'hook-track-tags',
1036 1041 default=False,
1037 1042 )
1038 1043 coreconfigitem(
1039 1044 b'experimental',
1040 1045 b'httppeer.advertise-v2',
1041 1046 default=False,
1042 1047 )
1043 1048 coreconfigitem(
1044 1049 b'experimental',
1045 1050 b'httppeer.v2-encoder-order',
1046 1051 default=None,
1047 1052 )
1048 1053 coreconfigitem(
1049 1054 b'experimental',
1050 1055 b'httppostargs',
1051 1056 default=False,
1052 1057 )
1053 1058 coreconfigitem(b'experimental', b'nointerrupt', default=False)
1054 1059 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
1055 1060
1056 1061 coreconfigitem(
1057 1062 b'experimental',
1058 1063 b'obsmarkers-exchange-debug',
1059 1064 default=False,
1060 1065 )
1061 1066 coreconfigitem(
1062 1067 b'experimental',
1063 1068 b'remotenames',
1064 1069 default=False,
1065 1070 )
1066 1071 coreconfigitem(
1067 1072 b'experimental',
1068 1073 b'removeemptydirs',
1069 1074 default=True,
1070 1075 )
1071 1076 coreconfigitem(
1072 1077 b'experimental',
1073 1078 b'revert.interactive.select-to-keep',
1074 1079 default=False,
1075 1080 )
1076 1081 coreconfigitem(
1077 1082 b'experimental',
1078 1083 b'revisions.prefixhexnode',
1079 1084 default=False,
1080 1085 )
1081 1086 coreconfigitem(
1082 1087 b'experimental',
1083 1088 b'revlogv2',
1084 1089 default=None,
1085 1090 )
1086 1091 coreconfigitem(
1087 1092 b'experimental',
1088 1093 b'revisions.disambiguatewithin',
1089 1094 default=None,
1090 1095 )
1091 1096 coreconfigitem(
1092 1097 b'experimental',
1093 1098 b'rust.index',
1094 1099 default=False,
1095 1100 )
1096 1101 coreconfigitem(
1097 1102 b'experimental',
1098 1103 b'server.filesdata.recommended-batch-size',
1099 1104 default=50000,
1100 1105 )
1101 1106 coreconfigitem(
1102 1107 b'experimental',
1103 1108 b'server.manifestdata.recommended-batch-size',
1104 1109 default=100000,
1105 1110 )
1106 1111 coreconfigitem(
1107 1112 b'experimental',
1108 1113 b'server.stream-narrow-clones',
1109 1114 default=False,
1110 1115 )
1111 1116 coreconfigitem(
1112 1117 b'experimental',
1113 1118 b'single-head-per-branch',
1114 1119 default=False,
1115 1120 )
1116 1121 coreconfigitem(
1117 1122 b'experimental',
1118 1123 b'single-head-per-branch:account-closed-heads',
1119 1124 default=False,
1120 1125 )
1121 1126 coreconfigitem(
1122 1127 b'experimental',
1123 1128 b'single-head-per-branch:public-changes-only',
1124 1129 default=False,
1125 1130 )
1126 1131 coreconfigitem(
1127 1132 b'experimental',
1128 1133 b'sshserver.support-v2',
1129 1134 default=False,
1130 1135 )
1131 1136 coreconfigitem(
1132 1137 b'experimental',
1133 1138 b'sparse-read',
1134 1139 default=False,
1135 1140 )
1136 1141 coreconfigitem(
1137 1142 b'experimental',
1138 1143 b'sparse-read.density-threshold',
1139 1144 default=0.50,
1140 1145 )
1141 1146 coreconfigitem(
1142 1147 b'experimental',
1143 1148 b'sparse-read.min-gap-size',
1144 1149 default=b'65K',
1145 1150 )
1146 1151 coreconfigitem(
1147 1152 b'experimental',
1148 1153 b'treemanifest',
1149 1154 default=False,
1150 1155 )
1151 1156 coreconfigitem(
1152 1157 b'experimental',
1153 1158 b'update.atomic-file',
1154 1159 default=False,
1155 1160 )
1156 1161 coreconfigitem(
1157 1162 b'experimental',
1158 1163 b'sshpeer.advertise-v2',
1159 1164 default=False,
1160 1165 )
1161 1166 coreconfigitem(
1162 1167 b'experimental',
1163 1168 b'web.apiserver',
1164 1169 default=False,
1165 1170 )
1166 1171 coreconfigitem(
1167 1172 b'experimental',
1168 1173 b'web.api.http-v2',
1169 1174 default=False,
1170 1175 )
1171 1176 coreconfigitem(
1172 1177 b'experimental',
1173 1178 b'web.api.debugreflect',
1174 1179 default=False,
1175 1180 )
1176 1181 coreconfigitem(
1177 1182 b'experimental',
1178 1183 b'worker.wdir-get-thread-safe',
1179 1184 default=False,
1180 1185 )
1181 1186 coreconfigitem(
1182 1187 b'experimental',
1183 1188 b'worker.repository-upgrade',
1184 1189 default=False,
1185 1190 )
1186 1191 coreconfigitem(
1187 1192 b'experimental',
1188 1193 b'xdiff',
1189 1194 default=False,
1190 1195 )
1191 1196 coreconfigitem(
1192 1197 b'extensions',
1193 1198 b'.*',
1194 1199 default=None,
1195 1200 generic=True,
1196 1201 )
1197 1202 coreconfigitem(
1198 1203 b'extdata',
1199 1204 b'.*',
1200 1205 default=None,
1201 1206 generic=True,
1202 1207 )
1203 1208 coreconfigitem(
1204 1209 b'format',
1205 1210 b'bookmarks-in-store',
1206 1211 default=False,
1207 1212 )
1208 1213 coreconfigitem(
1209 1214 b'format',
1210 1215 b'chunkcachesize',
1211 1216 default=None,
1212 1217 experimental=True,
1213 1218 )
1214 1219 coreconfigitem(
1215 1220 b'format',
1216 1221 b'dotencode',
1217 1222 default=True,
1218 1223 )
1219 1224 coreconfigitem(
1220 1225 b'format',
1221 1226 b'generaldelta',
1222 1227 default=False,
1223 1228 experimental=True,
1224 1229 )
1225 1230 coreconfigitem(
1226 1231 b'format',
1227 1232 b'manifestcachesize',
1228 1233 default=None,
1229 1234 experimental=True,
1230 1235 )
1231 1236 coreconfigitem(
1232 1237 b'format',
1233 1238 b'maxchainlen',
1234 1239 default=dynamicdefault,
1235 1240 experimental=True,
1236 1241 )
1237 1242 coreconfigitem(
1238 1243 b'format',
1239 1244 b'obsstore-version',
1240 1245 default=None,
1241 1246 )
1242 1247 coreconfigitem(
1243 1248 b'format',
1244 1249 b'sparse-revlog',
1245 1250 default=True,
1246 1251 )
1247 1252 coreconfigitem(
1248 1253 b'format',
1249 1254 b'revlog-compression',
1250 1255 default=lambda: [b'zlib'],
1251 1256 alias=[(b'experimental', b'format.compression')],
1252 1257 )
1253 1258 coreconfigitem(
1254 1259 b'format',
1255 1260 b'usefncache',
1256 1261 default=True,
1257 1262 )
1258 1263 coreconfigitem(
1259 1264 b'format',
1260 1265 b'usegeneraldelta',
1261 1266 default=True,
1262 1267 )
1263 1268 coreconfigitem(
1264 1269 b'format',
1265 1270 b'usestore',
1266 1271 default=True,
1267 1272 )
1268 1273 coreconfigitem(
1269 1274 b'format',
1270 1275 b'use-persistent-nodemap',
1271 1276 default=False,
1272 1277 )
1273 1278 coreconfigitem(
1274 1279 b'format',
1275 1280 b'exp-use-copies-side-data-changeset',
1276 1281 default=False,
1277 1282 experimental=True,
1278 1283 )
1279 1284 coreconfigitem(
1280 1285 b'format',
1281 1286 b'exp-use-side-data',
1282 1287 default=False,
1283 1288 experimental=True,
1284 1289 )
1285 1290 coreconfigitem(
1286 1291 b'format',
1287 1292 b'use-share-safe',
1288 1293 default=False,
1289 1294 )
1290 1295 coreconfigitem(
1291 1296 b'format',
1292 1297 b'internal-phase',
1293 1298 default=False,
1294 1299 experimental=True,
1295 1300 )
1296 1301 coreconfigitem(
1297 1302 b'fsmonitor',
1298 1303 b'warn_when_unused',
1299 1304 default=True,
1300 1305 )
1301 1306 coreconfigitem(
1302 1307 b'fsmonitor',
1303 1308 b'warn_update_file_count',
1304 1309 default=50000,
1305 1310 )
1306 1311 coreconfigitem(
1307 1312 b'fsmonitor',
1308 1313 b'warn_update_file_count_rust',
1309 1314 default=400000,
1310 1315 )
1311 1316 coreconfigitem(
1312 1317 b'help',
1313 1318 br'hidden-command\..*',
1314 1319 default=False,
1315 1320 generic=True,
1316 1321 )
1317 1322 coreconfigitem(
1318 1323 b'help',
1319 1324 br'hidden-topic\..*',
1320 1325 default=False,
1321 1326 generic=True,
1322 1327 )
1323 1328 coreconfigitem(
1324 1329 b'hooks',
1325 1330 b'.*',
1326 1331 default=dynamicdefault,
1327 1332 generic=True,
1328 1333 )
1329 1334 coreconfigitem(
1330 1335 b'hgweb-paths',
1331 1336 b'.*',
1332 1337 default=list,
1333 1338 generic=True,
1334 1339 )
1335 1340 coreconfigitem(
1336 1341 b'hostfingerprints',
1337 1342 b'.*',
1338 1343 default=list,
1339 1344 generic=True,
1340 1345 )
1341 1346 coreconfigitem(
1342 1347 b'hostsecurity',
1343 1348 b'ciphers',
1344 1349 default=None,
1345 1350 )
1346 1351 coreconfigitem(
1347 1352 b'hostsecurity',
1348 1353 b'minimumprotocol',
1349 1354 default=dynamicdefault,
1350 1355 )
1351 1356 coreconfigitem(
1352 1357 b'hostsecurity',
1353 1358 b'.*:minimumprotocol$',
1354 1359 default=dynamicdefault,
1355 1360 generic=True,
1356 1361 )
1357 1362 coreconfigitem(
1358 1363 b'hostsecurity',
1359 1364 b'.*:ciphers$',
1360 1365 default=dynamicdefault,
1361 1366 generic=True,
1362 1367 )
1363 1368 coreconfigitem(
1364 1369 b'hostsecurity',
1365 1370 b'.*:fingerprints$',
1366 1371 default=list,
1367 1372 generic=True,
1368 1373 )
1369 1374 coreconfigitem(
1370 1375 b'hostsecurity',
1371 1376 b'.*:verifycertsfile$',
1372 1377 default=None,
1373 1378 generic=True,
1374 1379 )
1375 1380
1376 1381 coreconfigitem(
1377 1382 b'http_proxy',
1378 1383 b'always',
1379 1384 default=False,
1380 1385 )
1381 1386 coreconfigitem(
1382 1387 b'http_proxy',
1383 1388 b'host',
1384 1389 default=None,
1385 1390 )
1386 1391 coreconfigitem(
1387 1392 b'http_proxy',
1388 1393 b'no',
1389 1394 default=list,
1390 1395 )
1391 1396 coreconfigitem(
1392 1397 b'http_proxy',
1393 1398 b'passwd',
1394 1399 default=None,
1395 1400 )
1396 1401 coreconfigitem(
1397 1402 b'http_proxy',
1398 1403 b'user',
1399 1404 default=None,
1400 1405 )
1401 1406
1402 1407 coreconfigitem(
1403 1408 b'http',
1404 1409 b'timeout',
1405 1410 default=None,
1406 1411 )
1407 1412
1408 1413 coreconfigitem(
1409 1414 b'logtoprocess',
1410 1415 b'commandexception',
1411 1416 default=None,
1412 1417 )
1413 1418 coreconfigitem(
1414 1419 b'logtoprocess',
1415 1420 b'commandfinish',
1416 1421 default=None,
1417 1422 )
1418 1423 coreconfigitem(
1419 1424 b'logtoprocess',
1420 1425 b'command',
1421 1426 default=None,
1422 1427 )
1423 1428 coreconfigitem(
1424 1429 b'logtoprocess',
1425 1430 b'develwarn',
1426 1431 default=None,
1427 1432 )
1428 1433 coreconfigitem(
1429 1434 b'logtoprocess',
1430 1435 b'uiblocked',
1431 1436 default=None,
1432 1437 )
1433 1438 coreconfigitem(
1434 1439 b'merge',
1435 1440 b'checkunknown',
1436 1441 default=b'abort',
1437 1442 )
1438 1443 coreconfigitem(
1439 1444 b'merge',
1440 1445 b'checkignored',
1441 1446 default=b'abort',
1442 1447 )
1443 1448 coreconfigitem(
1444 1449 b'experimental',
1445 1450 b'merge.checkpathconflicts',
1446 1451 default=False,
1447 1452 )
1448 1453 coreconfigitem(
1449 1454 b'merge',
1450 1455 b'followcopies',
1451 1456 default=True,
1452 1457 )
1453 1458 coreconfigitem(
1454 1459 b'merge',
1455 1460 b'on-failure',
1456 1461 default=b'continue',
1457 1462 )
1458 1463 coreconfigitem(
1459 1464 b'merge',
1460 1465 b'preferancestor',
1461 1466 default=lambda: [b'*'],
1462 1467 experimental=True,
1463 1468 )
1464 1469 coreconfigitem(
1465 1470 b'merge',
1466 1471 b'strict-capability-check',
1467 1472 default=False,
1468 1473 )
1469 1474 coreconfigitem(
1470 1475 b'merge-tools',
1471 1476 b'.*',
1472 1477 default=None,
1473 1478 generic=True,
1474 1479 )
1475 1480 coreconfigitem(
1476 1481 b'merge-tools',
1477 1482 br'.*\.args$',
1478 1483 default=b"$local $base $other",
1479 1484 generic=True,
1480 1485 priority=-1,
1481 1486 )
1482 1487 coreconfigitem(
1483 1488 b'merge-tools',
1484 1489 br'.*\.binary$',
1485 1490 default=False,
1486 1491 generic=True,
1487 1492 priority=-1,
1488 1493 )
1489 1494 coreconfigitem(
1490 1495 b'merge-tools',
1491 1496 br'.*\.check$',
1492 1497 default=list,
1493 1498 generic=True,
1494 1499 priority=-1,
1495 1500 )
1496 1501 coreconfigitem(
1497 1502 b'merge-tools',
1498 1503 br'.*\.checkchanged$',
1499 1504 default=False,
1500 1505 generic=True,
1501 1506 priority=-1,
1502 1507 )
1503 1508 coreconfigitem(
1504 1509 b'merge-tools',
1505 1510 br'.*\.executable$',
1506 1511 default=dynamicdefault,
1507 1512 generic=True,
1508 1513 priority=-1,
1509 1514 )
1510 1515 coreconfigitem(
1511 1516 b'merge-tools',
1512 1517 br'.*\.fixeol$',
1513 1518 default=False,
1514 1519 generic=True,
1515 1520 priority=-1,
1516 1521 )
1517 1522 coreconfigitem(
1518 1523 b'merge-tools',
1519 1524 br'.*\.gui$',
1520 1525 default=False,
1521 1526 generic=True,
1522 1527 priority=-1,
1523 1528 )
1524 1529 coreconfigitem(
1525 1530 b'merge-tools',
1526 1531 br'.*\.mergemarkers$',
1527 1532 default=b'basic',
1528 1533 generic=True,
1529 1534 priority=-1,
1530 1535 )
1531 1536 coreconfigitem(
1532 1537 b'merge-tools',
1533 1538 br'.*\.mergemarkertemplate$',
1534 1539 default=dynamicdefault, # take from command-templates.mergemarker
1535 1540 generic=True,
1536 1541 priority=-1,
1537 1542 )
1538 1543 coreconfigitem(
1539 1544 b'merge-tools',
1540 1545 br'.*\.priority$',
1541 1546 default=0,
1542 1547 generic=True,
1543 1548 priority=-1,
1544 1549 )
1545 1550 coreconfigitem(
1546 1551 b'merge-tools',
1547 1552 br'.*\.premerge$',
1548 1553 default=dynamicdefault,
1549 1554 generic=True,
1550 1555 priority=-1,
1551 1556 )
1552 1557 coreconfigitem(
1553 1558 b'merge-tools',
1554 1559 br'.*\.symlink$',
1555 1560 default=False,
1556 1561 generic=True,
1557 1562 priority=-1,
1558 1563 )
1559 1564 coreconfigitem(
1560 1565 b'pager',
1561 1566 b'attend-.*',
1562 1567 default=dynamicdefault,
1563 1568 generic=True,
1564 1569 )
1565 1570 coreconfigitem(
1566 1571 b'pager',
1567 1572 b'ignore',
1568 1573 default=list,
1569 1574 )
1570 1575 coreconfigitem(
1571 1576 b'pager',
1572 1577 b'pager',
1573 1578 default=dynamicdefault,
1574 1579 )
1575 1580 coreconfigitem(
1576 1581 b'patch',
1577 1582 b'eol',
1578 1583 default=b'strict',
1579 1584 )
1580 1585 coreconfigitem(
1581 1586 b'patch',
1582 1587 b'fuzz',
1583 1588 default=2,
1584 1589 )
1585 1590 coreconfigitem(
1586 1591 b'paths',
1587 1592 b'default',
1588 1593 default=None,
1589 1594 )
1590 1595 coreconfigitem(
1591 1596 b'paths',
1592 1597 b'default-push',
1593 1598 default=None,
1594 1599 )
1595 1600 coreconfigitem(
1596 1601 b'paths',
1597 1602 b'.*',
1598 1603 default=None,
1599 1604 generic=True,
1600 1605 )
1601 1606 coreconfigitem(
1602 1607 b'phases',
1603 1608 b'checksubrepos',
1604 1609 default=b'follow',
1605 1610 )
1606 1611 coreconfigitem(
1607 1612 b'phases',
1608 1613 b'new-commit',
1609 1614 default=b'draft',
1610 1615 )
1611 1616 coreconfigitem(
1612 1617 b'phases',
1613 1618 b'publish',
1614 1619 default=True,
1615 1620 )
1616 1621 coreconfigitem(
1617 1622 b'profiling',
1618 1623 b'enabled',
1619 1624 default=False,
1620 1625 )
1621 1626 coreconfigitem(
1622 1627 b'profiling',
1623 1628 b'format',
1624 1629 default=b'text',
1625 1630 )
1626 1631 coreconfigitem(
1627 1632 b'profiling',
1628 1633 b'freq',
1629 1634 default=1000,
1630 1635 )
1631 1636 coreconfigitem(
1632 1637 b'profiling',
1633 1638 b'limit',
1634 1639 default=30,
1635 1640 )
1636 1641 coreconfigitem(
1637 1642 b'profiling',
1638 1643 b'nested',
1639 1644 default=0,
1640 1645 )
1641 1646 coreconfigitem(
1642 1647 b'profiling',
1643 1648 b'output',
1644 1649 default=None,
1645 1650 )
1646 1651 coreconfigitem(
1647 1652 b'profiling',
1648 1653 b'showmax',
1649 1654 default=0.999,
1650 1655 )
1651 1656 coreconfigitem(
1652 1657 b'profiling',
1653 1658 b'showmin',
1654 1659 default=dynamicdefault,
1655 1660 )
1656 1661 coreconfigitem(
1657 1662 b'profiling',
1658 1663 b'showtime',
1659 1664 default=True,
1660 1665 )
1661 1666 coreconfigitem(
1662 1667 b'profiling',
1663 1668 b'sort',
1664 1669 default=b'inlinetime',
1665 1670 )
1666 1671 coreconfigitem(
1667 1672 b'profiling',
1668 1673 b'statformat',
1669 1674 default=b'hotpath',
1670 1675 )
1671 1676 coreconfigitem(
1672 1677 b'profiling',
1673 1678 b'time-track',
1674 1679 default=dynamicdefault,
1675 1680 )
1676 1681 coreconfigitem(
1677 1682 b'profiling',
1678 1683 b'type',
1679 1684 default=b'stat',
1680 1685 )
1681 1686 coreconfigitem(
1682 1687 b'progress',
1683 1688 b'assume-tty',
1684 1689 default=False,
1685 1690 )
1686 1691 coreconfigitem(
1687 1692 b'progress',
1688 1693 b'changedelay',
1689 1694 default=1,
1690 1695 )
1691 1696 coreconfigitem(
1692 1697 b'progress',
1693 1698 b'clear-complete',
1694 1699 default=True,
1695 1700 )
1696 1701 coreconfigitem(
1697 1702 b'progress',
1698 1703 b'debug',
1699 1704 default=False,
1700 1705 )
1701 1706 coreconfigitem(
1702 1707 b'progress',
1703 1708 b'delay',
1704 1709 default=3,
1705 1710 )
1706 1711 coreconfigitem(
1707 1712 b'progress',
1708 1713 b'disable',
1709 1714 default=False,
1710 1715 )
1711 1716 coreconfigitem(
1712 1717 b'progress',
1713 1718 b'estimateinterval',
1714 1719 default=60.0,
1715 1720 )
1716 1721 coreconfigitem(
1717 1722 b'progress',
1718 1723 b'format',
1719 1724 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1720 1725 )
1721 1726 coreconfigitem(
1722 1727 b'progress',
1723 1728 b'refresh',
1724 1729 default=0.1,
1725 1730 )
1726 1731 coreconfigitem(
1727 1732 b'progress',
1728 1733 b'width',
1729 1734 default=dynamicdefault,
1730 1735 )
1731 1736 coreconfigitem(
1732 1737 b'pull',
1733 1738 b'confirm',
1734 1739 default=False,
1735 1740 )
1736 1741 coreconfigitem(
1737 1742 b'push',
1738 1743 b'pushvars.server',
1739 1744 default=False,
1740 1745 )
1741 1746 coreconfigitem(
1742 1747 b'rewrite',
1743 1748 b'backup-bundle',
1744 1749 default=True,
1745 1750 alias=[(b'ui', b'history-editing-backup')],
1746 1751 )
1747 1752 coreconfigitem(
1748 1753 b'rewrite',
1749 1754 b'update-timestamp',
1750 1755 default=False,
1751 1756 )
1752 1757 coreconfigitem(
1753 1758 b'rewrite',
1754 1759 b'empty-successor',
1755 1760 default=b'skip',
1756 1761 experimental=True,
1757 1762 )
1758 1763 coreconfigitem(
1759 1764 b'storage',
1760 1765 b'new-repo-backend',
1761 1766 default=b'revlogv1',
1762 1767 experimental=True,
1763 1768 )
1764 1769 coreconfigitem(
1765 1770 b'storage',
1766 1771 b'revlog.optimize-delta-parent-choice',
1767 1772 default=True,
1768 1773 alias=[(b'format', b'aggressivemergedeltas')],
1769 1774 )
1770 1775 # experimental as long as rust is experimental (or a C version is implemented)
1771 1776 coreconfigitem(
1772 1777 b'storage',
1773 1778 b'revlog.persistent-nodemap.mmap',
1774 1779 default=True,
1775 1780 )
1776 1781 # experimental as long as format.use-persistent-nodemap is.
1777 1782 coreconfigitem(
1778 1783 b'storage',
1779 1784 b'revlog.persistent-nodemap.slow-path',
1780 1785 default=b"abort",
1781 1786 )
1782 1787
1783 1788 coreconfigitem(
1784 1789 b'storage',
1785 1790 b'revlog.reuse-external-delta',
1786 1791 default=True,
1787 1792 )
1788 1793 coreconfigitem(
1789 1794 b'storage',
1790 1795 b'revlog.reuse-external-delta-parent',
1791 1796 default=None,
1792 1797 )
1793 1798 coreconfigitem(
1794 1799 b'storage',
1795 1800 b'revlog.zlib.level',
1796 1801 default=None,
1797 1802 )
1798 1803 coreconfigitem(
1799 1804 b'storage',
1800 1805 b'revlog.zstd.level',
1801 1806 default=None,
1802 1807 )
1803 1808 coreconfigitem(
1804 1809 b'server',
1805 1810 b'bookmarks-pushkey-compat',
1806 1811 default=True,
1807 1812 )
1808 1813 coreconfigitem(
1809 1814 b'server',
1810 1815 b'bundle1',
1811 1816 default=True,
1812 1817 )
1813 1818 coreconfigitem(
1814 1819 b'server',
1815 1820 b'bundle1gd',
1816 1821 default=None,
1817 1822 )
1818 1823 coreconfigitem(
1819 1824 b'server',
1820 1825 b'bundle1.pull',
1821 1826 default=None,
1822 1827 )
1823 1828 coreconfigitem(
1824 1829 b'server',
1825 1830 b'bundle1gd.pull',
1826 1831 default=None,
1827 1832 )
1828 1833 coreconfigitem(
1829 1834 b'server',
1830 1835 b'bundle1.push',
1831 1836 default=None,
1832 1837 )
1833 1838 coreconfigitem(
1834 1839 b'server',
1835 1840 b'bundle1gd.push',
1836 1841 default=None,
1837 1842 )
1838 1843 coreconfigitem(
1839 1844 b'server',
1840 1845 b'bundle2.stream',
1841 1846 default=True,
1842 1847 alias=[(b'experimental', b'bundle2.stream')],
1843 1848 )
1844 1849 coreconfigitem(
1845 1850 b'server',
1846 1851 b'compressionengines',
1847 1852 default=list,
1848 1853 )
1849 1854 coreconfigitem(
1850 1855 b'server',
1851 1856 b'concurrent-push-mode',
1852 1857 default=b'check-related',
1853 1858 )
1854 1859 coreconfigitem(
1855 1860 b'server',
1856 1861 b'disablefullbundle',
1857 1862 default=False,
1858 1863 )
1859 1864 coreconfigitem(
1860 1865 b'server',
1861 1866 b'maxhttpheaderlen',
1862 1867 default=1024,
1863 1868 )
1864 1869 coreconfigitem(
1865 1870 b'server',
1866 1871 b'pullbundle',
1867 1872 default=False,
1868 1873 )
1869 1874 coreconfigitem(
1870 1875 b'server',
1871 1876 b'preferuncompressed',
1872 1877 default=False,
1873 1878 )
1874 1879 coreconfigitem(
1875 1880 b'server',
1876 1881 b'streamunbundle',
1877 1882 default=False,
1878 1883 )
1879 1884 coreconfigitem(
1880 1885 b'server',
1881 1886 b'uncompressed',
1882 1887 default=True,
1883 1888 )
1884 1889 coreconfigitem(
1885 1890 b'server',
1886 1891 b'uncompressedallowsecret',
1887 1892 default=False,
1888 1893 )
1889 1894 coreconfigitem(
1890 1895 b'server',
1891 1896 b'view',
1892 1897 default=b'served',
1893 1898 )
1894 1899 coreconfigitem(
1895 1900 b'server',
1896 1901 b'validate',
1897 1902 default=False,
1898 1903 )
1899 1904 coreconfigitem(
1900 1905 b'server',
1901 1906 b'zliblevel',
1902 1907 default=-1,
1903 1908 )
1904 1909 coreconfigitem(
1905 1910 b'server',
1906 1911 b'zstdlevel',
1907 1912 default=3,
1908 1913 )
1909 1914 coreconfigitem(
1910 1915 b'share',
1911 1916 b'pool',
1912 1917 default=None,
1913 1918 )
1914 1919 coreconfigitem(
1915 1920 b'share',
1916 1921 b'poolnaming',
1917 1922 default=b'identity',
1918 1923 )
1919 1924 coreconfigitem(
1920 1925 b'share',
1921 1926 b'safe-mismatch.source-not-safe',
1922 1927 default=b'abort',
1923 1928 )
1924 1929 coreconfigitem(
1925 1930 b'share',
1926 1931 b'safe-mismatch.source-safe',
1927 1932 default=b'abort',
1928 1933 )
1929 1934 coreconfigitem(
1930 1935 b'share',
1931 1936 b'safe-mismatch.source-not-safe.warn',
1932 1937 default=True,
1933 1938 )
1934 1939 coreconfigitem(
1935 1940 b'share',
1936 1941 b'safe-mismatch.source-safe.warn',
1937 1942 default=True,
1938 1943 )
1939 1944 coreconfigitem(
1940 1945 b'shelve',
1941 1946 b'maxbackups',
1942 1947 default=10,
1943 1948 )
1944 1949 coreconfigitem(
1945 1950 b'smtp',
1946 1951 b'host',
1947 1952 default=None,
1948 1953 )
1949 1954 coreconfigitem(
1950 1955 b'smtp',
1951 1956 b'local_hostname',
1952 1957 default=None,
1953 1958 )
1954 1959 coreconfigitem(
1955 1960 b'smtp',
1956 1961 b'password',
1957 1962 default=None,
1958 1963 )
1959 1964 coreconfigitem(
1960 1965 b'smtp',
1961 1966 b'port',
1962 1967 default=dynamicdefault,
1963 1968 )
1964 1969 coreconfigitem(
1965 1970 b'smtp',
1966 1971 b'tls',
1967 1972 default=b'none',
1968 1973 )
1969 1974 coreconfigitem(
1970 1975 b'smtp',
1971 1976 b'username',
1972 1977 default=None,
1973 1978 )
1974 1979 coreconfigitem(
1975 1980 b'sparse',
1976 1981 b'missingwarning',
1977 1982 default=True,
1978 1983 experimental=True,
1979 1984 )
1980 1985 coreconfigitem(
1981 1986 b'subrepos',
1982 1987 b'allowed',
1983 1988 default=dynamicdefault, # to make backporting simpler
1984 1989 )
1985 1990 coreconfigitem(
1986 1991 b'subrepos',
1987 1992 b'hg:allowed',
1988 1993 default=dynamicdefault,
1989 1994 )
1990 1995 coreconfigitem(
1991 1996 b'subrepos',
1992 1997 b'git:allowed',
1993 1998 default=dynamicdefault,
1994 1999 )
1995 2000 coreconfigitem(
1996 2001 b'subrepos',
1997 2002 b'svn:allowed',
1998 2003 default=dynamicdefault,
1999 2004 )
2000 2005 coreconfigitem(
2001 2006 b'templates',
2002 2007 b'.*',
2003 2008 default=None,
2004 2009 generic=True,
2005 2010 )
2006 2011 coreconfigitem(
2007 2012 b'templateconfig',
2008 2013 b'.*',
2009 2014 default=dynamicdefault,
2010 2015 generic=True,
2011 2016 )
2012 2017 coreconfigitem(
2013 2018 b'trusted',
2014 2019 b'groups',
2015 2020 default=list,
2016 2021 )
2017 2022 coreconfigitem(
2018 2023 b'trusted',
2019 2024 b'users',
2020 2025 default=list,
2021 2026 )
2022 2027 coreconfigitem(
2023 2028 b'ui',
2024 2029 b'_usedassubrepo',
2025 2030 default=False,
2026 2031 )
2027 2032 coreconfigitem(
2028 2033 b'ui',
2029 2034 b'allowemptycommit',
2030 2035 default=False,
2031 2036 )
2032 2037 coreconfigitem(
2033 2038 b'ui',
2034 2039 b'archivemeta',
2035 2040 default=True,
2036 2041 )
2037 2042 coreconfigitem(
2038 2043 b'ui',
2039 2044 b'askusername',
2040 2045 default=False,
2041 2046 )
2042 2047 coreconfigitem(
2043 2048 b'ui',
2044 2049 b'available-memory',
2045 2050 default=None,
2046 2051 )
2047 2052
2048 2053 coreconfigitem(
2049 2054 b'ui',
2050 2055 b'clonebundlefallback',
2051 2056 default=False,
2052 2057 )
2053 2058 coreconfigitem(
2054 2059 b'ui',
2055 2060 b'clonebundleprefers',
2056 2061 default=list,
2057 2062 )
2058 2063 coreconfigitem(
2059 2064 b'ui',
2060 2065 b'clonebundles',
2061 2066 default=True,
2062 2067 )
2063 2068 coreconfigitem(
2064 2069 b'ui',
2065 2070 b'color',
2066 2071 default=b'auto',
2067 2072 )
2068 2073 coreconfigitem(
2069 2074 b'ui',
2070 2075 b'commitsubrepos',
2071 2076 default=False,
2072 2077 )
2073 2078 coreconfigitem(
2074 2079 b'ui',
2075 2080 b'debug',
2076 2081 default=False,
2077 2082 )
2078 2083 coreconfigitem(
2079 2084 b'ui',
2080 2085 b'debugger',
2081 2086 default=None,
2082 2087 )
2083 2088 coreconfigitem(
2084 2089 b'ui',
2085 2090 b'editor',
2086 2091 default=dynamicdefault,
2087 2092 )
2088 2093 coreconfigitem(
2089 2094 b'ui',
2090 2095 b'detailed-exit-code',
2091 2096 default=False,
2092 2097 experimental=True,
2093 2098 )
2094 2099 coreconfigitem(
2095 2100 b'ui',
2096 2101 b'fallbackencoding',
2097 2102 default=None,
2098 2103 )
2099 2104 coreconfigitem(
2100 2105 b'ui',
2101 2106 b'forcecwd',
2102 2107 default=None,
2103 2108 )
2104 2109 coreconfigitem(
2105 2110 b'ui',
2106 2111 b'forcemerge',
2107 2112 default=None,
2108 2113 )
2109 2114 coreconfigitem(
2110 2115 b'ui',
2111 2116 b'formatdebug',
2112 2117 default=False,
2113 2118 )
2114 2119 coreconfigitem(
2115 2120 b'ui',
2116 2121 b'formatjson',
2117 2122 default=False,
2118 2123 )
2119 2124 coreconfigitem(
2120 2125 b'ui',
2121 2126 b'formatted',
2122 2127 default=None,
2123 2128 )
2124 2129 coreconfigitem(
2125 2130 b'ui',
2126 2131 b'interactive',
2127 2132 default=None,
2128 2133 )
2129 2134 coreconfigitem(
2130 2135 b'ui',
2131 2136 b'interface',
2132 2137 default=None,
2133 2138 )
2134 2139 coreconfigitem(
2135 2140 b'ui',
2136 2141 b'interface.chunkselector',
2137 2142 default=None,
2138 2143 )
2139 2144 coreconfigitem(
2140 2145 b'ui',
2141 2146 b'large-file-limit',
2142 2147 default=10000000,
2143 2148 )
2144 2149 coreconfigitem(
2145 2150 b'ui',
2146 2151 b'logblockedtimes',
2147 2152 default=False,
2148 2153 )
2149 2154 coreconfigitem(
2150 2155 b'ui',
2151 2156 b'merge',
2152 2157 default=None,
2153 2158 )
2154 2159 coreconfigitem(
2155 2160 b'ui',
2156 2161 b'mergemarkers',
2157 2162 default=b'basic',
2158 2163 )
2159 2164 coreconfigitem(
2160 2165 b'ui',
2161 2166 b'message-output',
2162 2167 default=b'stdio',
2163 2168 )
2164 2169 coreconfigitem(
2165 2170 b'ui',
2166 2171 b'nontty',
2167 2172 default=False,
2168 2173 )
2169 2174 coreconfigitem(
2170 2175 b'ui',
2171 2176 b'origbackuppath',
2172 2177 default=None,
2173 2178 )
2174 2179 coreconfigitem(
2175 2180 b'ui',
2176 2181 b'paginate',
2177 2182 default=True,
2178 2183 )
2179 2184 coreconfigitem(
2180 2185 b'ui',
2181 2186 b'patch',
2182 2187 default=None,
2183 2188 )
2184 2189 coreconfigitem(
2185 2190 b'ui',
2186 2191 b'portablefilenames',
2187 2192 default=b'warn',
2188 2193 )
2189 2194 coreconfigitem(
2190 2195 b'ui',
2191 2196 b'promptecho',
2192 2197 default=False,
2193 2198 )
2194 2199 coreconfigitem(
2195 2200 b'ui',
2196 2201 b'quiet',
2197 2202 default=False,
2198 2203 )
2199 2204 coreconfigitem(
2200 2205 b'ui',
2201 2206 b'quietbookmarkmove',
2202 2207 default=False,
2203 2208 )
2204 2209 coreconfigitem(
2205 2210 b'ui',
2206 2211 b'relative-paths',
2207 2212 default=b'legacy',
2208 2213 )
2209 2214 coreconfigitem(
2210 2215 b'ui',
2211 2216 b'remotecmd',
2212 2217 default=b'hg',
2213 2218 )
2214 2219 coreconfigitem(
2215 2220 b'ui',
2216 2221 b'report_untrusted',
2217 2222 default=True,
2218 2223 )
2219 2224 coreconfigitem(
2220 2225 b'ui',
2221 2226 b'rollback',
2222 2227 default=True,
2223 2228 )
2224 2229 coreconfigitem(
2225 2230 b'ui',
2226 2231 b'signal-safe-lock',
2227 2232 default=True,
2228 2233 )
2229 2234 coreconfigitem(
2230 2235 b'ui',
2231 2236 b'slash',
2232 2237 default=False,
2233 2238 )
2234 2239 coreconfigitem(
2235 2240 b'ui',
2236 2241 b'ssh',
2237 2242 default=b'ssh',
2238 2243 )
2239 2244 coreconfigitem(
2240 2245 b'ui',
2241 2246 b'ssherrorhint',
2242 2247 default=None,
2243 2248 )
2244 2249 coreconfigitem(
2245 2250 b'ui',
2246 2251 b'statuscopies',
2247 2252 default=False,
2248 2253 )
2249 2254 coreconfigitem(
2250 2255 b'ui',
2251 2256 b'strict',
2252 2257 default=False,
2253 2258 )
2254 2259 coreconfigitem(
2255 2260 b'ui',
2256 2261 b'style',
2257 2262 default=b'',
2258 2263 )
2259 2264 coreconfigitem(
2260 2265 b'ui',
2261 2266 b'supportcontact',
2262 2267 default=None,
2263 2268 )
2264 2269 coreconfigitem(
2265 2270 b'ui',
2266 2271 b'textwidth',
2267 2272 default=78,
2268 2273 )
2269 2274 coreconfigitem(
2270 2275 b'ui',
2271 2276 b'timeout',
2272 2277 default=b'600',
2273 2278 )
2274 2279 coreconfigitem(
2275 2280 b'ui',
2276 2281 b'timeout.warn',
2277 2282 default=0,
2278 2283 )
2279 2284 coreconfigitem(
2280 2285 b'ui',
2281 2286 b'timestamp-output',
2282 2287 default=False,
2283 2288 )
2284 2289 coreconfigitem(
2285 2290 b'ui',
2286 2291 b'traceback',
2287 2292 default=False,
2288 2293 )
2289 2294 coreconfigitem(
2290 2295 b'ui',
2291 2296 b'tweakdefaults',
2292 2297 default=False,
2293 2298 )
2294 2299 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
2295 2300 coreconfigitem(
2296 2301 b'ui',
2297 2302 b'verbose',
2298 2303 default=False,
2299 2304 )
2300 2305 coreconfigitem(
2301 2306 b'verify',
2302 2307 b'skipflags',
2303 2308 default=None,
2304 2309 )
2305 2310 coreconfigitem(
2306 2311 b'web',
2307 2312 b'allowbz2',
2308 2313 default=False,
2309 2314 )
2310 2315 coreconfigitem(
2311 2316 b'web',
2312 2317 b'allowgz',
2313 2318 default=False,
2314 2319 )
2315 2320 coreconfigitem(
2316 2321 b'web',
2317 2322 b'allow-pull',
2318 2323 alias=[(b'web', b'allowpull')],
2319 2324 default=True,
2320 2325 )
2321 2326 coreconfigitem(
2322 2327 b'web',
2323 2328 b'allow-push',
2324 2329 alias=[(b'web', b'allow_push')],
2325 2330 default=list,
2326 2331 )
2327 2332 coreconfigitem(
2328 2333 b'web',
2329 2334 b'allowzip',
2330 2335 default=False,
2331 2336 )
2332 2337 coreconfigitem(
2333 2338 b'web',
2334 2339 b'archivesubrepos',
2335 2340 default=False,
2336 2341 )
2337 2342 coreconfigitem(
2338 2343 b'web',
2339 2344 b'cache',
2340 2345 default=True,
2341 2346 )
2342 2347 coreconfigitem(
2343 2348 b'web',
2344 2349 b'comparisoncontext',
2345 2350 default=5,
2346 2351 )
2347 2352 coreconfigitem(
2348 2353 b'web',
2349 2354 b'contact',
2350 2355 default=None,
2351 2356 )
2352 2357 coreconfigitem(
2353 2358 b'web',
2354 2359 b'deny_push',
2355 2360 default=list,
2356 2361 )
2357 2362 coreconfigitem(
2358 2363 b'web',
2359 2364 b'guessmime',
2360 2365 default=False,
2361 2366 )
2362 2367 coreconfigitem(
2363 2368 b'web',
2364 2369 b'hidden',
2365 2370 default=False,
2366 2371 )
2367 2372 coreconfigitem(
2368 2373 b'web',
2369 2374 b'labels',
2370 2375 default=list,
2371 2376 )
2372 2377 coreconfigitem(
2373 2378 b'web',
2374 2379 b'logoimg',
2375 2380 default=b'hglogo.png',
2376 2381 )
2377 2382 coreconfigitem(
2378 2383 b'web',
2379 2384 b'logourl',
2380 2385 default=b'https://mercurial-scm.org/',
2381 2386 )
2382 2387 coreconfigitem(
2383 2388 b'web',
2384 2389 b'accesslog',
2385 2390 default=b'-',
2386 2391 )
2387 2392 coreconfigitem(
2388 2393 b'web',
2389 2394 b'address',
2390 2395 default=b'',
2391 2396 )
2392 2397 coreconfigitem(
2393 2398 b'web',
2394 2399 b'allow-archive',
2395 2400 alias=[(b'web', b'allow_archive')],
2396 2401 default=list,
2397 2402 )
2398 2403 coreconfigitem(
2399 2404 b'web',
2400 2405 b'allow_read',
2401 2406 default=list,
2402 2407 )
2403 2408 coreconfigitem(
2404 2409 b'web',
2405 2410 b'baseurl',
2406 2411 default=None,
2407 2412 )
2408 2413 coreconfigitem(
2409 2414 b'web',
2410 2415 b'cacerts',
2411 2416 default=None,
2412 2417 )
2413 2418 coreconfigitem(
2414 2419 b'web',
2415 2420 b'certificate',
2416 2421 default=None,
2417 2422 )
2418 2423 coreconfigitem(
2419 2424 b'web',
2420 2425 b'collapse',
2421 2426 default=False,
2422 2427 )
2423 2428 coreconfigitem(
2424 2429 b'web',
2425 2430 b'csp',
2426 2431 default=None,
2427 2432 )
2428 2433 coreconfigitem(
2429 2434 b'web',
2430 2435 b'deny_read',
2431 2436 default=list,
2432 2437 )
2433 2438 coreconfigitem(
2434 2439 b'web',
2435 2440 b'descend',
2436 2441 default=True,
2437 2442 )
2438 2443 coreconfigitem(
2439 2444 b'web',
2440 2445 b'description',
2441 2446 default=b"",
2442 2447 )
2443 2448 coreconfigitem(
2444 2449 b'web',
2445 2450 b'encoding',
2446 2451 default=lambda: encoding.encoding,
2447 2452 )
2448 2453 coreconfigitem(
2449 2454 b'web',
2450 2455 b'errorlog',
2451 2456 default=b'-',
2452 2457 )
2453 2458 coreconfigitem(
2454 2459 b'web',
2455 2460 b'ipv6',
2456 2461 default=False,
2457 2462 )
2458 2463 coreconfigitem(
2459 2464 b'web',
2460 2465 b'maxchanges',
2461 2466 default=10,
2462 2467 )
2463 2468 coreconfigitem(
2464 2469 b'web',
2465 2470 b'maxfiles',
2466 2471 default=10,
2467 2472 )
2468 2473 coreconfigitem(
2469 2474 b'web',
2470 2475 b'maxshortchanges',
2471 2476 default=60,
2472 2477 )
2473 2478 coreconfigitem(
2474 2479 b'web',
2475 2480 b'motd',
2476 2481 default=b'',
2477 2482 )
2478 2483 coreconfigitem(
2479 2484 b'web',
2480 2485 b'name',
2481 2486 default=dynamicdefault,
2482 2487 )
2483 2488 coreconfigitem(
2484 2489 b'web',
2485 2490 b'port',
2486 2491 default=8000,
2487 2492 )
2488 2493 coreconfigitem(
2489 2494 b'web',
2490 2495 b'prefix',
2491 2496 default=b'',
2492 2497 )
2493 2498 coreconfigitem(
2494 2499 b'web',
2495 2500 b'push_ssl',
2496 2501 default=True,
2497 2502 )
2498 2503 coreconfigitem(
2499 2504 b'web',
2500 2505 b'refreshinterval',
2501 2506 default=20,
2502 2507 )
2503 2508 coreconfigitem(
2504 2509 b'web',
2505 2510 b'server-header',
2506 2511 default=None,
2507 2512 )
2508 2513 coreconfigitem(
2509 2514 b'web',
2510 2515 b'static',
2511 2516 default=None,
2512 2517 )
2513 2518 coreconfigitem(
2514 2519 b'web',
2515 2520 b'staticurl',
2516 2521 default=None,
2517 2522 )
2518 2523 coreconfigitem(
2519 2524 b'web',
2520 2525 b'stripes',
2521 2526 default=1,
2522 2527 )
2523 2528 coreconfigitem(
2524 2529 b'web',
2525 2530 b'style',
2526 2531 default=b'paper',
2527 2532 )
2528 2533 coreconfigitem(
2529 2534 b'web',
2530 2535 b'templates',
2531 2536 default=None,
2532 2537 )
2533 2538 coreconfigitem(
2534 2539 b'web',
2535 2540 b'view',
2536 2541 default=b'served',
2537 2542 experimental=True,
2538 2543 )
2539 2544 coreconfigitem(
2540 2545 b'worker',
2541 2546 b'backgroundclose',
2542 2547 default=dynamicdefault,
2543 2548 )
2544 2549 # Windows defaults to a limit of 512 open files. A buffer of 128
2545 2550 # should give us enough headway.
2546 2551 coreconfigitem(
2547 2552 b'worker',
2548 2553 b'backgroundclosemaxqueue',
2549 2554 default=384,
2550 2555 )
2551 2556 coreconfigitem(
2552 2557 b'worker',
2553 2558 b'backgroundcloseminfilecount',
2554 2559 default=2048,
2555 2560 )
2556 2561 coreconfigitem(
2557 2562 b'worker',
2558 2563 b'backgroundclosethreadcount',
2559 2564 default=4,
2560 2565 )
2561 2566 coreconfigitem(
2562 2567 b'worker',
2563 2568 b'enabled',
2564 2569 default=True,
2565 2570 )
2566 2571 coreconfigitem(
2567 2572 b'worker',
2568 2573 b'numcpus',
2569 2574 default=None,
2570 2575 )
2571 2576
2572 2577 # Rebase related configuration moved to core because other extension are doing
2573 2578 # strange things. For example, shelve import the extensions to reuse some bit
2574 2579 # without formally loading it.
2575 2580 coreconfigitem(
2576 2581 b'commands',
2577 2582 b'rebase.requiredest',
2578 2583 default=False,
2579 2584 )
2580 2585 coreconfigitem(
2581 2586 b'experimental',
2582 2587 b'rebaseskipobsolete',
2583 2588 default=True,
2584 2589 )
2585 2590 coreconfigitem(
2586 2591 b'rebase',
2587 2592 b'singletransaction',
2588 2593 default=False,
2589 2594 )
2590 2595 coreconfigitem(
2591 2596 b'rebase',
2592 2597 b'experimental.inmemory',
2593 2598 default=False,
2594 2599 )
@@ -1,58 +1,59 b''
1 1 # Read the output of a "svn log --xml" command on stdin, parse it and
2 2 # print a subset of attributes common to all svn versions tested by
3 3 # hg.
4 4 from __future__ import absolute_import
5 5 import sys
6 6 import xml.dom.minidom
7 7
8 8
9 9 def xmltext(e):
10 10 return ''.join(c.data for c in e.childNodes if c.nodeType == c.TEXT_NODE)
11 11
12 12
13 13 def parseentry(entry):
14 14 e = {}
15 15 e['revision'] = entry.getAttribute('revision')
16 16 e['author'] = xmltext(entry.getElementsByTagName('author')[0])
17 17 e['msg'] = xmltext(entry.getElementsByTagName('msg')[0])
18 e['date'] = xmltext(entry.getElementsByTagName('date')[0])
18 19 e['paths'] = []
19 20 paths = entry.getElementsByTagName('paths')
20 21 if paths:
21 22 paths = paths[0]
22 23 for p in paths.getElementsByTagName('path'):
23 24 action = p.getAttribute('action').encode('utf-8')
24 25 path = xmltext(p).encode('utf-8')
25 26 frompath = p.getAttribute('copyfrom-path').encode('utf-8')
26 27 fromrev = p.getAttribute('copyfrom-rev').encode('utf-8')
27 28 e['paths'].append((path, action, frompath, fromrev))
28 29 return e
29 30
30 31
31 32 def parselog(data):
32 33 entries = []
33 34 doc = xml.dom.minidom.parseString(data)
34 35 for e in doc.getElementsByTagName('logentry'):
35 36 entries.append(parseentry(e))
36 37 return entries
37 38
38 39
39 40 def printentries(entries):
40 41 try:
41 42 fp = sys.stdout.buffer
42 43 except AttributeError:
43 44 fp = sys.stdout
44 45 for e in entries:
45 for k in ('revision', 'author', 'msg'):
46 for k in ('revision', 'author', 'date', 'msg'):
46 47 fp.write(('%s: %s\n' % (k, e[k])).encode('utf-8'))
47 48 for path, action, fpath, frev in sorted(e['paths']):
48 49 frominfo = b''
49 50 if frev:
50 51 frominfo = b' (from %s@%s)' % (fpath, frev)
51 52 p = b' %s %s%s\n' % (action, path, frominfo)
52 53 fp.write(p)
53 54
54 55
55 56 if __name__ == '__main__':
56 57 data = sys.stdin.read()
57 58 entries = parselog(data)
58 59 printentries(entries)
@@ -1,550 +1,634 b''
1 1 #require svn13
2 2
3 3 $ svnupanddisplay()
4 4 > {
5 5 > (
6 6 > cd $1;
7 7 > svn up -q;
8 8 > svn st -v | sed 's/ */ /g' | sort
9 9 > limit=''
10 10 > if [ $2 -gt 0 ]; then
11 11 > limit="--limit=$2"
12 12 > fi
13 13 > svn log --xml -v $limit | "$PYTHON" "$TESTDIR/svnxml.py"
14 14 > )
15 15 > }
16 16
17 17 $ cat >> $HGRCPATH <<EOF
18 18 > [extensions]
19 19 > convert =
20 20 > EOF
21 21
22 22 $ hg init a
23 23
24 24 Add
25 25
26 26 $ echo a > a/a
27 27 $ mkdir -p a/d1/d2
28 28 $ echo b > a/d1/d2/b
29 29 $ hg --cwd a ci -d '0 0' -A -m 'add a file'
30 30 adding a
31 31 adding d1/d2/b
32 32
33 33 Modify
34 34
35 35 $ svn-safe-append.py a a/a
36 36 $ hg --cwd a ci -d '1 0' -m 'modify a file'
37 37 $ hg --cwd a tip -q
38 38 1:e0e2b8a9156b
39 39
40 40 $ hg convert -d svn a
41 41 assuming destination a-hg
42 42 initializing svn repository 'a-hg'
43 43 initializing svn working copy 'a-hg-wc'
44 44 scanning source...
45 45 sorting...
46 46 converting...
47 47 1 add a file
48 48 0 modify a file
49 49 $ svnupanddisplay a-hg-wc 2
50 50 2 1 test d1
51 51 2 1 test d1/d2
52 52 2 1 test d1/d2/b
53 53 2 2 test .
54 54 2 2 test a
55 55 revision: 2
56 56 author: test
57 date: * (glob)
57 58 msg: modify a file
58 59 M /a
59 60 revision: 1
60 61 author: test
62 date: * (glob)
61 63 msg: add a file
62 64 A /a
63 65 A /d1
64 66 A /d1/d2
65 67 A /d1/d2/b
66 68 $ ls a a-hg-wc
67 69 a:
68 70 a
69 71 d1
70 72
71 73 a-hg-wc:
72 74 a
73 75 d1
74 76 $ cmp a/a a-hg-wc/a
75 77
76 78 Rename
77 79
78 80 $ hg --cwd a mv a b
79 81 $ hg --cwd a ci -d '2 0' -m 'rename a file'
80 82 $ hg --cwd a tip -q
81 83 2:eb5169441d43
82 84
83 85 $ hg convert -d svn a
84 86 assuming destination a-hg
85 87 initializing svn working copy 'a-hg-wc'
86 88 scanning source...
87 89 sorting...
88 90 converting...
89 91 0 rename a file
90 92 $ svnupanddisplay a-hg-wc 1
91 93 3 1 test d1
92 94 3 1 test d1/d2
93 95 3 1 test d1/d2/b
94 96 3 3 test .
95 97 3 3 test b
96 98 revision: 3
97 99 author: test
100 date: * (glob)
98 101 msg: rename a file
99 102 D /a
100 103 A /b (from /a@2)
101 104 $ ls a a-hg-wc
102 105 a:
103 106 b
104 107 d1
105 108
106 109 a-hg-wc:
107 110 b
108 111 d1
109 112
110 113 Copy
111 114
112 115 $ hg --cwd a cp b c
113 116
114 117 $ hg --cwd a ci -d '3 0' -m 'copy a file'
115 118 $ hg --cwd a tip -q
116 119 3:60effef6ab48
117 120
118 121 $ hg convert -d svn a
119 122 assuming destination a-hg
120 123 initializing svn working copy 'a-hg-wc'
121 124 scanning source...
122 125 sorting...
123 126 converting...
124 127 0 copy a file
125 128 $ svnupanddisplay a-hg-wc 1
126 129 4 1 test d1
127 130 4 1 test d1/d2
128 131 4 1 test d1/d2/b
129 132 4 3 test b
130 133 4 4 test .
131 134 4 4 test c
132 135 revision: 4
133 136 author: test
137 date: * (glob)
134 138 msg: copy a file
135 139 A /c (from /b@3)
136 140 $ ls a a-hg-wc
137 141 a:
138 142 b
139 143 c
140 144 d1
141 145
142 146 a-hg-wc:
143 147 b
144 148 c
145 149 d1
146 150
147 151 $ hg --cwd a rm b
148 152
149 153 Remove
150 154
151 155 $ hg --cwd a ci -d '4 0' -m 'remove a file'
152 156 $ hg --cwd a tip -q
153 157 4:87bbe3013fb6
154 158
155 159 $ hg convert -d svn a
156 160 assuming destination a-hg
157 161 initializing svn working copy 'a-hg-wc'
158 162 scanning source...
159 163 sorting...
160 164 converting...
161 165 0 remove a file
162 166 $ svnupanddisplay a-hg-wc 1
163 167 5 1 test d1
164 168 5 1 test d1/d2
165 169 5 1 test d1/d2/b
166 170 5 4 test c
167 171 5 5 test .
168 172 revision: 5
169 173 author: test
174 date: * (glob)
170 175 msg: remove a file
171 176 D /b
172 177 $ ls a a-hg-wc
173 178 a:
174 179 c
175 180 d1
176 181
177 182 a-hg-wc:
178 183 c
179 184 d1
180 185
181 186 Executable
182 187
183 188 #if execbit
184 189 $ chmod +x a/c
185 190 #else
186 191 $ echo fake >> a/c
187 192 #endif
188 193 $ hg --cwd a ci -d '5 0' -m 'make a file executable'
189 194 #if execbit
190 195 $ hg --cwd a tip -q
191 196 5:ff42e473c340
192 197 #else
193 198 $ hg --cwd a tip -q
194 199 5:817a700c8cf1
195 200 #endif
196 201
197 202 $ hg convert -d svn a
198 203 assuming destination a-hg
199 204 initializing svn working copy 'a-hg-wc'
200 205 scanning source...
201 206 sorting...
202 207 converting...
203 208 0 make a file executable
204 209 $ svnupanddisplay a-hg-wc 1
205 210 6 1 test d1
206 211 6 1 test d1/d2
207 212 6 1 test d1/d2/b
208 213 6 6 test .
209 214 6 6 test c
210 215 revision: 6
211 216 author: test
217 date: * (glob)
212 218 msg: make a file executable
213 219 M /c
214 220 #if execbit
215 221 $ test -x a-hg-wc/c
216 222 #endif
217 223
218 224 #if symlink
219 225
220 226 Symlinks
221 227
222 228 $ ln -s a/missing a/link
223 229 $ hg --cwd a commit -Am 'add symlink'
224 230 adding link
225 231 $ hg --cwd a mv link newlink
226 232 $ hg --cwd a commit -m 'move symlink'
227 233 $ hg convert -d svn a a-svnlink
228 234 initializing svn repository 'a-svnlink'
229 235 initializing svn working copy 'a-svnlink-wc'
230 236 scanning source...
231 237 sorting...
232 238 converting...
233 239 7 add a file
234 240 6 modify a file
235 241 5 rename a file
236 242 4 copy a file
237 243 3 remove a file
238 244 2 make a file executable
239 245 1 add symlink
240 246 0 move symlink
241 247 $ svnupanddisplay a-svnlink-wc 1
242 248 8 1 test d1
243 249 8 1 test d1/d2
244 250 8 1 test d1/d2/b
245 251 8 6 test c
246 252 8 8 test .
247 253 8 8 test newlink
248 254 revision: 8
249 255 author: test
256 date: * (glob)
250 257 msg: move symlink
251 258 D /link
252 259 A /newlink (from /link@7)
253 260
254 261 Make sure our changes don't affect the rest of the test cases
255 262
256 263 $ hg --cwd a up 5
257 264 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
258 265 $ hg --cwd a --config extensions.strip= strip -r 6
259 266 saved backup bundle to $TESTTMP/a/.hg/strip-backup/bd4f7b7a7067-ed505e42-backup.hg
260 267
261 268 #endif
262 269
263 270 Convert with --full adds and removes files that didn't change
264 271
265 272 $ touch a/f
266 273 $ hg -R a ci -Aqmf
267 274 $ echo "rename c d" > filemap
268 275 $ hg convert -d svn a --filemap filemap --full
269 276 assuming destination a-hg
270 277 initializing svn working copy 'a-hg-wc'
271 278 scanning source...
272 279 sorting...
273 280 converting...
274 281 0 f
275 282 $ svnupanddisplay a-hg-wc 1
276 283 7 7 test .
277 284 7 7 test d
278 285 7 7 test f
279 286 revision: 7
280 287 author: test
288 date: * (glob)
281 289 msg: f
282 290 D /c
283 291 A /d
284 292 D /d1
285 293 A /f
286 294
287 295 $ rm -rf a a-hg a-hg-wc
288 296
289 297
290 298 Executable in new directory
291 299
292 300 $ hg init a
293 301
294 302 $ mkdir a/d1
295 303 $ echo a > a/d1/a
296 304 #if execbit
297 305 $ chmod +x a/d1/a
298 306 #else
299 307 $ echo fake >> a/d1/a
300 308 #endif
301 309 $ hg --cwd a ci -d '0 0' -A -m 'add executable file in new directory'
302 310 adding d1/a
303 311
304 312 $ hg convert -d svn a
305 313 assuming destination a-hg
306 314 initializing svn repository 'a-hg'
307 315 initializing svn working copy 'a-hg-wc'
308 316 scanning source...
309 317 sorting...
310 318 converting...
311 319 0 add executable file in new directory
312 320 $ svnupanddisplay a-hg-wc 1
313 321 1 1 test .
314 322 1 1 test d1
315 323 1 1 test d1/a
316 324 revision: 1
317 325 author: test
326 date: * (glob)
318 327 msg: add executable file in new directory
319 328 A /d1
320 329 A /d1/a
321 330 #if execbit
322 331 $ test -x a-hg-wc/d1/a
323 332 #endif
324 333
325 334 Copy to new directory
326 335
327 336 $ mkdir a/d2
328 337 $ hg --cwd a cp d1/a d2/a
329 338 $ hg --cwd a ci -d '1 0' -A -m 'copy file to new directory'
330 339
331 340 $ hg convert -d svn a
332 341 assuming destination a-hg
333 342 initializing svn working copy 'a-hg-wc'
334 343 scanning source...
335 344 sorting...
336 345 converting...
337 346 0 copy file to new directory
338 347 $ svnupanddisplay a-hg-wc 1
339 348 2 1 test d1
340 349 2 1 test d1/a
341 350 2 2 test .
342 351 2 2 test d2
343 352 2 2 test d2/a
344 353 revision: 2
345 354 author: test
355 date: * (glob)
346 356 msg: copy file to new directory
347 357 A /d2
348 358 A /d2/a (from /d1/a@1)
349 359
350 360 Branchy history
351 361
352 362 $ hg init b
353 363 $ echo base > b/b
354 364 $ hg --cwd b ci -d '0 0' -Ambase
355 365 adding b
356 366
357 367 $ svn-safe-append.py left-1 b/b
358 368 $ echo left-1 > b/left-1
359 369 $ hg --cwd b ci -d '1 0' -Amleft-1
360 370 adding left-1
361 371
362 372 $ svn-safe-append.py left-2 b/b
363 373 $ echo left-2 > b/left-2
364 374 $ hg --cwd b ci -d '2 0' -Amleft-2
365 375 adding left-2
366 376
367 377 $ hg --cwd b up 0
368 378 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
369 379
370 380 $ svn-safe-append.py right-1 b/b
371 381 $ echo right-1 > b/right-1
372 382 $ hg --cwd b ci -d '3 0' -Amright-1
373 383 adding right-1
374 384 created new head
375 385
376 386 $ svn-safe-append.py right-2 b/b
377 387 $ echo right-2 > b/right-2
378 388 $ hg --cwd b ci -d '4 0' -Amright-2
379 389 adding right-2
380 390
381 391 $ hg --cwd b up -C 2
382 392 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
383 393 $ hg --cwd b merge
384 394 merging b
385 395 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
386 396 2 files updated, 0 files merged, 0 files removed, 1 files unresolved
387 397 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
388 398 [1]
389 399 $ hg --cwd b revert -r 2 b
390 400 $ hg --cwd b resolve -m b
391 401 (no more unresolved files)
392 402 $ hg --cwd b ci -d '5 0' -m 'merge'
393 403
394 404 Expect 4 changes
395 405
396 406 $ hg convert -d svn b
397 407 assuming destination b-hg
398 408 initializing svn repository 'b-hg'
399 409 initializing svn working copy 'b-hg-wc'
400 410 scanning source...
401 411 sorting...
402 412 converting...
403 413 5 base
404 414 4 left-1
405 415 3 left-2
406 416 2 right-1
407 417 1 right-2
408 418 0 merge
409 419
410 420 $ svnupanddisplay b-hg-wc 0
411 421 4 2 test left-1
412 422 4 3 test b
413 423 4 3 test left-2
414 424 4 4 test .
415 425 4 4 test right-1
416 426 4 4 test right-2
417 427 revision: 4
418 428 author: test
429 date: * (glob)
419 430 msg: merge
420 431 A /right-1
421 432 A /right-2
422 433 revision: 3
423 434 author: test
435 date: * (glob)
424 436 msg: left-2
425 437 M /b
426 438 A /left-2
427 439 revision: 2
428 440 author: test
441 date: * (glob)
429 442 msg: left-1
430 443 M /b
431 444 A /left-1
432 445 revision: 1
433 446 author: test
447 date: * (glob)
434 448 msg: base
435 449 A /b
436 450
437 451 Tags are not supported, but must not break conversion
438 452
439 453 $ rm -rf a a-hg a-hg-wc
440 454 $ hg init a
441 455 $ echo a > a/a
442 456 $ hg --cwd a ci -d '0 0' -A -m 'Add file a'
443 457 adding a
444 458 $ hg --cwd a tag -d '1 0' -m 'Tagged as v1.0' v1.0
445 459
446 460 $ hg convert -d svn a
447 461 assuming destination a-hg
448 462 initializing svn repository 'a-hg'
449 463 initializing svn working copy 'a-hg-wc'
450 464 scanning source...
451 465 sorting...
452 466 converting...
453 467 1 Add file a
454 468 0 Tagged as v1.0
455 469 writing Subversion tags is not yet implemented
456 470 $ svnupanddisplay a-hg-wc 2
457 471 2 1 test a
458 472 2 2 test .
459 473 2 2 test .hgtags
460 474 revision: 2
461 475 author: test
476 date: * (glob)
462 477 msg: Tagged as v1.0
463 478 A /.hgtags
464 479 revision: 1
465 480 author: test
481 date: * (glob)
466 482 msg: Add file a
467 483 A /a
468 484 $ rm -rf a a-hg a-hg-wc
469 485
470 486 #if execbit
471 487
472 488 Executable bit removal
473 489
474 490 $ hg init a
475 491
476 492 $ echo a > a/exec
477 493 $ chmod +x a/exec
478 494 $ hg --cwd a ci -d '1 0' -A -m 'create executable'
479 495 adding exec
480 496 $ chmod -x a/exec
481 497 $ hg --cwd a ci -d '2 0' -A -m 'remove executable bit'
482 498
483 499 $ hg convert -d svn a
484 500 assuming destination a-hg
485 501 initializing svn repository 'a-hg'
486 502 initializing svn working copy 'a-hg-wc'
487 503 scanning source...
488 504 sorting...
489 505 converting...
490 506 1 create executable
491 507 0 remove executable bit
492 508 $ svnupanddisplay a-hg-wc 0
493 509 2 2 test .
494 510 2 2 test exec
495 511 revision: 2
496 512 author: test
513 date: * (glob)
497 514 msg: remove executable bit
498 515 M /exec
499 516 revision: 1
500 517 author: test
518 date: * (glob)
501 519 msg: create executable
502 520 A /exec
503 521 $ test ! -x a-hg-wc/exec
504 522
505 523 $ rm -rf a a-hg a-hg-wc
506 524
507 525 #endif
508 526
509 527 Skipping empty commits
510 528
511 529 $ hg init a
512 530
513 531 $ hg --cwd a --config ui.allowemptycommit=True ci -d '1 0' -m 'Initial empty commit'
514 532
515 533 $ echo a > a/a
516 534 $ hg --cwd a ci -d '0 0' -A -m 'Some change'
517 535 adding a
518 536 $ hg --cwd a --config ui.allowemptycommit=True ci -d '2 0' -m 'Empty commit 1'
519 537 $ hg --cwd a --config ui.allowemptycommit=True ci -d '3 0' -m 'Empty commit 2'
520 538 $ echo b > a/b
521 539 $ hg --cwd a ci -d '0 0' -A -m 'Another change'
522 540 adding b
523 541
524 542 $ hg convert -d svn a
525 543 assuming destination a-hg
526 544 initializing svn repository 'a-hg'
527 545 initializing svn working copy 'a-hg-wc'
528 546 scanning source...
529 547 sorting...
530 548 converting...
531 549 4 Initial empty commit
532 550 3 Some change
533 551 2 Empty commit 1
534 552 1 Empty commit 2
535 553 0 Another change
536 554
537 555 $ svnupanddisplay a-hg-wc 0
538 556 2 1 test a
539 557 2 2 test .
540 558 2 2 test b
541 559 revision: 2
542 560 author: test
561 date: * (glob)
543 562 msg: Another change
544 563 A /b
545 564 revision: 1
546 565 author: test
566 date: * (glob)
547 567 msg: Some change
548 568 A /a
549 569
550 570 $ rm -rf a a-hg a-hg-wc
571
572 Commit dates convertion
573
574 $ hg init a
575
576 $ echo a >> a/a
577 $ hg add a
578 adding a/a
579 $ hg --cwd a ci -d '1 0' -A -m 'Change 1'
580
581 $ echo a >> a/a
582 $ hg --cwd a ci -d '2 0' -m 'Change 2'
583
584 $ echo a >> a/a
585 $ hg --cwd a ci -d '2 0' -m 'Change at the same time'
586
587 $ echo a >> a/a
588 $ hg --cwd a ci -d '1 0' -m 'Change in the past'
589
590 $ echo a >> a/a
591 $ hg --cwd a ci -d '3 0' -m 'Change in the future'
592
593 $ hg convert --config convert.svn.dangerous-set-commit-dates=true -d svn a
594 assuming destination a-hg
595 initializing svn repository 'a-hg'
596 initializing svn working copy 'a-hg-wc'
597 scanning source...
598 sorting...
599 converting...
600 4 Change 1
601 3 Change 2
602 2 Change at the same time
603 1 Change in the past
604 0 Change in the future
605 $ svnupanddisplay a-hg-wc 0
606 5 5 test .
607 5 5 test a
608 revision: 5
609 author: test
610 date: 1970-01-01T00:00:03.000000Z
611 msg: Change in the future
612 M /a
613 revision: 4
614 author: test
615 date: 1970-01-01T00:00:01.000000Z
616 msg: Change in the past
617 M /a
618 revision: 3
619 author: test
620 date: 1970-01-01T00:00:02.000000Z
621 msg: Change at the same time
622 M /a
623 revision: 2
624 author: test
625 date: 1970-01-01T00:00:02.000000Z
626 msg: Change 2
627 M /a
628 revision: 1
629 author: test
630 date: 1970-01-01T00:00:01.000000Z
631 msg: Change 1
632 A /a
633
634 $ rm -rf a a-hg a-hg-wc
@@ -1,635 +1,652 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > convert=
4 4 > [convert]
5 5 > hg.saverev=False
6 6 > EOF
7 7 $ hg help convert
8 8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
9 9
10 10 convert a foreign SCM repository to a Mercurial one.
11 11
12 12 Accepted source formats [identifiers]:
13 13
14 14 - Mercurial [hg]
15 15 - CVS [cvs]
16 16 - Darcs [darcs]
17 17 - git [git]
18 18 - Subversion [svn]
19 19 - Monotone [mtn]
20 20 - GNU Arch [gnuarch]
21 21 - Bazaar [bzr]
22 22 - Perforce [p4]
23 23
24 24 Accepted destination formats [identifiers]:
25 25
26 26 - Mercurial [hg]
27 27 - Subversion [svn] (history on branches is not preserved)
28 28
29 29 If no revision is given, all revisions will be converted. Otherwise,
30 30 convert will only import up to the named revision (given in a format
31 31 understood by the source).
32 32
33 33 If no destination directory name is specified, it defaults to the basename
34 34 of the source with "-hg" appended. If the destination repository doesn't
35 35 exist, it will be created.
36 36
37 37 By default, all sources except Mercurial will use --branchsort. Mercurial
38 38 uses --sourcesort to preserve original revision numbers order. Sort modes
39 39 have the following effects:
40 40
41 41 --branchsort convert from parent to child revision when possible, which
42 42 means branches are usually converted one after the other.
43 43 It generates more compact repositories.
44 44 --datesort sort revisions by date. Converted repositories have good-
45 45 looking changelogs but are often an order of magnitude
46 46 larger than the same ones generated by --branchsort.
47 47 --sourcesort try to preserve source revisions order, only supported by
48 48 Mercurial sources.
49 49 --closesort try to move closed revisions as close as possible to parent
50 50 branches, only supported by Mercurial sources.
51 51
52 52 If "REVMAP" isn't given, it will be put in a default location
53 53 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
54 54 maps each source commit ID to the destination ID for that revision, like
55 55 so:
56 56
57 57 <source ID> <destination ID>
58 58
59 59 If the file doesn't exist, it's automatically created. It's updated on
60 60 each commit copied, so 'hg convert' can be interrupted and can be run
61 61 repeatedly to copy new commits.
62 62
63 63 The authormap is a simple text file that maps each source commit author to
64 64 a destination commit author. It is handy for source SCMs that use unix
65 65 logins to identify authors (e.g.: CVS). One line per author mapping and
66 66 the line format is:
67 67
68 68 source author = destination author
69 69
70 70 Empty lines and lines starting with a "#" are ignored.
71 71
72 72 The filemap is a file that allows filtering and remapping of files and
73 73 directories. Each line can contain one of the following directives:
74 74
75 75 include path/to/file-or-dir
76 76
77 77 exclude path/to/file-or-dir
78 78
79 79 rename path/to/source path/to/destination
80 80
81 81 Comment lines start with "#". A specified path matches if it equals the
82 82 full relative name of a file or one of its parent directories. The
83 83 "include" or "exclude" directive with the longest matching path applies,
84 84 so line order does not matter.
85 85
86 86 The "include" directive causes a file, or all files under a directory, to
87 87 be included in the destination repository. The default if there are no
88 88 "include" statements is to include everything. If there are any "include"
89 89 statements, nothing else is included. The "exclude" directive causes files
90 90 or directories to be omitted. The "rename" directive renames a file or
91 91 directory if it is converted. To rename from a subdirectory into the root
92 92 of the repository, use "." as the path to rename to.
93 93
94 94 "--full" will make sure the converted changesets contain exactly the right
95 95 files with the right content. It will make a full conversion of all files,
96 96 not just the ones that have changed. Files that already are correct will
97 97 not be changed. This can be used to apply filemap changes when converting
98 98 incrementally. This is currently only supported for Mercurial and
99 99 Subversion.
100 100
101 101 The splicemap is a file that allows insertion of synthetic history,
102 102 letting you specify the parents of a revision. This is useful if you want
103 103 to e.g. give a Subversion merge two parents, or graft two disconnected
104 104 series of history together. Each entry contains a key, followed by a
105 105 space, followed by one or two comma-separated values:
106 106
107 107 key parent1, parent2
108 108
109 109 The key is the revision ID in the source revision control system whose
110 110 parents should be modified (same format as a key in .hg/shamap). The
111 111 values are the revision IDs (in either the source or destination revision
112 112 control system) that should be used as the new parents for that node. For
113 113 example, if you have merged "release-1.0" into "trunk", then you should
114 114 specify the revision on "trunk" as the first parent and the one on the
115 115 "release-1.0" branch as the second.
116 116
117 117 The branchmap is a file that allows you to rename a branch when it is
118 118 being brought in from whatever external repository. When used in
119 119 conjunction with a splicemap, it allows for a powerful combination to help
120 120 fix even the most badly mismanaged repositories and turn them into nicely
121 121 structured Mercurial repositories. The branchmap contains lines of the
122 122 form:
123 123
124 124 original_branch_name new_branch_name
125 125
126 126 where "original_branch_name" is the name of the branch in the source
127 127 repository, and "new_branch_name" is the name of the branch is the
128 128 destination repository. No whitespace is allowed in the new branch name.
129 129 This can be used to (for instance) move code in one repository from
130 130 "default" to a named branch.
131 131
132 132 Mercurial Source
133 133 ################
134 134
135 135 The Mercurial source recognizes the following configuration options, which
136 136 you can set on the command line with "--config":
137 137
138 138 convert.hg.ignoreerrors
139 139 ignore integrity errors when reading. Use it to fix
140 140 Mercurial repositories with missing revlogs, by converting
141 141 from and to Mercurial. Default is False.
142 142 convert.hg.saverev
143 143 store original revision ID in changeset (forces target IDs
144 144 to change). It takes a boolean argument and defaults to
145 145 False.
146 146 convert.hg.startrev
147 147 specify the initial Mercurial revision. The default is 0.
148 148 convert.hg.revs
149 149 revset specifying the source revisions to convert.
150 150
151 151 Bazaar Source
152 152 #############
153 153
154 154 The following options can be used with "--config":
155 155
156 156 convert.bzr.saverev
157 157 whether to store the original Bazaar commit ID in the
158 158 metadata of the destination commit. The default is True.
159 159
160 160 CVS Source
161 161 ##########
162 162
163 163 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
164 164 indicate the starting point of what will be converted. Direct access to
165 165 the repository files is not needed, unless of course the repository is
166 166 ":local:". The conversion uses the top level directory in the sandbox to
167 167 find the CVS repository, and then uses CVS rlog commands to find files to
168 168 convert. This means that unless a filemap is given, all files under the
169 169 starting directory will be converted, and that any directory
170 170 reorganization in the CVS sandbox is ignored.
171 171
172 172 The following options can be used with "--config":
173 173
174 174 convert.cvsps.cache
175 175 Set to False to disable remote log caching, for testing and
176 176 debugging purposes. Default is True.
177 177 convert.cvsps.fuzz
178 178 Specify the maximum time (in seconds) that is allowed
179 179 between commits with identical user and log message in a
180 180 single changeset. When very large files were checked in as
181 181 part of a changeset then the default may not be long enough.
182 182 The default is 60.
183 183 convert.cvsps.logencoding
184 184 Specify encoding name to be used for transcoding CVS log
185 185 messages. Multiple encoding names can be specified as a list
186 186 (see 'hg help config.Syntax'), but only the first acceptable
187 187 encoding in the list is used per CVS log entries. This
188 188 transcoding is executed before cvslog hook below.
189 189 convert.cvsps.mergeto
190 190 Specify a regular expression to which commit log messages
191 191 are matched. If a match occurs, then the conversion process
192 192 will insert a dummy revision merging the branch on which
193 193 this log message occurs to the branch indicated in the
194 194 regex. Default is "{{mergetobranch ([-\w]+)}}"
195 195 convert.cvsps.mergefrom
196 196 Specify a regular expression to which commit log messages
197 197 are matched. If a match occurs, then the conversion process
198 198 will add the most recent revision on the branch indicated in
199 199 the regex as the second parent of the changeset. Default is
200 200 "{{mergefrombranch ([-\w]+)}}"
201 201 convert.localtimezone
202 202 use local time (as determined by the TZ environment
203 203 variable) for changeset date/times. The default is False
204 204 (use UTC).
205 205 hooks.cvslog Specify a Python function to be called at the end of
206 206 gathering the CVS log. The function is passed a list with
207 207 the log entries, and can modify the entries in-place, or add
208 208 or delete them.
209 209 hooks.cvschangesets
210 210 Specify a Python function to be called after the changesets
211 211 are calculated from the CVS log. The function is passed a
212 212 list with the changeset entries, and can modify the
213 213 changesets in-place, or add or delete them.
214 214
215 215 An additional "debugcvsps" Mercurial command allows the builtin changeset
216 216 merging code to be run without doing a conversion. Its parameters and
217 217 output are similar to that of cvsps 2.1. Please see the command help for
218 218 more details.
219 219
220 220 Subversion Source
221 221 #################
222 222
223 223 Subversion source detects classical trunk/branches/tags layouts. By
224 224 default, the supplied "svn://repo/path/" source URL is converted as a
225 225 single branch. If "svn://repo/path/trunk" exists it replaces the default
226 226 branch. If "svn://repo/path/branches" exists, its subdirectories are
227 227 listed as possible branches. If "svn://repo/path/tags" exists, it is
228 228 looked for tags referencing converted branches. Default "trunk",
229 229 "branches" and "tags" values can be overridden with following options. Set
230 230 them to paths relative to the source URL, or leave them blank to disable
231 231 auto detection.
232 232
233 233 The following options can be set with "--config":
234 234
235 235 convert.svn.branches
236 236 specify the directory containing branches. The default is
237 237 "branches".
238 238 convert.svn.tags
239 239 specify the directory containing tags. The default is
240 240 "tags".
241 241 convert.svn.trunk
242 242 specify the name of the trunk branch. The default is
243 243 "trunk".
244 244 convert.localtimezone
245 245 use local time (as determined by the TZ environment
246 246 variable) for changeset date/times. The default is False
247 247 (use UTC).
248 248
249 249 Source history can be retrieved starting at a specific revision, instead
250 250 of being integrally converted. Only single branch conversions are
251 251 supported.
252 252
253 253 convert.svn.startrev
254 254 specify start Subversion revision number. The default is 0.
255 255
256 256 Git Source
257 257 ##########
258 258
259 259 The Git importer converts commits from all reachable branches (refs in
260 260 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
261 261 converted to bookmarks with the same name, with the leading 'refs/heads'
262 262 stripped. Git submodules are converted to Git subrepos in Mercurial.
263 263
264 264 The following options can be set with "--config":
265 265
266 266 convert.git.similarity
267 267 specify how similar files modified in a commit must be to be
268 268 imported as renames or copies, as a percentage between "0"
269 269 (disabled) and "100" (files must be identical). For example,
270 270 "90" means that a delete/add pair will be imported as a
271 271 rename if more than 90% of the file hasn't changed. The
272 272 default is "50".
273 273 convert.git.findcopiesharder
274 274 while detecting copies, look at all files in the working
275 275 copy instead of just changed ones. This is very expensive
276 276 for large projects, and is only effective when
277 277 "convert.git.similarity" is greater than 0. The default is
278 278 False.
279 279 convert.git.renamelimit
280 280 perform rename and copy detection up to this many changed
281 281 files in a commit. Increasing this will make rename and copy
282 282 detection more accurate but will significantly slow down
283 283 computation on large projects. The option is only relevant
284 284 if "convert.git.similarity" is greater than 0. The default
285 285 is "400".
286 286 convert.git.committeractions
287 287 list of actions to take when processing author and committer
288 288 values.
289 289
290 290 Git commits have separate author (who wrote the commit) and committer
291 291 (who applied the commit) fields. Not all destinations support separate
292 292 author and committer fields (including Mercurial). This config option
293 293 controls what to do with these author and committer fields during
294 294 conversion.
295 295
296 296 A value of "messagedifferent" will append a "committer: ..." line to
297 297 the commit message if the Git committer is different from the author.
298 298 The prefix of that line can be specified using the syntax
299 299 "messagedifferent=<prefix>". e.g. "messagedifferent=git-committer:".
300 300 When a prefix is specified, a space will always be inserted between
301 301 the prefix and the value.
302 302
303 303 "messagealways" behaves like "messagedifferent" except it will always
304 304 result in a "committer: ..." line being appended to the commit
305 305 message. This value is mutually exclusive with "messagedifferent".
306 306
307 307 "dropcommitter" will remove references to the committer. Only
308 308 references to the author will remain. Actions that add references to
309 309 the committer will have no effect when this is set.
310 310
311 311 "replaceauthor" will replace the value of the author field with the
312 312 committer. Other actions that add references to the committer will
313 313 still take effect when this is set.
314 314
315 315 The default is "messagedifferent".
316 316
317 317 convert.git.extrakeys
318 318 list of extra keys from commit metadata to copy to the
319 319 destination. Some Git repositories store extra metadata in
320 320 commits. By default, this non-default metadata will be lost
321 321 during conversion. Setting this config option can retain
322 322 that metadata. Some built-in keys such as "parent" and
323 323 "branch" are not allowed to be copied.
324 324 convert.git.remoteprefix
325 325 remote refs are converted as bookmarks with
326 326 "convert.git.remoteprefix" as a prefix followed by a /. The
327 327 default is 'remote'.
328 328 convert.git.saverev
329 329 whether to store the original Git commit ID in the metadata
330 330 of the destination commit. The default is True.
331 331 convert.git.skipsubmodules
332 332 does not convert root level .gitmodules files or files with
333 333 160000 mode indicating a submodule. Default is False.
334 334
335 335 Perforce Source
336 336 ###############
337 337
338 338 The Perforce (P4) importer can be given a p4 depot path or a client
339 339 specification as source. It will convert all files in the source to a flat
340 340 Mercurial repository, ignoring labels, branches and integrations. Note
341 341 that when a depot path is given you then usually should specify a target
342 342 directory, because otherwise the target may be named "...-hg".
343 343
344 344 The following options can be set with "--config":
345 345
346 346 convert.p4.encoding
347 347 specify the encoding to use when decoding standard output of
348 348 the Perforce command line tool. The default is default
349 349 system encoding.
350 350 convert.p4.startrev
351 351 specify initial Perforce revision (a Perforce changelist
352 352 number).
353 353
354 354 Mercurial Destination
355 355 #####################
356 356
357 357 The Mercurial destination will recognize Mercurial subrepositories in the
358 358 destination directory, and update the .hgsubstate file automatically if
359 359 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
360 360 Converting a repository with subrepositories requires converting a single
361 361 repository at a time, from the bottom up.
362 362
363 363 The following options are supported:
364 364
365 365 convert.hg.clonebranches
366 366 dispatch source branches in separate clones. The default is
367 367 False.
368 368 convert.hg.tagsbranch
369 369 branch name for tag revisions, defaults to "default".
370 370 convert.hg.usebranchnames
371 371 preserve branch names. The default is True.
372 372 convert.hg.sourcename
373 373 records the given string as a 'convert_source' extra value
374 374 on each commit made in the target repository. The default is
375 375 None.
376 376 convert.hg.preserve-hash
377 377 only works with mercurial sources. Make convert prevent
378 378 performance improvement to the list of modified files in
379 379 commits when such an improvement would cause the hash of a
380 380 commit to change. The default is False.
381 381
382 382 All Destinations
383 383 ################
384 384
385 385 All destination types accept the following options:
386 386
387 387 convert.skiptags
388 388 does not convert tags from the source repo to the target
389 389 repo. The default is False.
390 390
391 Subversion Destination
392 ######################
393
394 Original commit dates are not preserved by default.
395
396 convert.svn.dangerous-set-commit-dates
397 preserve original commit dates, forcefully setting
398 "svn:date" revision properties. This option is DANGEROUS and
399 may break some subversion functionality for the resulting
400 repository (e.g. filtering revisions with date ranges in
401 "svn log"), as original commit dates are not guaranteed to
402 be monotonically increasing.
403
404 For commit dates setting to work destination repository must have "pre-
405 revprop-change" hook configured to allow setting of "svn:date" revision
406 properties. See Subversion documentation for more details.
407
391 408 options ([+] can be repeated):
392 409
393 410 -s --source-type TYPE source repository type
394 411 -d --dest-type TYPE destination repository type
395 412 -r --rev REV [+] import up to source revision REV
396 413 -A --authormap FILE remap usernames using this file
397 414 --filemap FILE remap file names using contents of file
398 415 --full apply filemap changes by converting all files again
399 416 --splicemap FILE splice synthesized history into place
400 417 --branchmap FILE change branch names while converting
401 418 --branchsort try to sort changesets by branches
402 419 --datesort try to sort changesets by date
403 420 --sourcesort preserve source changesets order
404 421 --closesort try to reorder closed revisions
405 422
406 423 (some details hidden, use --verbose to show complete help)
407 424 $ hg init a
408 425 $ cd a
409 426 $ echo a > a
410 427 $ hg ci -d'0 0' -Ama
411 428 adding a
412 429 $ hg cp a b
413 430 $ hg ci -d'1 0' -mb
414 431 $ hg rm a
415 432 $ hg ci -d'2 0' -mc
416 433 $ hg mv b a
417 434 $ hg ci -d'3 0' -md
418 435 $ echo a >> a
419 436 $ hg ci -d'4 0' -me
420 437 $ cd ..
421 438 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
422 439 assuming destination a-hg
423 440 initializing destination a-hg repository
424 441 scanning source...
425 442 sorting...
426 443 converting...
427 444 4 a
428 445 3 b
429 446 2 c
430 447 1 d
431 448 0 e
432 449 $ hg --cwd a-hg pull ../a
433 450 pulling from ../a
434 451 searching for changes
435 452 no changes found
436 453 5 local changesets published
437 454
438 455 conversion to existing file should fail
439 456
440 457 $ touch bogusfile
441 458 $ hg convert a bogusfile
442 459 initializing destination bogusfile repository
443 460 abort: cannot create new bundle repository
444 461 [255]
445 462
446 463 #if unix-permissions no-root
447 464
448 465 conversion to dir without permissions should fail
449 466
450 467 $ mkdir bogusdir
451 468 $ chmod 000 bogusdir
452 469
453 470 $ hg convert a bogusdir
454 471 abort: Permission denied: *bogusdir* (glob)
455 472 [255]
456 473
457 474 user permissions should succeed
458 475
459 476 $ chmod 700 bogusdir
460 477 $ hg convert a bogusdir
461 478 initializing destination bogusdir repository
462 479 scanning source...
463 480 sorting...
464 481 converting...
465 482 4 a
466 483 3 b
467 484 2 c
468 485 1 d
469 486 0 e
470 487
471 488 #endif
472 489
473 490 test pre and post conversion actions
474 491
475 492 $ echo 'include b' > filemap
476 493 $ hg convert --debug --filemap filemap a partialb | \
477 494 > grep 'run hg'
478 495 run hg source pre-conversion action
479 496 run hg sink pre-conversion action
480 497 run hg sink post-conversion action
481 498 run hg source post-conversion action
482 499
483 500 converting empty dir should fail "nicely
484 501
485 502 $ mkdir emptydir
486 503
487 504 override $PATH to ensure p4 not visible; use $PYTHON in case we're
488 505 running from a devel copy, not a temp installation
489 506
490 507 $ PATH="$BINDIR" "$PYTHON" "$BINDIR"/hg convert emptydir
491 508 assuming destination emptydir-hg
492 509 initializing destination emptydir-hg repository
493 510 emptydir does not look like a CVS checkout
494 511 $TESTTMP/emptydir does not look like a Git repository
495 512 emptydir does not look like a Subversion repository
496 513 emptydir is not a local Mercurial repository
497 514 emptydir does not look like a darcs repository
498 515 emptydir does not look like a monotone repository
499 516 emptydir does not look like a GNU Arch repository
500 517 emptydir does not look like a Bazaar repository
501 518 cannot find required "p4" tool
502 519 abort: emptydir: missing or unsupported repository
503 520 [255]
504 521
505 522 convert with imaginary source type
506 523
507 524 $ hg convert --source-type foo a a-foo
508 525 initializing destination a-foo repository
509 526 abort: foo: invalid source repository type
510 527 [255]
511 528
512 529 convert with imaginary sink type
513 530
514 531 $ hg convert --dest-type foo a a-foo
515 532 abort: foo: invalid destination repository type
516 533 [255]
517 534
518 535 testing: convert must not produce duplicate entries in fncache
519 536
520 537 $ hg convert a b
521 538 initializing destination b repository
522 539 scanning source...
523 540 sorting...
524 541 converting...
525 542 4 a
526 543 3 b
527 544 2 c
528 545 1 d
529 546 0 e
530 547
531 548 contents of fncache file:
532 549
533 550 #if repofncache
534 551 $ cat b/.hg/store/fncache | sort
535 552 data/a.i (reporevlogstore !)
536 553 data/b.i (reporevlogstore !)
537 554 #endif
538 555
539 556 test bogus URL
540 557
541 558 #if no-msys
542 559 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
543 560 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
544 561 [255]
545 562 #endif
546 563
547 564 test revset converted() lookup
548 565
549 566 $ hg --config convert.hg.saverev=True convert a c
550 567 initializing destination c repository
551 568 scanning source...
552 569 sorting...
553 570 converting...
554 571 4 a
555 572 3 b
556 573 2 c
557 574 1 d
558 575 0 e
559 576 $ echo f > c/f
560 577 $ hg -R c ci -d'0 0' -Amf
561 578 adding f
562 579 created new head
563 580 $ hg -R c log -r "converted(09d945a62ce6)"
564 581 changeset: 1:98c3dd46a874
565 582 user: test
566 583 date: Thu Jan 01 00:00:01 1970 +0000
567 584 summary: b
568 585
569 586 $ hg -R c log -r "converted()"
570 587 changeset: 0:31ed57b2037c
571 588 user: test
572 589 date: Thu Jan 01 00:00:00 1970 +0000
573 590 summary: a
574 591
575 592 changeset: 1:98c3dd46a874
576 593 user: test
577 594 date: Thu Jan 01 00:00:01 1970 +0000
578 595 summary: b
579 596
580 597 changeset: 2:3b9ca06ef716
581 598 user: test
582 599 date: Thu Jan 01 00:00:02 1970 +0000
583 600 summary: c
584 601
585 602 changeset: 3:4e0debd37cf2
586 603 user: test
587 604 date: Thu Jan 01 00:00:03 1970 +0000
588 605 summary: d
589 606
590 607 changeset: 4:9de3bc9349c5
591 608 user: test
592 609 date: Thu Jan 01 00:00:04 1970 +0000
593 610 summary: e
594 611
595 612
596 613 test specifying a sourcename
597 614 $ echo g > a/g
598 615 $ hg -R a ci -d'0 0' -Amg
599 616 adding g
600 617 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
601 618 scanning source...
602 619 sorting...
603 620 converting...
604 621 0 g
605 622 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
606 623 branch=default
607 624 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
608 625 convert_source=mysource
609 626
610 627 $ cat > branchmap.txt << EOF
611 628 > old branch new_branch
612 629 > EOF
613 630
614 631 $ hg -R a branch -q 'old branch'
615 632 $ echo gg > a/g
616 633 $ hg -R a ci -m 'branch name with spaces'
617 634 $ hg convert --branchmap branchmap.txt a d
618 635 initializing destination d repository
619 636 scanning source...
620 637 sorting...
621 638 converting...
622 639 6 a
623 640 5 b
624 641 4 c
625 642 3 d
626 643 2 e
627 644 1 g
628 645 0 branch name with spaces
629 646
630 647 $ hg -R a branches
631 648 old branch 6:a24a66ade009
632 649 default 5:a3bc6100aa8e (inactive)
633 650 $ hg -R d branches
634 651 new_branch 6:64ed208b732b
635 652 default 5:a3bc6100aa8e (inactive)
General Comments 0
You need to be logged in to leave comments. Login now