Patchwork D9721: convert: set date and time for svn commits

login
register
mail settings
Submitter phabricator
Date Jan. 11, 2021, 10:56 p.m.
Message ID <differential-rev-PHID-DREV-35b34jxrbb2cppecumer-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/48032/
State New
Headers show

Comments

phabricator - Jan. 11, 2021, 10:56 p.m.
nslsrv created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  Committing to subversion repository is changed to use commit dates from
  the source. Subversion always uses commit dates with UTC timezone, so only
  timestamps are used.
  
  By default svn commit dates are kept monotonically increasing by replacing
  locally decreasing date and time values. This behaviour is disabled using
  `convert.svn.allowdecreasingdates` configuration option.
  
  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.
  
  Additional test cases are added to test commit dates autofixing.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D9721

AFFECTED FILES
  hgext/convert/__init__.py
  hgext/convert/subversion.py
  mercurial/configitems.py
  tests/svnxml.py
  tests/test-convert-hg-svn.t
  tests/test-convert-svn-sink.t
  tests/test-convert.t

CHANGE DETAILS




To: nslsrv, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

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
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-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/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/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/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/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)