changeset 364:9ff000a03a71

Refactor: Major internal overhaul: 1. Make all non-public classes have private names -- starting with a "_" 2. Sort all functions and classes by external visibility: public stuff comes first then the private implementation stuff.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 18 Feb 2019 01:02:30 +0100
parents cdd94f12d93d
children 21a4af796c3e
files extensions/timestamps.py
diffstat 1 files changed, 331 insertions(+), 332 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/timestamps.py	Mon Feb 18 00:36:18 2019 +0100
+++ b/extensions/timestamps.py	Mon Feb 18 01:02:30 2019 +0100
@@ -167,104 +167,321 @@
     if not ctx:
         raise error.Abort(_("no Mercurial working directory"))
     if opts.get("import"):
-        import_timestamps(ui,
-                          ctx,
-                          tsconfig=opts.get("tsconfig"),
-                          amend=opts.get("amend"),
-                          all=opts.get("all"))
+        _import_timestamps(ui,
+                           ctx,
+                           tsconfig=opts.get("tsconfig"),
+                           amend=opts.get("amend"),
+                           all=opts.get("all"))
     elif opts.get("save"):
-        save_timestamps(ui,
-                        ctx,
-                        tsconfig=opts.get("tsconfig"),
-                        amend=opts.get("amend"))
+        _save_timestamps(ui,
+                         ctx,
+                         tsconfig=opts.get("tsconfig"),
+                         amend=opts.get("amend"))
     elif opts.get("restore"):
-        restore_timestamps(ui, ctx, tsconfig=opts.get("tsconfig"))
+        _restore_timestamps(ui, ctx, tsconfig=opts.get("tsconfig"))
     elif opts.get("show"):
         with ui.formatter("timestamps", opts) as fm:
-            show_timestamps(ui, ctx, fm, tsconfig=opts.get("tsconfig"))
+            _show_timestamps(ui, ctx, fm, tsconfig=opts.get("tsconfig"))
     else:
         raise error.Abort(_("must give a command:"
                             "--save or --restore, --show or --import"))
 
 
