Show More
@@ -491,6 +491,22 b' def convert(ui, src, dest=None, revmapfi' | |||
|
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 |
@@ -97,6 +97,17 b' def fs2svn(s):' | |||
|
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 | |
@@ -1158,12 +1169,7 b' class svn_source(converter_source):' | |||
|
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 | |
@@ -1380,7 +1386,7 b' class svn_source(converter_source):' | |||
|
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" |
@@ -1388,15 +1394,26 b' 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) |
@@ -1470,9 +1487,20 b' class svn_sink(converter_sink, commandli' | |||
|
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 | |
@@ -1667,6 +1695,23 b' class svn_sink(converter_sink, commandli' | |||
|
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) |
@@ -570,6 +570,11 b' coreconfigitem(' | |||
|
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, |
@@ -15,6 +15,7 b' def parseentry(entry):' | |||
|
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: |
@@ -42,7 +43,7 b' def printentries(entries):' | |||
|
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'' |
@@ -54,10 +54,12 b' Modify' | |||
|
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 |
@@ -95,6 +97,7 b' Rename' | |||
|
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) |
@@ -131,6 +134,7 b' Copy' | |||
|
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 |
@@ -167,6 +171,7 b' Remove' | |||
|
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 |
@@ -209,6 +214,7 b' Executable' | |||
|
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 |
@@ -247,6 +253,7 b' Symlinks' | |||
|
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) |
@@ -278,6 +285,7 b' Convert with --full adds and removes fil' | |||
|
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 |
@@ -315,6 +323,7 b' Executable in new directory' | |||
|
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 |
@@ -343,6 +352,7 b' Copy to new directory' | |||
|
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) |
@@ -416,21 +426,25 b' Expect 4 changes' | |||
|
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 | |
@@ -459,10 +473,12 b' Tags are not supported, but must not bre' | |||
|
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 |
@@ -494,10 +510,12 b' Executable bit removal' | |||
|
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 |
@@ -540,11 +558,77 b' Skipping empty commits' | |||
|
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 |
@@ -388,6 +388,23 b'' | |||
|
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 |
General Comments 0
You need to be logged in to leave comments.
Login now