Show More
@@ -491,6 +491,22 b' def convert(ui, src, dest=None, revmapfi' | |||||
491 |
|
491 | |||
492 | :convert.skiptags: does not convert tags from the source repo to the target |
|
492 | :convert.skiptags: does not convert tags from the source repo to the target | |
493 | repo. The default is False. |
|
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 | return convcmd.convert(ui, src, dest, revmapfile, **opts) |
|
511 | return convcmd.convert(ui, src, dest, revmapfile, **opts) | |
496 |
|
512 |
@@ -97,6 +97,17 b' def fs2svn(s):' | |||||
97 | return s.decode(fsencoding).encode('utf-8') |
|
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 | class SvnPathNotFound(Exception): |
|
111 | class SvnPathNotFound(Exception): | |
101 | pass |
|
112 | pass | |
102 |
|
113 | |||
@@ -1158,12 +1169,7 b' class svn_source(converter_source):' | |||||
1158 | continue |
|
1169 | continue | |
1159 | paths.append((path, ent)) |
|
1170 | paths.append((path, ent)) | |
1160 |
|
1171 | |||
1161 | # Example SVN datetime. Includes microseconds. |
|
1172 | date = parsesvndate(date) | |
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 | ) |
|
|||
1167 | if self.ui.configbool(b'convert', b'localtimezone'): |
|
1173 | if self.ui.configbool(b'convert', b'localtimezone'): | |
1168 | date = makedatetimestamp(date[0]) |
|
1174 | date = makedatetimestamp(date[0]) | |
1169 |
|
1175 | |||
@@ -1380,7 +1386,7 b' class svn_source(converter_source):' | |||||
1380 | return logstream(stdout) |
|
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 | REPOS="$1" |
|
1391 | REPOS="$1" | |
1386 | REV="$2" |
|
1392 | REV="$2" | |
@@ -1388,15 +1394,26 b' USER="$3"' | |||||
1388 | PROPNAME="$4" |
|
1394 | PROPNAME="$4" | |
1389 | ACTION="$5" |
|
1395 | ACTION="$5" | |
1390 |
|
1396 | |||
1391 | if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi |
|
1397 | %(rules)s | |
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 |
|
|||
1394 |
|
1398 | |||
1395 | echo "Changing prohibited revision property" >&2 |
|
1399 | echo "Changing prohibited revision property" >&2 | |
1396 | exit 1 |
|
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 | class svn_sink(converter_sink, commandline): |
|
1417 | class svn_sink(converter_sink, commandline): | |
1401 | commit_re = re.compile(br'Committed revision (\d+).', re.M) |
|
1418 | commit_re = re.compile(br'Committed revision (\d+).', re.M) | |
1402 | uuid_re = re.compile(br'Repository UUID:\s*(\S+)', re.M) |
|
1419 | uuid_re = re.compile(br'Repository UUID:\s*(\S+)', re.M) | |
@@ -1470,9 +1487,20 b' class svn_sink(converter_sink, commandli' | |||||
1470 | self.is_exec = None |
|
1487 | self.is_exec = None | |
1471 |
|
1488 | |||
1472 | if created: |
|
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 | hook = os.path.join(created, b'hooks', b'pre-revprop-change') |
|
1501 | hook = os.path.join(created, b'hooks', b'pre-revprop-change') | |
1474 | fp = open(hook, b'wb') |
|
1502 | fp = open(hook, b'wb') | |
1475 | fp.write(pre_revprop_change) |
|
1503 | fp.write(gen_pre_revprop_change_hook(prop_actions_allowed)) | |
1476 | fp.close() |
|
1504 | fp.close() | |
1477 | util.setflags(hook, False, True) |
|
1505 | util.setflags(hook, False, True) | |
1478 |
|
1506 | |||
@@ -1667,6 +1695,23 b' class svn_sink(converter_sink, commandli' | |||||
1667 | revprop=True, |
|
1695 | revprop=True, | |
1668 | revision=rev, |
|
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 | for parent in parents: |
|
1715 | for parent in parents: | |
1671 | self.addchild(parent, rev) |
|
1716 | self.addchild(parent, rev) | |
1672 | return self.revid(rev) |
|
1717 | return self.revid(rev) |
@@ -570,6 +570,11 b' coreconfigitem(' | |||||
570 | default=0, |
|
570 | default=0, | |
571 | ) |
|
571 | ) | |
572 | coreconfigitem( |
|
572 | coreconfigitem( | |
|
573 | b'convert', | |||
|
574 | b'svn.dangerous-set-commit-dates', | |||
|
575 | default=False, | |||
|
576 | ) | |||
|
577 | coreconfigitem( | |||
573 | b'debug', |
|
578 | b'debug', | |
574 | b'dirstate.delaywrite', |
|
579 | b'dirstate.delaywrite', | |
575 | default=0, |
|
580 | default=0, |
@@ -15,6 +15,7 b' def parseentry(entry):' | |||||
15 | e['revision'] = entry.getAttribute('revision') |
|
15 | e['revision'] = entry.getAttribute('revision') | |
16 | e['author'] = xmltext(entry.getElementsByTagName('author')[0]) |
|
16 | e['author'] = xmltext(entry.getElementsByTagName('author')[0]) | |
17 | e['msg'] = xmltext(entry.getElementsByTagName('msg')[0]) |
|
17 | e['msg'] = xmltext(entry.getElementsByTagName('msg')[0]) | |
|
18 | e['date'] = xmltext(entry.getElementsByTagName('date')[0]) | |||
18 | e['paths'] = [] |
|
19 | e['paths'] = [] | |
19 | paths = entry.getElementsByTagName('paths') |
|
20 | paths = entry.getElementsByTagName('paths') | |
20 | if paths: |
|
21 | if paths: | |
@@ -42,7 +43,7 b' def printentries(entries):' | |||||
42 | except AttributeError: |
|
43 | except AttributeError: | |
43 | fp = sys.stdout |
|
44 | fp = sys.stdout | |
44 | for e in entries: |
|
45 | for e in entries: | |
45 | for k in ('revision', 'author', 'msg'): |
|
46 | for k in ('revision', 'author', 'date', 'msg'): | |
46 | fp.write(('%s: %s\n' % (k, e[k])).encode('utf-8')) |
|
47 | fp.write(('%s: %s\n' % (k, e[k])).encode('utf-8')) | |
47 | for path, action, fpath, frev in sorted(e['paths']): |
|
48 | for path, action, fpath, frev in sorted(e['paths']): | |
48 | frominfo = b'' |
|
49 | frominfo = b'' |
@@ -54,10 +54,12 b' Modify' | |||||
54 | 2 2 test a |
|
54 | 2 2 test a | |
55 | revision: 2 |
|
55 | revision: 2 | |
56 | author: test |
|
56 | author: test | |
|
57 | date: * (glob) | |||
57 | msg: modify a file |
|
58 | msg: modify a file | |
58 | M /a |
|
59 | M /a | |
59 | revision: 1 |
|
60 | revision: 1 | |
60 | author: test |
|
61 | author: test | |
|
62 | date: * (glob) | |||
61 | msg: add a file |
|
63 | msg: add a file | |
62 | A /a |
|
64 | A /a | |
63 | A /d1 |
|
65 | A /d1 | |
@@ -95,6 +97,7 b' Rename' | |||||
95 | 3 3 test b |
|
97 | 3 3 test b | |
96 | revision: 3 |
|
98 | revision: 3 | |
97 | author: test |
|
99 | author: test | |
|
100 | date: * (glob) | |||
98 | msg: rename a file |
|
101 | msg: rename a file | |
99 | D /a |
|
102 | D /a | |
100 | A /b (from /a@2) |
|
103 | A /b (from /a@2) | |
@@ -131,6 +134,7 b' Copy' | |||||
131 | 4 4 test c |
|
134 | 4 4 test c | |
132 | revision: 4 |
|
135 | revision: 4 | |
133 | author: test |
|
136 | author: test | |
|
137 | date: * (glob) | |||
134 | msg: copy a file |
|
138 | msg: copy a file | |
135 | A /c (from /b@3) |
|
139 | A /c (from /b@3) | |
136 | $ ls a a-hg-wc |
|
140 | $ ls a a-hg-wc | |
@@ -167,6 +171,7 b' Remove' | |||||
167 | 5 5 test . |
|
171 | 5 5 test . | |
168 | revision: 5 |
|
172 | revision: 5 | |
169 | author: test |
|
173 | author: test | |
|
174 | date: * (glob) | |||
170 | msg: remove a file |
|
175 | msg: remove a file | |
171 | D /b |
|
176 | D /b | |
172 | $ ls a a-hg-wc |
|
177 | $ ls a a-hg-wc | |
@@ -209,6 +214,7 b' Executable' | |||||
209 | 6 6 test c |
|
214 | 6 6 test c | |
210 | revision: 6 |
|
215 | revision: 6 | |
211 | author: test |
|
216 | author: test | |
|
217 | date: * (glob) | |||
212 | msg: make a file executable |
|
218 | msg: make a file executable | |
213 | M /c |
|
219 | M /c | |
214 | #if execbit |
|
220 | #if execbit | |
@@ -247,6 +253,7 b' Symlinks' | |||||
247 | 8 8 test newlink |
|
253 | 8 8 test newlink | |
248 | revision: 8 |
|
254 | revision: 8 | |
249 | author: test |
|
255 | author: test | |
|
256 | date: * (glob) | |||
250 | msg: move symlink |
|
257 | msg: move symlink | |
251 | D /link |
|
258 | D /link | |
252 | A /newlink (from /link@7) |
|
259 | A /newlink (from /link@7) | |
@@ -278,6 +285,7 b' Convert with --full adds and removes fil' | |||||
278 | 7 7 test f |
|
285 | 7 7 test f | |
279 | revision: 7 |
|
286 | revision: 7 | |
280 | author: test |
|
287 | author: test | |
|
288 | date: * (glob) | |||
281 | msg: f |
|
289 | msg: f | |
282 | D /c |
|
290 | D /c | |
283 | A /d |
|
291 | A /d | |
@@ -315,6 +323,7 b' Executable in new directory' | |||||
315 | 1 1 test d1/a |
|
323 | 1 1 test d1/a | |
316 | revision: 1 |
|
324 | revision: 1 | |
317 | author: test |
|
325 | author: test | |
|
326 | date: * (glob) | |||
318 | msg: add executable file in new directory |
|
327 | msg: add executable file in new directory | |
319 | A /d1 |
|
328 | A /d1 | |
320 | A /d1/a |
|
329 | A /d1/a | |
@@ -343,6 +352,7 b' Copy to new directory' | |||||
343 | 2 2 test d2/a |
|
352 | 2 2 test d2/a | |
344 | revision: 2 |
|
353 | revision: 2 | |
345 | author: test |
|
354 | author: test | |
|
355 | date: * (glob) | |||
346 | msg: copy file to new directory |
|
356 | msg: copy file to new directory | |
347 | A /d2 |
|
357 | A /d2 | |
348 | A /d2/a (from /d1/a@1) |
|
358 | A /d2/a (from /d1/a@1) | |
@@ -416,21 +426,25 b' Expect 4 changes' | |||||
416 | 4 4 test right-2 |
|
426 | 4 4 test right-2 | |
417 | revision: 4 |
|
427 | revision: 4 | |
418 | author: test |
|
428 | author: test | |
|
429 | date: * (glob) | |||
419 | msg: merge |
|
430 | msg: merge | |
420 | A /right-1 |
|
431 | A /right-1 | |
421 | A /right-2 |
|
432 | A /right-2 | |
422 | revision: 3 |
|
433 | revision: 3 | |
423 | author: test |
|
434 | author: test | |
|
435 | date: * (glob) | |||
424 | msg: left-2 |
|
436 | msg: left-2 | |
425 | M /b |
|
437 | M /b | |
426 | A /left-2 |
|
438 | A /left-2 | |
427 | revision: 2 |
|
439 | revision: 2 | |
428 | author: test |
|
440 | author: test | |
|
441 | date: * (glob) | |||
429 | msg: left-1 |
|
442 | msg: left-1 | |
430 | M /b |
|
443 | M /b | |
431 | A /left-1 |
|
444 | A /left-1 | |
432 | revision: 1 |
|
445 | revision: 1 | |
433 | author: test |
|
446 | author: test | |
|
447 | date: * (glob) | |||
434 | msg: base |
|
448 | msg: base | |
435 | A /b |
|
449 | A /b | |
436 |
|
450 | |||
@@ -459,10 +473,12 b' Tags are not supported, but must not bre' | |||||
459 | 2 2 test .hgtags |
|
473 | 2 2 test .hgtags | |
460 | revision: 2 |
|
474 | revision: 2 | |
461 | author: test |
|
475 | author: test | |
|
476 | date: * (glob) | |||
462 | msg: Tagged as v1.0 |
|
477 | msg: Tagged as v1.0 | |
463 | A /.hgtags |
|
478 | A /.hgtags | |
464 | revision: 1 |
|
479 | revision: 1 | |
465 | author: test |
|
480 | author: test | |
|
481 | date: * (glob) | |||
466 | msg: Add file a |
|
482 | msg: Add file a | |
467 | A /a |
|
483 | A /a | |
468 | $ rm -rf a a-hg a-hg-wc |
|
484 | $ rm -rf a a-hg a-hg-wc | |
@@ -494,10 +510,12 b' Executable bit removal' | |||||
494 | 2 2 test exec |
|
510 | 2 2 test exec | |
495 | revision: 2 |
|
511 | revision: 2 | |
496 | author: test |
|
512 | author: test | |
|
513 | date: * (glob) | |||
497 | msg: remove executable bit |
|
514 | msg: remove executable bit | |
498 | M /exec |
|
515 | M /exec | |
499 | revision: 1 |
|
516 | revision: 1 | |
500 | author: test |
|
517 | author: test | |
|
518 | date: * (glob) | |||
501 | msg: create executable |
|
519 | msg: create executable | |
502 | A /exec |
|
520 | A /exec | |
503 | $ test ! -x a-hg-wc/exec |
|
521 | $ test ! -x a-hg-wc/exec | |
@@ -540,11 +558,77 b' Skipping empty commits' | |||||
540 | 2 2 test b |
|
558 | 2 2 test b | |
541 | revision: 2 |
|
559 | revision: 2 | |
542 | author: test |
|
560 | author: test | |
|
561 | date: * (glob) | |||
543 | msg: Another change |
|
562 | msg: Another change | |
544 | A /b |
|
563 | A /b | |
545 | revision: 1 |
|
564 | revision: 1 | |
546 | author: test |
|
565 | author: test | |
|
566 | date: * (glob) | |||
547 | msg: Some change |
|
567 | msg: Some change | |
548 | A /a |
|
568 | A /a | |
549 |
|
569 | |||
550 | $ rm -rf a a-hg a-hg-wc |
|
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 | does not convert tags from the source repo to the target |
|
388 | does not convert tags from the source repo to the target | |
389 | repo. The default is False. |
|
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 | options ([+] can be repeated): |
|
408 | options ([+] can be repeated): | |
392 |
|
409 | |||
393 | -s --source-type TYPE source repository type |
|
410 | -s --source-type TYPE source repository type |
General Comments 0
You need to be logged in to leave comments.
Login now