-def save_timestamps(ui,
-                    ctx,
-                    tsconfig=None,
-                    amend=False):
+def uisetup(ui):
+    ui.setconfig('hooks', 'pre-update.timestamps', pre_hook)
+    ui.setconfig('hooks', 'pre-merge.timestamps', pre_hook)
+    ui.setconfig('hooks', 'pre-resolve.timestamps', pre_hook)
+    ui.setconfig('hooks', 'post-resolve.timestamps', post_hook)
+    ui.setconfig('hooks', 'pre-revert.timestamps', pre_hook)
+    ui.setconfig('hooks', 'post-revert.timestamps', post_hook)
+
+
+def pre_hook(ui, repo, hooktype, **kwds):
+    """Generic pre-xxx hook: just forwards to the corresponding repo method"""
+    repo._ts_record_pre_data(hooktype)
+
+
+def post_hook(ui, repo, hooktype, **kwds):
+    """Generic post-xxx hook: just forward to the `hooktype` related
+    implementations.
+
+    This generic dispatcher hook simplifies configuration.
+
+    Current implementations exist for :hg:`revert`.
+
+    """
+    ui.debug("POST HOOK: " + hooktype + '\n')
+    ui.debug("POST HOOK: " + repr(kwds) + '\n')
+
+    if not repo.local():
+        return
+    if hooktype == "post-revert":
+        wctx = repo[None]
+        matcher = _gen_matcher_from_tsconfig(wctx, ui=ui)
+        if matcher is None:
+            return
+        ts = _Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
+        if ts is None:
+            return
+        ui.note(_("restoring timestamps\n"))
+        opts = kwds.get("opts")
+        if opts.get("all"):
+            candidates = wctx
+        else:
+            # the same as in revert's implementation in `cmdutil.revert`
+            patterns = scmutil.match(wctx, kwds.get("pats"), opts=opts)
+            candidates = wctx.walk(patterns)
+        _do_restore_timestamps(repo, candidates, matcher, ts, ui=ui)
+    elif hooktype == "post-resolve":
+        wctx = repo[None]
+        matcher = _gen_matcher_from_tsconfig(wctx, ui=ui)
+        if matcher is None:
+            return
+        ts = _Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
+        if ts is None:
+            ui.warn(_("update.timestamps: timestamps database file could not be found or read\n"))
+            return
+        ui.note(_("restoring timestamps\n"))
+
+
+def update_hook(ui, repo, hooktype, **kwds):
+    """Hook called by :hg:`update` and :hg:`merge` if configured correctly.
+
+    """
+    if _DEV:
+        ui.debug("UPDATE: " + repr(repo) + '\n')
+        ui.debug("UPDATE: " + repr(kwds) + '\n')
+    #
+    # Note: We get only the target revisions. We cannot just compute the
+    #       difference
+    repo._ts_update(parent1=kwds.get("parent1"),
+                    parent2=kwds.get("parent2"),
+                    error=kwds.get("error"),
+                    preview=kwds.get("preview", False))
+
+
+def reposetup(ui, repo):
+
+    class TimestampedRepo(repo.__class__):
+
+        def __init__(self, *args, **kwds):
+            super(TimestampedRepo, self).__init__(*args, **kwds)
+            self.__hooktype = None
+            self.__from_parents = None
+            self.ui.debug("init of a TimestampedRepo\n")
+
+        def commit(self, text="", user=None, date=None, match=None,
+                   force=False, editor=False, extra=None, **kwds):
+            if self.local():
+                match = self._ts_commit(match)
+            else:
+                self.ui.debug("commit.timestamps: repo is not local\n")
+            return super(TimestampedRepo, self).commit(text=text,
+                                                       user=user,
+                                                       date=date,
+                                                       match=match,
+                                                       force=force,
+                                                       editor=editor,
+                                                       extra=extra,
+                                                       **kwds)
+
+        def _ts_commit(self, match):
+            wctx = self[None]
+            tsmatch = _gen_matcher_from_tsconfig(wctx)
+            if tsmatch is not None:
+                self.ui.debug("TimestampedRepo.commit():"
+                              " handling timestamps\n")
+                ts = _Timestamps.from_ctx(wctx, self.ui)
+                if ts is None:
+                    ts = _Timestamps.create(self.ui)
+
+                #
+                # collect the timestamps
+                #
+
+                self.ui.note(_("collecting timestamps\n"))
+
+                #
+                # get the status of all files w/o timestamps database and
+                # config
+                #
+                fstatus = self.status(
+                    match=_matchmod.differencematcher(
+                        match,
+                        _matchmod.exact(
+                            self.root,
+                            '',
+                            [TIMESTAMPS_DATABASE,
+                             TIMESTAMPS_CONFIGFILE])))
+                # remove delete items
+                for f in fstatus.removed:
+                    try:
+                        del ts[f]
+                    except KeyError:
+                        pass
+                # put each file's mtime into the database
+                with _FloatTimesInStat():
+                    for f in itertools.chain(fstatus.added,
+                                             fstatus.modified):
+                        if tsmatch(f):
+                            st = os.lstat(self.wjoin(f))
+                            ts[f] = _to_isoformat(st.st_mtime)
+
+                with io.open(self.wjoin(TIMESTAMPS_DATABASE), "wb") as fp:
+                    ts.write(fp)
+
+                # ensure the database file is really tracked
+                dbstatus = self.status(
+                    match=_matchmod.exact(self.root, '', [TIMESTAMPS_DATABASE]),
+                    clean=True)
+                if dbstatus.modified or dbstatus.added or dbstatus.clean:
+                    # already tracked
+                    pass
+                else:
+                    wctx.add([TIMESTAMPS_DATABASE])
+                #
+                # Add the timestamps database and the configuration to
+                # the commit match always, If the files are not changed
+                # it does not harm.
+                #
+                # match: "hg ci" -> alwaysmatcher
+                #        "hg ci file1 ... -> patternmatcher with the files
+                #
+                if match.always():
+                    # already included
+                    pass
+                else:
+                    auto_included = []
+                    for f in (TIMESTAMPS_DATABASE, TIMESTAMPS_CONFIGFILE):
+                        fstatus = self.status(
+                            match=_matchmod.exact(self.root, '', [f]))
+                        if fstatus.added or fstatus.modified:
+                            auto_included.append(f)
+                    if auto_included:
+                        #
+                        # also handles the very rare cases if match.isexact()
+                        # holds
+                        #
+                        match = _matchmod.unionmatcher(
+                            [match,
+                             _matchmod.exact(self.root,
+                                             '',
+                                             auto_included)])
+                self.ui.note(_("timestamps have been collected\n"))
+            else:
+                self.ui.debug("TimestampedRepo.commit():"
+                              " timestamps not activated/configured\n")
+            return match
+
+        def _ts_record_pre_data(self, hooktype):
+            if not self.local():
+                return
+            self.__hooktype = hooktype
+            self.__from_parents = [p.hex() for p in self[None].parents()]
+
+        def _ts_update(self, parent1=None, parent2=None, error=0, preview=False):
+            """Called on :hg:`hg update` and :hg:`hg merge`
+
+            """
+            if not self.local():
+                self.ui.debug("update.timestamps: repo is not local\n")
+                return
+            if not self.__hooktype \
+                   or self.__hooktype not in ("pre-update",
+                                              "pre-merge"):
+                self.ui.warn("timestamps: no recorded update hook data for use in `_ts_update()'\n")
+                return
+            if preview:
+                self.ui.note(_("skipping updating timestamps in preview mode\n"))
+                return
+            self.ui.note(_("restoring timestamps\n"))
+            wctx = self[None]
+            tsmatch = _gen_matcher_from_tsconfig(wctx)
+            if tsmatch is not None:
+                ts = _Timestamps.from_ctx(wctx,
+                                          self.ui, name=TIMESTAMPS_DATABASE)
+                if ts is None:
+                    self.ui.warn(_("update.timestamps: timestamps database file could not be found or read\n"))
+                else:
+                    if not parent2:
+                        self._ts_update_update(tsmatch, ts)
+                    else:
+                        self._ts_update_update(tsmatch, ts,
+                                               allow_other_region=True)
+            else:
+                self.ui.debug("update.timestamps: timestamps not activated/configured\n")
+
+        def _ts_update_update(self, tsmatch, ts, allow_other_region=False):
+            """:hg:`update`, :hg:`merge --abort`"""
+            to_update = set()
+            for p in self.__from_parents:
+                status = self.status(p)
+                if _DEV:
+                    self.ui.debug("FROM PARENT: " + p + " Status: "
+                                  + repr(status) + '\n')
+                to_update.update(set(status.added))
+                to_update.update(set(status.modified))
+            if _DEV:
+                self.ui.debug("UPDATE TO_UPDATE: " + repr(to_update)
+                              + '\n')
+            ms = _mergemod.mergestate.read(self)
+            _debug_mergestate(self.ui, ms)
+            if ms.active():
+                to_update.difference_update(set(ms.files()))
+            if _DEV:
+                self.ui.debug("UPDATE TO_UPDATE w/o resolved: "
+                              + repr(to_update) + '\n')
+            _do_restore_timestamps(self, to_update, tsmatch, ts,
+                                   allow_other_region=allow_other_region)
+
+    repo.__class__ = TimestampedRepo
+
+    ui.setconfig('hooks', 'update.timestamps', update_hook)
+
+
+def _save_timestamps(ui,
+                     ctx,
+                     tsconfig=None,
+                     amend=False):
     repo = ctx.repo()
     if not repo.local():
         raise error.Abort(_("repository is not local"))
