[PATCH 3 of 4] patch: move updatedir() from cmdutil into patch

Patrick Mezard pmezard at gmail.com
Sat May 7 12:16:48 CDT 2011


# HG changeset patch
# User Patrick Mezard <pmezard at gmail.com>
# Date 1304787687 -7200
# Node ID 398726bef83770ec2548aeb49b60f75154390aab
# Parent  3fdef56efaaa937d11b20932496e34f8de4cd6f7
patch: move updatedir() from cmdutil into patch

Also, create an artificial wdutil.py to avoid import cycles between patch.py
and cmdutil.py.

diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -618,7 +618,7 @@
                 fuzz = patchmod.patch(patchfile, self.ui, strip=1,
                                       cwd=repo.root, files=files, eolmode=None)
             finally:
-                files = cmdutil.updatedir(self.ui, repo, files)
+                files = patchmod.updatedir(self.ui, repo, files)
             return (True, files, fuzz)
         except Exception, inst:
             self.ui.note(str(inst) + '\n')
diff --git a/hgext/record.py b/hgext/record.py
--- a/hgext/record.py
+++ b/hgext/record.py
@@ -482,7 +482,7 @@
                         patch.internalpatch(fp, ui, 1, repo.root, files=pfiles,
                                             eolmode=None)
                     finally:
-                        cmdutil.updatedir(ui, repo, pfiles)
+                        patch.updatedir(ui, repo, pfiles)
                 except patch.PatchError, err:
                     raise util.Abort(str(err))
             del fp
diff --git a/hgext/transplant.py b/hgext/transplant.py
--- a/hgext/transplant.py
+++ b/hgext/transplant.py
@@ -232,7 +232,7 @@
                                      % revlog.hex(node))
                         return None
                 finally:
-                    files = cmdutil.updatedir(self.ui, repo, files)
+                    files = patch.updatedir(self.ui, repo, files)
             except Exception, inst:
                 seriespath = os.path.join(self.path, 'series')
                 if os.path.exists(seriespath):
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -8,10 +8,17 @@
 from node import hex, nullid, nullrev, short
 from i18n import _
 import os, sys, errno, re, glob, tempfile
-import util, scmutil, templater, patch, error, templatekw
+import util, scmutil, templater, patch, error, templatekw, wdutil
 import match as matchmod
 import similar, revset, subrepo
 
+expandpats = wdutil.expandpats
+match = wdutil.match
+matchall = wdutil.matchall
+matchfiles = wdutil.matchfiles
+addremove = wdutil.addremove
+dirstatecopy = wdutil.dirstatecopy
+
 revrangesep = ':'
 
 def parsealiases(cmd):
@@ -243,157 +250,6 @@
                               pathname),
                 mode)
 
