diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py --- a/hgext/convert/__init__.py +++ b/hgext/convert/__init__.py @@ -491,6 +491,17 @@ :convert.skiptags: does not convert tags from the source repo to the target repo. The default is False. + + Subversion Destination + ###################### + + Commit dates being converted to subversion destination are kept + monotonically increasing by default. + + :convert.svn.allowdecreasingdates: convert commit dates as is allowing svn + commit dates to decrease. This may break some subversion functionality + when using the resulting repository (e.g. filtering revisions by using + date ranges with ``svn log``). """ return convcmd.convert(ui, src, dest, revmapfile, **opts) diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py --- a/hgext/convert/subversion.py +++ b/hgext/convert/subversion.py @@ -97,6 +97,10 @@ return s.decode(fsencoding).encode('utf-8') +def svndate(date): + return dateutil.datestr(date, b'%Y-%m-%dT%H:%M:%S.000000Z') + + class SvnPathNotFound(Exception): pass @@ -1389,6 +1393,7 @@ ACTION="$5" if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi +if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:date" ]; 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 @@ -1479,6 +1484,10 @@ output = self.run0(b'info') self.uuid = self.uuid_re.search(output).group(1).strip() + # Last converted commit date, preserved to track decreasing of commit + # dates + self.lastdate = None + def wjoin(self, *names): return os.path.join(self.wc, *names) @@ -1636,6 +1645,11 @@ fp = os.fdopen(fd, 'wb') fp.write(util.tonativeeol(commit.desc)) fp.close() + + # Subverson always uses UTC to represent date and time + date = dateutil.parsedate(commit.date) + date = (date[0], 0) + try: output = self.run0( b'commit', @@ -1667,6 +1681,29 @@ revprop=True, revision=rev, ) + + # If svn dates decreasing is not allowed - keep them monotonically + # increasing by sacrificing some real date and time values. + if not self.ui.configbool(b'convert', b'svn.allowdecreasingdates'): + self.lastdate = max(self.lastdate, date) if self.lastdate is not None else date + if date != self.lastdate: + self.ui.warn(_(b'prevented svn commit date from decreasing ' + b'for revision %s, ' + b'used %s instead of %s\n') % + (rev, + svndate(self.lastdate), + svndate(date))) + date = self.lastdate + + # 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', + svndate(date), + revprop=True, + revision=rev, + ) + for parent in parents: self.addchild(parent, rev) return self.revid(rev) diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -570,6 +570,11 @@ default=0, ) coreconfigitem( + b'convert', + b'svn.allowdecreasingdates', + default=False, +) +coreconfigitem( b'debug', b'dirstate.delaywrite', default=0, diff --git a/tests/svnxml.py b/tests/svnxml.py --- a/tests/svnxml.py +++ b/tests/svnxml.py @@ -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'' diff --git a/tests/test-convert-hg-svn.t b/tests/test-convert-hg-svn.t --- a/tests/test-convert-hg-svn.t +++ b/tests/test-convert-hg-svn.t @@ -24,6 +24,7 @@ > ACTION="$5" > > if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi + > if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:date" ]; 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 > diff --git a/tests/test-convert-svn-sink.t b/tests/test-convert-svn-sink.t --- a/tests/test-convert-svn-sink.t +++ b/tests/test-convert-svn-sink.t @@ -54,10 +54,12 @@ 2 2 test a revision: 2 author: test + date: 1970-01-01T00:00:01.000000Z msg: modify a file M /a revision: 1 author: test + date: 1970-01-01T00:00:00.000000Z msg: add a file A /a A /d1 @@ -95,6 +97,7 @@ 3 3 test b revision: 3 author: test + date: 1970-01-01T00:00:02.000000Z msg: rename a file D /a A /b (from /a@2) @@ -131,6 +134,7 @@ 4 4 test c revision: 4 author: test + date: 1970-01-01T00:00:03.000000Z 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: 1970-01-01T00:00:04.000000Z msg: remove a file D /b $ ls a a-hg-wc @@ -209,6 +214,7 @@ 6 6 test c revision: 6 author: test + date: 1970-01-01T00:00:05.000000Z msg: make a file executable M /c #if execbit @@ -237,7 +243,9 @@ 3 remove a file 2 make a file executable 1 add symlink + prevented svn commit date from decreasing for revision 7, used 1970-01-01T00:00:05.000000Z instead of 1970-01-01T00:00:00.000000Z 0 move symlink + prevented svn commit date from decreasing for revision 8, used 1970-01-01T00:00:05.000000Z instead of 1970-01-01T00:00:00.000000Z $ svnupanddisplay a-svnlink-wc 1 8 1 test d1 8 1 test d1/d2 @@ -247,6 +255,7 @@ 8 8 test newlink revision: 8 author: test + date: 1970-01-01T00:00:05.000000Z msg: move symlink D /link A /newlink (from /link@7) @@ -278,6 +287,7 @@ 7 7 test f revision: 7 author: test + date: 1970-01-01T00:00:00.000000Z msg: f D /c A /d @@ -315,6 +325,7 @@ 1 1 test d1/a revision: 1 author: test + date: 1970-01-01T00:00:00.000000Z msg: add executable file in new directory A /d1 A /d1/a @@ -343,6 +354,7 @@ 2 2 test d2/a revision: 2 author: test + date: 1970-01-01T00:00:01.000000Z msg: copy file to new directory A /d2 A /d2/a (from /d1/a@1) @@ -416,21 +428,25 @@ 4 4 test right-2 revision: 4 author: test + date: 1970-01-01T00:00:05.000000Z msg: merge A /right-1 A /right-2 revision: 3 author: test + date: 1970-01-01T00:00:02.000000Z msg: left-2 M /b A /left-2 revision: 2 author: test + date: 1970-01-01T00:00:01.000000Z msg: left-1 M /b A /left-1 revision: 1 author: test + date: 1970-01-01T00:00:00.000000Z msg: base A /b @@ -459,10 +475,12 @@ 2 2 test .hgtags revision: 2 author: test + date: 1970-01-01T00:00:01.000000Z msg: Tagged as v1.0 A /.hgtags revision: 1 author: test + date: 1970-01-01T00:00:00.000000Z msg: Add file a A /a $ rm -rf a a-hg a-hg-wc @@ -494,10 +512,12 @@ 2 2 test exec revision: 2 author: test + date: 1970-01-01T00:00:02.000000Z msg: remove executable bit M /exec revision: 1 author: test + date: 1970-01-01T00:00:01.000000Z msg: create executable A /exec $ test ! -x a-hg-wc/exec @@ -540,11 +560,122 @@ 2 2 test b revision: 2 author: test + date: 1970-01-01T00:00:00.000000Z msg: Another change A /b revision: 1 author: test + date: 1970-01-01T00:00:00.000000Z msg: Some change A /a $ rm -rf a a-hg a-hg-wc + +Commit dates autofixing + + $ 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 -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 + prevented svn commit date from decreasing for revision 4, used 1970-01-01T00:00:02.000000Z instead of 1970-01-01T00:00:01.000000Z + 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:02.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-hg a-hg-wc + + $ hg convert --config convert.svn.allowdecreasingdates=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 diff --git a/tests/test-convert.t b/tests/test-convert.t --- a/tests/test-convert.t +++ b/tests/test-convert.t @@ -388,6 +388,18 @@ does not convert tags from the source repo to the target repo. The default is False. + Subversion Destination + ###################### + + Commit dates being converted to subversion destination are kept + monotonically increasing by default. + + convert.svn.allowdecreasingdates + convert commit dates as is allowing svn commit dates to + decrease. This may break some subversion functionality when + using the resulting repository (e.g. filtering revisions by + using date ranges with "svn log"). + options ([+] can be repeated): -s --source-type TYPE source repository type