-    matcher = gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui)
+    matcher = _gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui)
     if matcher is None:
         raise error.Abort(_("timestamps are not activated/configured"))
     if amend:
-        ts = Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
+        ts = _Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
         if ts is None:
             raise error.Abort(_("timestamps database file does not exist"
                                 " -- cannot amend"))
     else:
-        ts = Timestamps.create(ui)
-    with FloatTimesInStat():
+        ts = _Timestamps.create(ui)
+    with _FloatTimesInStat():
         for fn in ctx:
             if matcher(fn):
                 st = os.lstat(repo.wjoin(fn))
-                ts[fn] = to_isoformat(st.st_mtime)
+                ts[fn] = _to_isoformat(st.st_mtime)
     with io.open(repo.wjoin(TIMESTAMPS_DATABASE), "wb") as fp:
         ts.write(fp)
 
 
-def import_timestamps(ui,
-                      ctx,
-                      tsconfig=None,
-                      amend=False,
-                      all=False):
-    repo = ctx.repo()
-    if not repo.local():
-        raise error.Abort(_("repository is not local"))
-    matcher = gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui)
-    if matcher is None:
-        raise error.Abort(_("timestamps are not activated/configured"))
-    if amend:
-        ts = Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
-        if ts is None:
-            raise error.Abort(_("timestamps database file does not exist"
-                                " -- cannot amend"))
-    else:
-        ts = Timestamps.create(ui)
-    try:
-        sourcetsfile = io.open(repo.wjoin(".hgtimestamp"), "rt",
-                               encoding="utf-8")
-    except IOError:
-        raise error.Abort(_("no .hgtimestamp database file"))
-    try:
-        for line in sourcetsfile:
-            fname, mtime = line.strip().split(',')
-            if all or matcher(fname):
-                ts[fname] = to_isoformat(float(mtime))
-    finally:
-        sourcetsfile.close()
-    with io.open(repo.wjoin(TIMESTAMPS_DATABASE), "wb") as fp:
-        ts.write(fp)
-
-
-def restore_timestamps(ui, ctx,
-                       tsconfig=None):
+def _restore_timestamps(ui, ctx, tsconfig=None):
     repo = ctx.repo()
     if not repo.local():
         raise error.Abort(_("repository is not local"))
     cmdutil.checkunfinished(repo)
     cmdutil.bailifchanged(repo)
