[PATCH 3 of 3] merge: support timeout for merge-tool (issue4841)

timeless at mozdev.org timeless at mozdev.org
Fri Sep 25 01:33:27 CDT 2015


# HG changeset patch
# User timeless at mozdev.org
# Date 1443157100 14400
#      Fri Sep 25 00:58:20 2015 -0400
# Node ID c20ebc3b551a8bbf0839771f27a2523dfa917bb4
# Parent  51d7fd39756d08699fa9ec39e4387f4558df083a
merge: support timeout for merge-tool (issue4841)

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -5448,6 +5448,8 @@
         ret = 0
         didwork = False
 
+        timeout = repo.ui.configint('merge-tools', 'timeout', default=60*5)
+        stop = False
         for f in ms:
             if not m(f):
                 continue
@@ -5459,6 +5461,8 @@
             elif unmark:
                 ms.mark(f, "u")
             else:
+                if stop:
+                    continue
                 wctx = repo[None]
 
                 # backup pre-resolve (merge uses .orig for its own purposes)
@@ -5469,7 +5473,12 @@
                     # resolve file
                     ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
                                  'resolve')
-                    if ms.resolve(f, wctx):
+                    try:
+                        r = ms.resolve(f, wctx, timeout=timeout)
+                    except error.TimeoutWarning as t:
+                        r = t.result
+                        stop = True
+                    if r:
                         ret = 1
                 finally:
                     ui.setconfig('ui', 'forcemerge', '', 'resolve')
diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -8,6 +8,7 @@
 from __future__ import absolute_import
 
 import errno
+import time
 import os
 import shutil
 import struct
@@ -21,6 +22,7 @@
 )
 from . import (
     copies,
+    error,
     filemerge,
     obsolete,
     subrepo,
@@ -279,7 +281,7 @@
             if entry[0] == 'u':
                 yield f
 
-    def resolve(self, dfile, wctx, labels=None):
+    def resolve(self, dfile, wctx, labels=None, timeout=None):
         """rerun merge process for file path `dfile`"""
         if self[dfile] == 'r':
             return 0
@@ -302,14 +304,23 @@
         f = self._repo.vfs('merge/' + hash)
         self._repo.wwrite(dfile, f.read(), flags)
         f.close()
+
+        if timeout:
+            start = time.time()
         r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
                                 labels=labels)
+        if timeout:
+            elapsed = time.time() - start
         if r is None:
             # no real conflict
             del self._state[dfile]
             self._dirty = True
         elif not r:
             self.mark(dfile, 'r')
+        if timeout and elapsed > 1:
+            self._repo.ui.warn(_('warning: timeout running mergetool for %s\n') %
+                               lfile)
+            raise error.TimeoutWarning('timeout running mergetool', r, (lfile))
         return r
 
 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
@@ -831,7 +842,12 @@
         updated += 1
 
     # merge
+    stop = False
+    timeout = repo.ui.configint('merge-tools', 'timeout', default=60*5)
     for f, args, msg in actions['m']:
+        if stop:
+            unresolved += 1
+            continue
         repo.ui.debug(" %s: %s -> m\n" % (f, msg))
         z += 1
         progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -840,7 +856,12 @@
                              overwrite)
             continue
         audit(f)
-        r = ms.resolve(f, wctx, labels=labels)
+        stop = False
+        try:
+            r = ms.resolve(f, wctx, labels=labels, timeout=timeout)
+        except error.TimeoutWarning as t:
+            r = t.result
+            stop = True
         if r is not None and r > 0:
             unresolved += 1
         else:
diff --git a/tests/test-merge-tools.t b/tests/test-merge-tools.t
--- a/tests/test-merge-tools.t
+++ b/tests/test-merge-tools.t
@@ -1028,3 +1028,80 @@
   [1]
 
 #endif
+
+merge timeouts
+
+  $ cat >> $HGRCPATH <<EOF
+  > [merge-tools]
+  > timeout = 1
+  > sleep.executable = python
+  > sleep.args = -c 'import time; time.sleep(5)'
+  > sleep.premerge = False
+  > false.executable = python
+  > false.args = -c 'quit(1)'
+  > false.premerge = False
+  > slow.executable = python
+  > slow.args = -c 'import os,sys,time;quit((time.sleep(5) or 1) if os.path.basename(sys.argv[1]) == "y" else 0)' \$local
+  > slow.premerge = False
+  > EOF
+  $ hg update -q -C 1
+  $ rm -f f.orig
+  $ echo later > z
+  $ cp z y
+  $ hg commit -Am z-later
+  adding y
+  adding z
+  created new head
+  $ hg update -q -C 2
+  $ echo sooner > z
+  $ cp z y
+  $ hg commit -Am z-sooner
+  adding y
+  adding z
+  $ hg merge 8 --tool sleep
+  merging f
+  warning: timeout running mergetool for f
+  0 files updated, 1 files merged, 0 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ hg resolve -l
+  R f
+  U y
+  U z
+  $ hg update -q -C 9
+  $ hg merge 8 --tool false
+  merging f
+  merging f failed!
+  merging y
+  merging y failed!
+  merging z
+  merging z failed!
+  0 files updated, 0 files merged, 0 files removed, 3 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ hg resolve -l
+  U f
+  U y
+  U z
+  $ hg update -q -C 9
+  $ hg merge 8 --tool slow
+  merging f
+  merging y
+  merging y failed!
+  warning: timeout running mergetool for y
+  0 files updated, 1 files merged, 0 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ hg resolve -l
+  R f
+  U y
+  U z
+  $ hg resolve --tool slow y z
+  merging y
+  merging y failed!
+  warning: timeout running mergetool for y
+  [1]
+  $ hg resolve -l
+  R f
+  U y
+  U z


More information about the Mercurial-devel mailing list