Mercurial > hg-stable
changeset 46472:7525e77b5eac
convert: option to set date and time for svn commits
Converting to subversion repository is not preserving original commit dates as
it may break some subversion functionality if commit dates are not monotonically
increasing.
This patch adds `convert.svn.dangerous-set-commit-dates` configuration option
to change this behaviour and enable commit dates convertion for those who want
to take risks.
Subversion always uses commit dates with UTC timezone, so only timestamps
are used.
Test `test-convert-svn-sink.t` uses `svnxml.py` script to dump history of svn
repositories. Atm the script is not printing `date` field from svn log. This
patch changes this to allow checks on correctness of date and time convertion.
Documentation is updated. Additional test case is added to test commit dates
convertion.
Differential Revision: https://phab.mercurial-scm.org/D9721
author | Nikita Slyusarev <nslus@yandex-team.com> |
---|---|
date | Tue, 12 Jan 2021 00:11:16 +0300 |
parents | 1d6d1a15a963 |
children | 2aef69e8efbb |
files | hgext/convert/__init__.py hgext/convert/subversion.py mercurial/configitems.py tests/svnxml.py tests/test-convert-svn-sink.t tests/test-convert.t |
diffstat | 6 files changed, 180 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/hgext/convert/__init__.py Fri Jan 15 23:58:41 2021 +0100 +++ b/hgext/convert/__init__.py Tue Jan 12 00:11:16 2021 +0300 @@ -491,6 +491,22 @@ :convert.skiptags: does not convert tags from the source repo to the target repo. The default is False. + + Subversion Destination + ###################### + + Original commit dates are not preserved by default. + + :convert.svn.dangerous-set-commit-dates: preserve original commit dates, + forcefully setting ``svn:date`` revision properties. This option is + DANGEROUS and may break some subversion functionality for the resulting + repository (e.g. filtering revisions with date ranges in ``svn log``), + as original commit dates are not guaranteed to be monotonically + increasing. + + For commit dates setting to work destination repository must have + ``pre-revprop-change`` hook configured to allow setting of ``svn:date`` + revision properties. See Subversion documentation for more details. """ return convcmd.convert(ui, src, dest, revmapfile, **opts)
--- a/hgext/convert/subversion.py Fri Jan 15 23:58:41 2021 +0100 +++ b/hgext/convert/subversion.py Tue Jan 12 00:11:16 2021 +0300 @@ -97,6 +97,17 @@ return s.decode(fsencoding).encode('utf-8') +def formatsvndate(date): + return dateutil.datestr(date, b'%Y-%m-%dT%H:%M:%S.000000Z') + + +def parsesvndate(s): + # Example SVN datetime. Includes microseconds. + # ISO-8601 conformant + # '2007-01-04T17:35:00.902377Z' + return dateutil.parsedate(s[:19] + b' UTC', [b'%Y-%m-%dT%H:%M:%S']) + + class SvnPathNotFound(Exception): pass @@ -1158,12 +1169,7 @@ continue paths.append((path, ent)) - # Example SVN datetime. Includes microseconds. - # ISO-8601 conformant - # '2007-01-04T17:35:00.902377Z' - date = dateutil.parsedate( - date[:19] + b" UTC", [b"%Y-%m-%dT%H:%M:%S"] - ) + date = parsesvndate(date) if self.ui.configbool(b'convert', b'localtimezone'): date = makedatetimestamp(date[0]) @@ -1380,7 +1386,7 @@ return logstream(stdout) -pre_revprop_change = b'''#!/bin/sh +pre_revprop_change_template = b'''#!/bin/sh REPOS="$1" REV="$2" @@ -1388,15 +1394,26 @@ PROPNAME="$4" ACTION="$5" -if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi -if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi -if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi +%(rules)s echo "Changing prohibited revision property" >&2 exit 1 ''' +def gen_pre_revprop_change_hook(prop_actions_allowed): + rules = [] + for action, propname in prop_actions_allowed: + rules.append( + ( + b'if [ "$ACTION" = "%s" -a "$PROPNAME" = "%s" ]; ' + b'then exit 0; fi' + ) + % (action, propname) + ) + return pre_revprop_change_template % {b'rules': b'\n'.join(rules)} + + class svn_sink(converter_sink, commandline): commit_re = re.compile(br'Committed revision (\d+).', re.M) uuid_re = re.compile(br'Repository UUID:\s*(\S+)', re.M) @@ -1470,9 +1487,20 @@ self.is_exec = None if created: + prop_actions_allowed = [ + (b'M', b'svn:log'), + (b'A', b'hg:convert-branch'), + (b'A', b'hg:convert-rev'), + ] + + if self.ui.configbool( + b'convert', b'svn.dangerous-set-commit-dates' + ): + prop_actions_allowed.append((b'M', b'svn:date')) + hook = os.path.join(created, b'hooks', b'pre-revprop-change') fp = open(hook, b'wb') - fp.write(pre_revprop_change) + fp.write(gen_pre_revprop_change_hook(prop_actions_allowed)) fp.close() util.setflags(hook, False, True) @@ -1667,6 +1695,23 @@ revprop=True, revision=rev, ) + + if self.ui.configbool( + b'convert', b'svn.dangerous-set-commit-dates' + ): + # Subverson always uses UTC to represent date and time + date = dateutil.parsedate(commit.date) + date = (date[0], 0) + + # The only way to set date and time for svn commit is to use propset after commit is done + self.run( + b'propset', + b'svn:date', + formatsvndate(date), + revprop=True, + revision=rev, + ) + for parent in parents: self.addchild(parent, rev) return self.revid(rev)
--- a/mercurial/configitems.py Fri Jan 15 23:58:41 2021 +0100 +++ b/mercurial/configitems.py Tue Jan 12 00:11:16 2021 +0300 @@ -570,6 +570,11 @@ default=0, ) coreconfigitem( + b'convert', + b'svn.dangerous-set-commit-dates', + default=False, +) +coreconfigitem( b'debug', b'dirstate.delaywrite', default=0,
--- a/tests/svnxml.py Fri Jan 15 23:58:41 2021 +0100 +++ b/tests/svnxml.py Tue Jan 12 00:11:16 2021 +0300 @@ -15,6 +15,7 @@ e['revision'] = entry.getAttribute('revision') e['author'] = xmltext(entry.getElementsByTagName('author')[0]) e['msg'] = xmltext(entry.getElementsByTagName('msg')[0]) + e['date'] = xmltext(entry.getElementsByTagName('date')[0]) e['paths'] = [] paths = entry.getElementsByTagName('paths') if paths: @@ -42,7 +43,7 @@ except AttributeError: fp = sys.stdout for e in entries: - for k in ('revision', 'author', 'msg'): + for k in ('revision', 'author', 'date', 'msg'): fp.write(('%s: %s\n' % (k, e[k])).encode('utf-8')) for path, action, fpath, frev in sorted(e['paths']): frominfo = b''
--- a/tests/test-convert-svn-sink.t Fri Jan 15 23:58:41 2021 +0100 +++ b/tests/test-convert-svn-sink.t Tue Jan 12 00:11:16 2021 +0300 @@ -54,10 +54,12 @@ 2 2 test a revision: 2 author: test + date: * (glob) msg: modify a file M /a revision: 1 author: test + date: * (glob) msg: add a file A /a A /d1 @@ -95,6 +97,7 @@ 3 3 test b revision: 3 author: test + date: * (glob) msg: rename a file D /a A /b (from /a@2) @@ -131,6 +134,7 @@ 4 4 test c revision: 4 author: test + date: * (glob) msg: copy a file A /c (from /b@3) $ ls a a-hg-wc @@ -167,6 +171,7 @@ 5 5 test . revision: 5 author: test + date: * (glob) msg: remove a file D /b $ ls a a-hg-wc @@ -209,6 +214,7 @@ 6 6 test c revision: 6 author: test + date: * (glob) msg: make a file executable M /c #if execbit @@ -247,6 +253,7 @@ 8 8 test newlink revision: 8 author: test + date: * (glob) msg: move symlink D /link A /newlink (from /link@7) @@ -278,6 +285,7 @@ 7 7 test f revision: 7 author: test + date: * (glob) msg: f D /c A /d @@ -315,6 +323,7 @@ 1 1 test d1/a revision: 1 author: test + date: * (glob) msg: add executable file in new directory A /d1 A /d1/a @@ -343,6 +352,7 @@ 2 2 test d2/a revision: 2 author: test + date: * (glob) msg: copy file to new directory A /d2 A /d2/a (from /d1/a@1) @@ -416,21 +426,25 @@ 4 4 test right-2 revision: 4 author: test + date: * (glob) msg: merge A /right-1 A /right-2 revision: 3 author: test + date: * (glob) msg: left-2 M /b A /left-2 revision: 2 author: test + date: * (glob) msg: left-1 M /b A /left-1 revision: 1 author: test + date: * (glob) msg: base A /b @@ -459,10 +473,12 @@ 2 2 test .hgtags revision: 2 author: test + date: * (glob) msg: Tagged as v1.0 A /.hgtags revision: 1 author: test + date: * (glob) msg: Add file a A /a $ rm -rf a a-hg a-hg-wc @@ -494,10 +510,12 @@ 2 2 test exec revision: 2 author: test + date: * (glob) msg: remove executable bit M /exec revision: 1 author: test + date: * (glob) msg: create executable A /exec $ test ! -x a-hg-wc/exec @@ -540,11 +558,77 @@ 2 2 test b revision: 2 author: test + date: * (glob) msg: Another change A /b revision: 1 author: test + date: * (glob) msg: Some change A /a $ rm -rf a a-hg a-hg-wc + +Commit dates convertion + + $ hg init a + + $ echo a >> a/a + $ hg add a + adding a/a + $ hg --cwd a ci -d '1 0' -A -m 'Change 1' + + $ echo a >> a/a + $ hg --cwd a ci -d '2 0' -m 'Change 2' + + $ echo a >> a/a + $ hg --cwd a ci -d '2 0' -m 'Change at the same time' + + $ echo a >> a/a + $ hg --cwd a ci -d '1 0' -m 'Change in the past' + + $ echo a >> a/a + $ hg --cwd a ci -d '3 0' -m 'Change in the future' + + $ hg convert --config convert.svn.dangerous-set-commit-dates=true -d svn a + assuming destination a-hg + initializing svn repository 'a-hg' + initializing svn working copy 'a-hg-wc' + scanning source... + sorting... + converting... + 4 Change 1 + 3 Change 2 + 2 Change at the same time + 1 Change in the past + 0 Change in the future + $ svnupanddisplay a-hg-wc 0 + 5 5 test . + 5 5 test a + revision: 5 + author: test + date: 1970-01-01T00:00:03.000000Z + msg: Change in the future + M /a + revision: 4 + author: test + date: 1970-01-01T00:00:01.000000Z + msg: Change in the past + M /a + revision: 3 + author: test + date: 1970-01-01T00:00:02.000000Z + msg: Change at the same time + M /a + revision: 2 + author: test + date: 1970-01-01T00:00:02.000000Z + msg: Change 2 + M /a + revision: 1 + author: test + date: 1970-01-01T00:00:01.000000Z + msg: Change 1 + A /a + + $ rm -rf a a-hg a-hg-wc
--- a/tests/test-convert.t Fri Jan 15 23:58:41 2021 +0100 +++ b/tests/test-convert.t Tue Jan 12 00:11:16 2021 +0300 @@ -388,6 +388,23 @@ does not convert tags from the source repo to the target repo. The default is False. + Subversion Destination + ###################### + + Original commit dates are not preserved by default. + + convert.svn.dangerous-set-commit-dates + preserve original commit dates, forcefully setting + "svn:date" revision properties. This option is DANGEROUS and + may break some subversion functionality for the resulting + repository (e.g. filtering revisions with date ranges in + "svn log"), as original commit dates are not guaranteed to + be monotonically increasing. + + For commit dates setting to work destination repository must have "pre- + revprop-change" hook configured to allow setting of "svn:date" revision + properties. See Subversion documentation for more details. + options ([+] can be repeated): -s --source-type TYPE source repository type