-    matcher = gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui)
+    matcher = _gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui)
     if matcher is None:
         raise error.Abort(_("timestamps are not activated/configured"))
-    ts = Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
+    ts = _Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
     if ts is None:
         raise error.Abort(_("timestamps database file does not exist"))
-    _restore_timestamps(repo, ctx, matcher, ts, ui=ui)
+    _do_restore_timestamps(repo, ctx, matcher, ts, ui=ui)
 
 
-def show_timestamps(ui, ctx, formatter,
-                    tsconfig=None):
+def _show_timestamps(ui, ctx, formatter,
+                     tsconfig=None):
 
     def _tsv(v):
         return "<CONFLICT>" if v is _conflictobj else v
@@ -272,9 +489,9 @@
     #
     # NOTE: no need for a local repo here
     #
-    matcher = gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui) \
+    matcher = _gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui) \
         or _matchmod.never(ctx.repo().root, '')
-    ts = Timestamps.from_ctx(ctx, ui, name=TIMESTAMPS_DATABASE)
+    ts = _Timestamps.from_ctx(ctx, ui, name=TIMESTAMPS_DATABASE)
     if ts is None:
         raise error.Abort(_("timestamps database file does not exist"))
     if ui.debugflag or ui.verbose:
@@ -312,7 +529,41 @@
             f)
 
 
-def gen_matcher_from_tsconfig(ctx, tsconfig=None, ui=None):
+def _import_timestamps(ui,
+                       ctx,
+                       tsconfig=None,
+                       amend=False,
+                       all=False):
+    repo = ctx.repo()
+    if not repo.local():
+        raise error.Abort(_("repository is not local"))
+    matcher = _gen_matcher_from_tsconfig(ctx, tsconfig=tsconfig, ui=ui)
+    if matcher is None:
+        raise error.Abort(_("timestamps are not activated/configured"))
+    if amend:
+        ts = _Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
+        if ts is None:
+            raise error.Abort(_("timestamps database file does not exist"
+                                " -- cannot amend"))
+    else:
+        ts = _Timestamps.create(ui)
+    try:
+        sourcetsfile = io.open(repo.wjoin(".hgtimestamp"), "rt",
+                               encoding="utf-8")
+    except IOError:
+        raise error.Abort(_("no .hgtimestamp database file"))
+    try:
+        for line in sourcetsfile:
+            fname, mtime = line.strip().split(',')
+            if all or matcher(fname):
+                ts[fname] = _to_isoformat(float(mtime))
+    finally:
+        sourcetsfile.close()
+    with io.open(repo.wjoin(TIMESTAMPS_DATABASE), "wb") as fp:
+        ts.write(fp)
+
+
+def _gen_matcher_from_tsconfig(ctx, tsconfig=None, ui=None):
     """Read the configuration file and return a Mercurial matcher which is
     configured.
 
@@ -386,7 +637,7 @@
     return matcher
 
 
-def escape_str_field(fn):
+def _escape_str_field(fn):
     """Escape the string `fn` for database output if needed
 
     :return: an escaped version or the original one
@@ -401,7 +652,7 @@
     return fn
 
 