-def expandpats(pats):
-    if not util.expandglobs:
-        return list(pats)
-    ret = []
-    for p in pats:
-        kind, name = matchmod._patsplit(p, None)
-        if kind is None:
-            try:
-                globbed = glob.glob(name)
-            except re.error:
-                globbed = [name]
-            if globbed:
-                ret.extend(globbed)
-                continue
-        ret.append(p)
-    return ret
-
-def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
-    if pats == ("",):
-        pats = []
-    if not globbed and default == 'relpath':
-        pats = expandpats(pats or [])
-    m = matchmod.match(repo.root, repo.getcwd(), pats,
-                       opts.get('include'), opts.get('exclude'), default,
-                       auditor=repo.auditor)
-    def badfn(f, msg):
-        repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
-    m.bad = badfn
-    return m
-
-def matchall(repo):
-    return matchmod.always(repo.root, repo.getcwd())
-
-def matchfiles(repo, files):
-    return matchmod.exact(repo.root, repo.getcwd(), files)
-
-def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
-    if dry_run is None:
-        dry_run = opts.get('dry_run')
-    if similarity is None:
-        similarity = float(opts.get('similarity') or 0)
-    # we'd use status here, except handling of symlinks and ignore is tricky
-    added, unknown, deleted, removed = [], [], [], []
-    audit_path = scmutil.pathauditor(repo.root)
-    m = match(repo, pats, opts)
-    for abs in repo.walk(m):
-        target = repo.wjoin(abs)
-        good = True
-        try:
-            audit_path(abs)
-        except (OSError, util.Abort):
-            good = False
-        rel = m.rel(abs)
-        exact = m.exact(abs)
-        if good and abs not in repo.dirstate:
-            unknown.append(abs)
-            if repo.ui.verbose or not exact:
-                repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
-        elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
-            or (os.path.isdir(target) and not os.path.islink(target))):
-            deleted.append(abs)
-            if repo.ui.verbose or not exact:
-                repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
-        # for finding renames
-        elif repo.dirstate[abs] == 'r':
-            removed.append(abs)
-        elif repo.dirstate[abs] == 'a':
-            added.append(abs)
-    copies = {}
-    if similarity > 0:
-        for old, new, score in similar.findrenames(repo,
-                added + unknown, removed + deleted, similarity):
-            if repo.ui.verbose or not m.exact(old) or not m.exact(new):
-                repo.ui.status(_('recording removal of %s as rename to %s '
-                                 '(%d%% similar)\n') %
-                               (m.rel(old), m.rel(new), score * 100))
-            copies[new] = old
-
-    if not dry_run:
-        wctx = repo[None]
-        wlock = repo.wlock()
-        try:
-            wctx.remove(deleted)
-            wctx.add(unknown)
-            for new, old in copies.iteritems():
-                wctx.copy(old, new)
-        finally:
-            wlock.release()
-
-def updatedir(ui, repo, patches, similarity=0):
-    '''Update dirstate after patch application according to metadata'''
-    if not patches:
-        return []
-    copies = []
-    removes = set()
-    cfiles = patches.keys()
-    cwd = repo.getcwd()
-    if cwd:
-        cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
-    for f in patches:
-        gp = patches[f]
-        if not gp:
-            continue
-        if gp.op == 'RENAME':
-            copies.append((gp.oldpath, gp.path))
-            removes.add(gp.oldpath)
-        elif gp.op == 'COPY':
-            copies.append((gp.oldpath, gp.path))
-        elif gp.op == 'DELETE':
-            removes.add(gp.path)
-
-    wctx = repo[None]
-    for src, dst in copies:
-        dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
-    if (not similarity) and removes:
-        wctx.remove(sorted(removes), True)
-
-    for f in patches:
-        gp = patches[f]
-        if gp and gp.mode:
-            islink, isexec = gp.mode
-            dst = repo.wjoin(gp.path)
-            # patch won't create empty files
-            if gp.op == 'ADD' and not os.path.lexists(dst):
-                flags = (isexec and 'x' or '') + (islink and 'l' or '')
-                repo.wwrite(gp.path, '', flags)
-            util.setflags(dst, islink, isexec)
-    addremove(repo, cfiles, similarity=similarity)
-    files = patches.keys()
-    files.extend([r for r in removes if r not in files])
-    return sorted(files)
-
-def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
-    """Update the dirstate to reflect the intent of copying src to dst. For
-    different reasons it might not end with dst being marked as copied from src.
-    """
-    origsrc = repo.dirstate.copied(src) or src
-    if dst == origsrc: # copying back a copy?
-        if repo.dirstate[dst] not in 'mn' and not dryrun:
-            repo.dirstate.normallookup(dst)
-    else:
-        if repo.dirstate[origsrc] == 'a' and origsrc == src:
-            if not ui.quiet:
-                ui.warn(_("%s has not been committed yet, so no copy "
-                          "data will be stored for %s.\n")
-                        % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
-            if repo.dirstate[dst] in '?r' and not dryrun:
-                wctx.add([dst])
-        elif not dryrun:
-            wctx.copy(origsrc, dst)
-
 def copy(ui, repo, pats, opts, rename=False):
     # called with the repo lock held
     #
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2617,8 +2617,8 @@
                 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
                             files=files, eolmode=None)
             finally:
