mercurial/cmdutil.py
changeset 14259 df9ccd39828c
parent 14257 d035ede73434
child 14269 66257848c154
equal deleted inserted replaced
14258:50e3fb2ab9fa 14259:df9ccd39828c
     6 # GNU General Public License version 2 or any later version.
     6 # GNU General Public License version 2 or any later version.
     7 
     7 
     8 from node import hex, nullid, nullrev, short
     8 from node import hex, nullid, nullrev, short
     9 from i18n import _
     9 from i18n import _
    10 import os, sys, errno, re, glob, tempfile
    10 import os, sys, errno, re, glob, tempfile
    11 import util, scmutil, templater, patch, error, templatekw
    11 import util, scmutil, templater, patch, error, templatekw, wdutil
    12 import match as matchmod
    12 import match as matchmod
    13 import similar, revset, subrepo
    13 import similar, revset, subrepo
       
    14 
       
    15 expandpats = wdutil.expandpats
       
    16 match = wdutil.match
       
    17 matchall = wdutil.matchall
       
    18 matchfiles = wdutil.matchfiles
       
    19 addremove = wdutil.addremove
       
    20 dirstatecopy = wdutil.dirstatecopy
    14 
    21 
    15 revrangesep = ':'
    22 revrangesep = ':'
    16 
    23 
    17 def parsealiases(cmd):
    24 def parsealiases(cmd):
    18     return cmd.lstrip("^").split("|")
    25     return cmd.lstrip("^").split("|")
   240     if hasattr(pat, 'read') and 'r' in mode:
   247     if hasattr(pat, 'read') and 'r' in mode:
   241         return pat
   248         return pat
   242     return open(make_filename(repo, pat, node, total, seqno, revwidth,
   249     return open(make_filename(repo, pat, node, total, seqno, revwidth,
   243                               pathname),
   250                               pathname),
   244                 mode)
   251                 mode)
   245 
       
   246 def expandpats(pats):
       
   247     if not util.expandglobs:
       
   248         return list(pats)
       
   249     ret = []
       
   250     for p in pats:
       
   251         kind, name = matchmod._patsplit(p, None)
       
   252         if kind is None:
       
   253             try:
       
   254                 globbed = glob.glob(name)
       
   255             except re.error:
       
   256                 globbed = [name]
       
   257             if globbed:
       
   258                 ret.extend(globbed)
       
   259                 continue
       
   260         ret.append(p)
       
   261     return ret
       
   262 
       
   263 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
       
   264     if pats == ("",):
       
   265         pats = []
       
   266     if not globbed and default == 'relpath':
       
   267         pats = expandpats(pats or [])
       
   268     m = matchmod.match(repo.root, repo.getcwd(), pats,
       
   269                        opts.get('include'), opts.get('exclude'), default,
       
   270                        auditor=repo.auditor)
       
   271     def badfn(f, msg):
       
   272         repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
       
   273     m.bad = badfn
       
   274     return m
       
   275 
       
   276 def matchall(repo):
       
   277     return matchmod.always(repo.root, repo.getcwd())
       
   278 
       
   279 def matchfiles(repo, files):
       
   280     return matchmod.exact(repo.root, repo.getcwd(), files)
       
   281 
       
   282 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
       
   283     if dry_run is None:
       
   284         dry_run = opts.get('dry_run')
       
   285     if similarity is None:
       
   286         similarity = float(opts.get('similarity') or 0)
       
   287     # we'd use status here, except handling of symlinks and ignore is tricky
       
   288     added, unknown, deleted, removed = [], [], [], []
       
   289     audit_path = scmutil.pathauditor(repo.root)
       
   290     m = match(repo, pats, opts)
       
   291     for abs in repo.walk(m):
       
   292         target = repo.wjoin(abs)
       
   293         good = True
       
   294         try:
       
   295             audit_path(abs)
       
   296         except (OSError, util.Abort):
       
   297             good = False
       
   298         rel = m.rel(abs)
       
   299         exact = m.exact(abs)
       
   300         if good and abs not in repo.dirstate:
       
   301             unknown.append(abs)
       
   302             if repo.ui.verbose or not exact:
       
   303                 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
       
   304         elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
       
   305             or (os.path.isdir(target) and not os.path.islink(target))):
       
   306             deleted.append(abs)
       
   307             if repo.ui.verbose or not exact:
       
   308                 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
       
   309         # for finding renames
       
   310         elif repo.dirstate[abs] == 'r':
       
   311             removed.append(abs)
       
   312         elif repo.dirstate[abs] == 'a':
       
   313             added.append(abs)
       
   314     copies = {}
       
   315     if similarity > 0:
       
   316         for old, new, score in similar.findrenames(repo,
       
   317                 added + unknown, removed + deleted, similarity):
       
   318             if repo.ui.verbose or not m.exact(old) or not m.exact(new):
       
   319                 repo.ui.status(_('recording removal of %s as rename to %s '
       
   320                                  '(%d%% similar)\n') %
       
   321                                (m.rel(old), m.rel(new), score * 100))
       
   322             copies[new] = old
       
   323 
       
   324     if not dry_run:
       
   325         wctx = repo[None]
       
   326         wlock = repo.wlock()
       
   327         try:
       
   328             wctx.remove(deleted)
       
   329             wctx.add(unknown)
       
   330             for new, old in copies.iteritems():
       
   331                 wctx.copy(old, new)
       
   332         finally:
       
   333             wlock.release()
       
   334 
       
   335 def updatedir(ui, repo, patches, similarity=0):
       
   336     '''Update dirstate after patch application according to metadata'''
       
   337     if not patches:
       
   338         return []
       
   339     copies = []
       
   340     removes = set()
       
   341     cfiles = patches.keys()
       
   342     cwd = repo.getcwd()
       
   343     if cwd:
       
   344         cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
       
   345     for f in patches:
       
   346         gp = patches[f]
       
   347         if not gp:
       
   348             continue
       
   349         if gp.op == 'RENAME':
       
   350             copies.append((gp.oldpath, gp.path))
       
   351             removes.add(gp.oldpath)
       
   352         elif gp.op == 'COPY':
       
   353             copies.append((gp.oldpath, gp.path))
       
   354         elif gp.op == 'DELETE':
       
   355             removes.add(gp.path)
       
   356 
       
   357     wctx = repo[None]
       
   358     for src, dst in copies:
       
   359         dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
       
   360     if (not similarity) and removes:
       
   361         wctx.remove(sorted(removes), True)
       
   362 
       
   363     for f in patches:
       
   364         gp = patches[f]
       
   365         if gp and gp.mode:
       
   366             islink, isexec = gp.mode
       
   367             dst = repo.wjoin(gp.path)
       
   368             # patch won't create empty files
       
   369             if gp.op == 'ADD' and not os.path.lexists(dst):
       
   370                 flags = (isexec and 'x' or '') + (islink and 'l' or '')
       
   371                 repo.wwrite(gp.path, '', flags)
       
   372             util.setflags(dst, islink, isexec)
       
   373     addremove(repo, cfiles, similarity=similarity)
       
   374     files = patches.keys()
       
   375     files.extend([r for r in removes if r not in files])
       
   376     return sorted(files)
       
   377 
       
   378 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
       
   379     """Update the dirstate to reflect the intent of copying src to dst. For
       
   380     different reasons it might not end with dst being marked as copied from src.
       
   381     """
       
   382     origsrc = repo.dirstate.copied(src) or src
       
   383     if dst == origsrc: # copying back a copy?
       
   384         if repo.dirstate[dst] not in 'mn' and not dryrun:
       
   385             repo.dirstate.normallookup(dst)
       
   386     else:
       
   387         if repo.dirstate[origsrc] == 'a' and origsrc == src:
       
   388             if not ui.quiet:
       
   389                 ui.warn(_("%s has not been committed yet, so no copy "
       
   390                           "data will be stored for %s.\n")
       
   391                         % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
       
   392             if repo.dirstate[dst] in '?r' and not dryrun:
       
   393                 wctx.add([dst])
       
   394         elif not dryrun:
       
   395             wctx.copy(origsrc, dst)
       
   396 
   252 
   397 def copy(ui, repo, pats, opts, rename=False):
   253 def copy(ui, repo, pats, opts, rename=False):
   398     # called with the repo lock held
   254     # called with the repo lock held
   399     #
   255     #
   400     # hgsep => pathname that uses "/" to separate directories
   256     # hgsep => pathname that uses "/" to separate directories