-def db_reader(fp):
+def _db_reader(fp):
     """A generator that parses the file `fp` yielding records consisting of fields
 
     :param fp: the binary file object to be read
@@ -544,7 +795,7 @@
         yield (lineno, mergeblock_type, record)
 
 
-def db_writer(fp):
+def _db_writer(fp):
     """A coroutine to write records into the database `fp`.
 
     :param fp: the binary file object to write into
@@ -560,7 +811,7 @@
                         fp.write(',')
                     else:
                         print_sep = True
-                    fp.write(escape_str_field(field))
+                    fp.write(_escape_str_field(field))
                 fp.write('\n')
             else:
                 # no escaping for comments and other special lines
@@ -571,7 +822,7 @@
         fp.flush()
 
 
-def to_isoformat(t):
+def _to_isoformat(t):
     """Return the POSIX timestamp `t` formatted in full ISO format.
 
     `t` is expected to be in the UTC timezone.
@@ -584,7 +835,7 @@
         return dt.isoformat()
 
 
-def dt_from_isoformat(v):
+def _dt_from_isoformat(v):
     """Parse an full ISO timestamp string into a :class:`datetime.datetime`.
 
     """
@@ -608,7 +859,7 @@
         raise ValueError("invalid timestamp format in line %d" % lineno)
 
 
-class FloatTimesInStat(object):
+class _FloatTimesInStat(object):
     """Context manager to ensure that stat returns float values.
 
     For 3.7 <= Mercurial < 4.6: Mercurial calls :func:`os.stat_float_times`
@@ -634,7 +885,7 @@
             os.stat_float_times(False)
 
 
-class Timestamps(object):
+class _Timestamps(object):
     """The in-memory timestamps database implementation.
 
     The implementation tries to retain most of comment and empty lines
@@ -711,7 +962,7 @@
         """
         self._init()
         datano = 0
-        for lineno, mergeblock_type, record in db_reader(fp):
+        for lineno, mergeblock_type, record in _db_reader(fp):
             if mergeblock_type:
                 if mergeblock_type == '<':
                     mdata = self._local
@@ -792,7 +1043,7 @@
         assert self._d is not None
         assert self._version == 1
 
-        dbwriter = db_writer(fp)
+        dbwriter = _db_writer(fp)
         dbwriter.send(None)  # prime the coroutine
 
         #
@@ -889,262 +1140,10 @@
                 yield i
 
 