-                files = cmdutil.updatedir(ui, repo, files,
-                                          similarity=sim / 100.0)
+                files = patch.updatedir(ui, repo, files,
+                                        similarity=sim / 100.0)
             if opts.get('no_commit'):
                 if message:
                     msgs.append(message)
diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -11,7 +11,7 @@
 
 from i18n import _
 from node import hex, nullid, short
-import base85, mdiff, scmutil, util, diffhelpers, copies, encoding
+import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, wdutil
 
 gitre = re.compile('diff --git a/(.*) b/(.*)')
 
@@ -1157,6 +1157,49 @@
         return -1
     return err
 
+def updatedir(ui, repo, patches, similarity=0):
+    '''Update dirstate after patch application according to metadata'''
+    if not patches:
+        return []
+    copies = []
+    removes = set()
+    cfiles = patches.keys()
+    cwd = repo.getcwd()
+    if cwd:
+        cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
+    for f in patches:
+        gp = patches[f]
+        if not gp:
+            continue
+        if gp.op == 'RENAME':
+            copies.append((gp.oldpath, gp.path))
+            removes.add(gp.oldpath)
+        elif gp.op == 'COPY':
+            copies.append((gp.oldpath, gp.path))
+        elif gp.op == 'DELETE':
+            removes.add(gp.path)
+
+    wctx = repo[None]
+    for src, dst in copies:
+        wdutil.dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
+    if (not similarity) and removes:
+        wctx.remove(sorted(removes), True)
+
+    for f in patches:
+        gp = patches[f]
+        if gp and gp.mode:
+            islink, isexec = gp.mode
+            dst = repo.wjoin(gp.path)
+            # patch won't create empty files
+            if gp.op == 'ADD' and not os.path.lexists(dst):
+                flags = (isexec and 'x' or '') + (islink and 'l' or '')
+                repo.wwrite(gp.path, '', flags)
+            util.setflags(dst, islink, isexec)
+    wdutil.addremove(repo, cfiles, similarity=similarity)
+    files = patches.keys()
+    files.extend([r for r in removes if r not in files])
+    return sorted(files)
+
 def _externalpatch(patcher, patchname, ui, strip, cwd, files):
     """use <patcher> to apply <patchname> to the working directory.
     returns whether patch was applied with fuzz factor."""