-def pre_hook(ui, repo, hooktype, **kwds):
-    """Generic pre-xxx hook: just forwards to the corresponding repo method"""
-    repo._ts_record_pre_data(hooktype)
-
-
-def post_hook(ui, repo, hooktype, **kwds):
-    """Generic post-xxx hook: just forward to the `hooktype` related
-    implementations.
-
-    This generic dispatcher hook simplifies configuration.
-
-    Current implementations exist for :hg:`revert`.
-
-    """
-    ui.debug("POST HOOK: " + hooktype + '\n')
-    ui.debug("POST HOOK: " + repr(kwds) + '\n')
-
-    if not repo.local():
-        return
-    if hooktype == "post-revert":
-        wctx = repo[None]
-        matcher = gen_matcher_from_tsconfig(wctx, ui=ui)
-        if matcher is None:
-            return
-        ts = Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
-        if ts is None:
-            return
-        ui.note(_("restoring timestamps\n"))
-        opts = kwds.get("opts")
-        if opts.get("all"):
-            candidates = wctx
-        else:
-            # the same as in revert's implementation in `cmdutil.revert`
-            patterns = scmutil.match(wctx, kwds.get("pats"), opts=opts)
-            candidates = wctx.walk(patterns)
-        _restore_timestamps(repo, candidates, matcher, ts, ui=ui)
-    elif hooktype == "post-resolve":
-        wctx = repo[None]
-        matcher = gen_matcher_from_tsconfig(wctx, ui=ui)
-        if matcher is None:
-            return
-        ts = Timestamps.from_filename(ui, name=repo.wjoin(TIMESTAMPS_DATABASE))
-        if ts is None:
-            ui.warn(_("update.timestamps: timestamps database file could not be found or read\n"))
-            return
-        ui.note(_("restoring timestamps\n"))
-
-
-def update_hook(ui, repo, hooktype, **kwds):
-    """Hook called by :hg:`update` and :hg:`merge`
-
-    """
-    if _DEV:
-        ui.debug("UPDATE: " + repr(repo) + '\n')
-        ui.debug("UPDATE: " + repr(kwds) + '\n')
-    #
-    # Note: We get only the target revisions. We cannot just compute the
-    #       difference
-    repo._ts_update(parent1=kwds.get("parent1"),
-                    parent2=kwds.get("parent2"),
-                    error=kwds.get("error"),
-                    preview=kwds.get("preview", False))
-
-
-def reposetup(ui, repo):
-
-    class TimestampedRepo(repo.__class__):
-
-        def __init__(self, *args, **kwds):
-            super(TimestampedRepo, self).__init__(*args, **kwds)
-            self.__hooktype = None
-            self.__from_parents = None
-            self.ui.debug("init of a TimestampedRepo\n")
-
-        def commit(self, text="", user=None, date=None, match=None,
-                   force=False, editor=False, extra=None, **kwds):
-            if self.local():
-                match = self._ts_commit(match)
-            else:
-                self.ui.debug("commit.timestamps: repo is not local\n")
-            return super(TimestampedRepo, self).commit(text=text,
-                                                       user=user,
-                                                       date=date,
-                                                       match=match,
-                                                       force=force,
-                                                       editor=editor,
-                                                       extra=extra,
-                                                       **kwds)
-
-        def _ts_commit(self, match):
-            wctx = self[None]
-            tsmatch = gen_matcher_from_tsconfig(wctx)
-            if tsmatch is not None:
-                self.ui.debug("TimestampedRepo.commit():"
-                              " handling timestamps\n")
-                ts = Timestamps.from_ctx(wctx, self.ui)
-                if ts is None:
-                    ts = Timestamps.create(self.ui)
-
-                #
-                # collect the timestamps
-                #
-
-                self.ui.note(_("collecting timestamps\n"))
-
-                #
-                # get the status of all files w/o timestamps database and
-                # config
-                #
-                fstatus = self.status(
-                    match=_matchmod.differencematcher(
-                        match,
-                        _matchmod.exact(
-                            self.root,
-                            '',
-                            [TIMESTAMPS_DATABASE,
-                             TIMESTAMPS_CONFIGFILE])))
-                # remove delete items
-                for f in fstatus.removed:
-                    try:
-                        del ts[f]
-                    except KeyError:
-                        pass
-                # put each file's mtime into the database
-                with FloatTimesInStat():
-                    for f in itertools.chain(fstatus.added,
-                                             fstatus.modified):
-                        if tsmatch(f):
-                            st = os.lstat(self.wjoin(f))
-                            ts[f] = to_isoformat(st.st_mtime)
-
-                with io.open(self.wjoin(TIMESTAMPS_DATABASE), "wb") as fp:
-                    ts.write(fp)
-
-                # ensure the database file is really tracked
-                dbstatus = self.status(
-                    match=_matchmod.exact(self.root, '', [TIMESTAMPS_DATABASE]),
-                    clean=True)
-                if dbstatus.modified or dbstatus.added or dbstatus.clean:
-                    # already tracked
-                    pass
-                else:
-                    wctx.add([TIMESTAMPS_DATABASE])
-                #
-                # Add the timestamps database and the configuration to
-                # the commit match always, If the files are not changed
-                # it does not harm.
-                #
-                # match: "hg ci" -> alwaysmatcher
-                #        "hg ci file1 ... -> patternmatcher with the files
-                #
-                if match.always():
-                    # already included
-                    pass
-                else:
-                    auto_included = []
-                    for f in (TIMESTAMPS_DATABASE, TIMESTAMPS_CONFIGFILE):
-                        fstatus = self.status(
-                            match=_matchmod.exact(self.root, '', [f]))
-                        if fstatus.added or fstatus.modified:
-                            auto_included.append(f)
-                    if auto_included:
-                        #
-                        # also handles the very rare cases if match.isexact()
-                        # holds
-                        #
-                        match = _matchmod.unionmatcher(
-                            [match,
-                             _matchmod.exact(self.root,
-                                             '',
-                                             auto_included)])
-                self.ui.note(_("timestamps have been collected\n"))
-            else:
-                self.ui.debug("TimestampedRepo.commit():"
-                              " timestamps not activated/configured\n")
-            return match
-
-        def _ts_record_pre_data(self, hooktype):
-            if not self.local():
-                return
-            self.__hooktype = hooktype
-            self.__from_parents = [p.hex() for p in self[None].parents()]
-
-        def _ts_update(self, parent1=None, parent2=None, error=0, preview=False):
-            """Called on :hg:`hg update` and :hg:`hg merge`
-
-            """
-            if not self.local():
-                self.ui.debug("update.timestamps: repo is not local\n")
-                return
-            if not self.__hooktype \
-                   or self.__hooktype not in ("pre-update",
-                                              "pre-merge"):
-                self.ui.warn("timestamps: no recorded update hook data for use in `_ts_update()'\n")
-                return
-            if preview:
-                self.ui.note(_("skipping updating timestamps in preview mode\n"))
-                return
-            self.ui.note(_("restoring timestamps\n"))
-            wctx = self[None]
-            tsmatch = gen_matcher_from_tsconfig(wctx)
-            if tsmatch is not None:
-                ts = Timestamps.from_ctx(wctx,
-                                         self.ui, name=TIMESTAMPS_DATABASE)
-                if ts is None:
-                    self.ui.warn(_("update.timestamps: timestamps database file could not be found or read\n"))
-                else:
-                    if not parent2:
-                        self._ts_update_update(tsmatch, ts)
-                    else:
-                        self._ts_update_update(tsmatch, ts,
-                                               allow_other_region=True)
-            else:
-                self.ui.debug("update.timestamps: timestamps not activated/configured\n")
-
-        def _ts_update_update(self, tsmatch, ts, allow_other_region=False):
-            """:hg:`update`, :hg:`merge --abort`"""
-            to_update = set()
-            for p in self.__from_parents:
-                status = self.status(p)
-                if _DEV:
-                    self.ui.debug("FROM PARENT: " + p + " Status: "
-                                  + repr(status) + '\n')
-                to_update.update(set(status.added))
-                to_update.update(set(status.modified))
-            if _DEV:
-                self.ui.debug("UPDATE TO_UPDATE: " + repr(to_update)
-                              + '\n')
-            ms = _mergemod.mergestate.read(self)
-            _debug_mergestate(self.ui, ms)
-            if ms.active():
-                to_update.difference_update(set(ms.files()))
-            if _DEV:
-                self.ui.debug("UPDATE TO_UPDATE w/o resolved: "
-                              + repr(to_update) + '\n')
-            _restore_timestamps(self, to_update, tsmatch, ts,
-                                allow_other_region=allow_other_region)
-
-    repo.__class__ = TimestampedRepo
-
-    ui.setconfig('hooks', 'update.timestamps', update_hook)
-
-
-def uisetup(ui):
-    ui.setconfig('hooks', 'pre-update.timestamps', pre_hook)
-    ui.setconfig('hooks', 'pre-merge.timestamps', pre_hook)
-    ui.setconfig('hooks', 'pre-resolve.timestamps', pre_hook)
-    ui.setconfig('hooks', 'post-resolve.timestamps', post_hook)
-    ui.setconfig('hooks', 'pre-revert.timestamps', pre_hook)
-    ui.setconfig('hooks', 'post-revert.timestamps', post_hook)
-
-
-def _restore_timestamps(repo, candidates, tsmatch, tsdb,
-                        ui=None,
-                        alt=None,
-                        allow_other_region=False):
+def _do_restore_timestamps(repo, candidates, tsmatch, tsdb,
+                           ui=None,
+                           alt=None,
+                           allow_other_region=False):
     """Restore file timestamps of files in iterable `candidates` that
     match `tsmatch` to the times in database `tsdb`.
     If `alt` is given and one of "base", "local", "other" try to find a
@@ -1156,7 +1155,7 @@
     if not repo.local():
         raise error.Abort(_("repository is not local"))
     ui = ui or repo.ui
-    with FloatTimesInStat():
+    with _FloatTimesInStat():
         for f in candidates:
             if tsmatch(f):
                 if allow_other_region:
@@ -1164,7 +1163,7 @@
                 else:
                     mtime = tsdb.getalt(f, alt)
                 if mtime is not None:
-                    mt = calendar.timegm(dt_from_isoformat(mtime).timetuple())
+                    mt = calendar.timegm(_dt_from_isoformat(mtime).timetuple())
                     if _DEV:
                         ui.debug("RESTORING TIMESTAMP: " + f + ' -> '
                                  + mtime + '\n')