diff --git a/mercurial/wdutil.py b/mercurial/wdutil.py
new file mode 100644
--- /dev/null
+++ b/mercurial/wdutil.py
@@ -0,0 +1,162 @@
+# wdutil.py - working dir utilities
+#
+# Copyright 2011 Patrick Mezard <pmezard at gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import glob, os
+import util, similar, scmutil
+import match as matchmod
+from i18n import _
+
+def expandpats(pats):
+    if not util.expandglobs:
+        return list(pats)
+    ret = []
+    for p in pats:
+        kind, name = matchmod._patsplit(p, None)
+        if kind is None:
+            try:
+                globbed = glob.glob(name)
+            except re.error:
+                globbed = [name]
+            if globbed:
+                ret.extend(globbed)
+                continue
+        ret.append(p)
+    return ret
+
+def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
+    if pats == ("",):
+        pats = []
+    if not globbed and default == 'relpath':
+        pats = expandpats(pats or [])
+    m = matchmod.match(repo.root, repo.getcwd(), pats,
+                       opts.get('include'), opts.get('exclude'), default,
+                       auditor=repo.auditor)
+    def badfn(f, msg):
+        repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
+    m.bad = badfn
+    return m
+
+def matchall(repo):
+    return matchmod.always(repo.root, repo.getcwd())
+
+def matchfiles(repo, files):
+    return matchmod.exact(repo.root, repo.getcwd(), files)
+
+def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
+    if dry_run is None:
+        dry_run = opts.get('dry_run')
+    if similarity is None:
+        similarity = float(opts.get('similarity') or 0)
+    # we'd use status here, except handling of symlinks and ignore is tricky
+    added, unknown, deleted, removed = [], [], [], []
+    audit_path = scmutil.pathauditor(repo.root)
+    m = match(repo, pats, opts)
+    for abs in repo.walk(m):
+        target = repo.wjoin(abs)
+        good = True
+        try:
+            audit_path(abs)
+        except (OSError, util.Abort):
+            good = False
+        rel = m.rel(abs)
+        exact = m.exact(abs)
+        if good and abs not in repo.dirstate:
+            unknown.append(abs)
+            if repo.ui.verbose or not exact:
+                repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
+        elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
+            or (os.path.isdir(target) and not os.path.islink(target))):
+            deleted.append(abs)
+            if repo.ui.verbose or not exact:
+                repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
+        # for finding renames
+        elif repo.dirstate[abs] == 'r':
+            removed.append(abs)
+        elif repo.dirstate[abs] == 'a':
+            added.append(abs)
+    copies = {}
+    if similarity > 0:
+        for old, new, score in similar.findrenames(repo,
+                added + unknown, removed + deleted, similarity):
+            if repo.ui.verbose or not m.exact(old) or not m.exact(new):
+                repo.ui.status(_('recording removal of %s as rename to %s '
+                                 '(%d%% similar)\n') %
+                               (m.rel(old), m.rel(new), score * 100))
+            copies[new] = old
+
+    if not dry_run:
+        wctx = repo[None]
+        wlock = repo.wlock()
+        try:
+            wctx.remove(deleted)
+            wctx.add(unknown)
+            for new, old in copies.iteritems():
+                wctx.copy(old, new)
+        finally:
+            wlock.release()
+
+def updatedir(ui, repo, patches, similarity=0):
+    '''Update dirstate after patch application according to metadata'''
+    if not patches:
+        return []
+    copies = []
+    removes = set()
+    cfiles = patches.keys()
+    cwd = repo.getcwd()
+    if cwd:
+        cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
+    for f in patches:
+        gp = patches[f]
+        if not gp:
+            continue
+        if gp.op == 'RENAME':
+            copies.append((gp.oldpath, gp.path))
+            removes.add(gp.oldpath)
+        elif gp.op == 'COPY':
+            copies.append((gp.oldpath, gp.path))
+        elif gp.op == 'DELETE':
+            removes.add(gp.path)
+
+    wctx = repo[None]
+    for src, dst in copies:
+        dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
+    if (not similarity) and removes:
+        wctx.remove(sorted(removes), True)
+
+    for f in patches:
+        gp = patches[f]
+        if gp and gp.mode:
+            islink, isexec = gp.mode
+            dst = repo.wjoin(gp.path)
+            # patch won't create empty files
+            if gp.op == 'ADD' and not os.path.lexists(dst):
+                flags = (isexec and 'x' or '') + (islink and 'l' or '')
+                repo.wwrite(gp.path, '', flags)
+            util.setflags(dst, islink, isexec)
+    addremove(repo, cfiles, similarity=similarity)
+    files = patches.keys()
+    files.extend([r for r in removes if r not in files])
+    return sorted(files)
+
+def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
+    """Update the dirstate to reflect the intent of copying src to dst. For
+    different reasons it might not end with dst being marked as copied from src.
+    """
+    origsrc = repo.dirstate.copied(src) or src
+    if dst == origsrc: # copying back a copy?
+        if repo.dirstate[dst] not in 'mn' and not dryrun:
+            repo.dirstate.normallookup(dst)
+    else:
+        if repo.dirstate[origsrc] == 'a' and origsrc == src:
+            if not ui.quiet:
+                ui.warn(_("%s has not been committed yet, so no copy "
+                          "data will be stored for %s.\n")
+                        % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
+            if repo.dirstate[dst] in '?r' and not dryrun:
+                wctx.add([dst])
+        elif not dryrun:
+            wctx.copy(origsrc, dst)


More information about the Mercurial-devel mailing list