mercurial/cmdutil.py
changeset 5589 9981b6b19ecf
parent 5550 db6633f11d59
child 5604 4b7b21acede0
equal deleted inserted replaced
5588:083b6e3142a2 5589:9981b6b19ecf
     6 # of the GNU General Public License, incorporated herein by reference.
     6 # of the GNU General Public License, incorporated herein by reference.
     7 
     7 
     8 from node import *
     8 from node import *
     9 from i18n import _
     9 from i18n import _
    10 import os, sys, bisect, stat
    10 import os, sys, bisect, stat
    11 import mdiff, bdiff, util, templater, patch
    11 import mdiff, bdiff, util, templater, patch, errno
    12 
    12 
    13 revrangesep = ':'
    13 revrangesep = ':'
    14 
    14 
    15 class UnknownCommand(Exception):
    15 class UnknownCommand(Exception):
    16     """Exception raised if command is not in the command table."""
    16     """Exception raised if command is not in the command table."""
   283                 repo.ui.status(_('recording removal of %s as rename to %s '
   283                 repo.ui.status(_('recording removal of %s as rename to %s '
   284                                  '(%d%% similar)\n') %
   284                                  '(%d%% similar)\n') %
   285                                (oldrel, newrel, score * 100))
   285                                (oldrel, newrel, score * 100))
   286             if not dry_run:
   286             if not dry_run:
   287                 repo.copy(old, new)
   287                 repo.copy(old, new)
       
   288 
       
   289 def copy(ui, repo, pats, opts):
       
   290     # called with the repo lock held
       
   291     #
       
   292     # hgsep => pathname that uses "/" to separate directories
       
   293     # ossep => pathname that uses os.sep to separate directories
       
   294     cwd = repo.getcwd()
       
   295     errors = 0
       
   296     copied = []
       
   297     targets = {}
       
   298 
       
   299     # abs: hgsep
       
   300     # rel: ossep
       
   301     # return: hgsep
       
   302     def okaytocopy(abs, rel, exact):
       
   303         reasons = {'?': _('is not managed'),
       
   304                    'r': _('has been marked for remove')}
       
   305         state = repo.dirstate[abs]
       
   306         reason = reasons.get(state)
       
   307         if reason:
       
   308             if exact:
       
   309                 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
       
   310         else:
       
   311             if state == 'a':
       
   312                 origsrc = repo.dirstate.copied(abs)
       
   313                 if origsrc is not None:
       
   314                     return origsrc
       
   315             return abs
       
   316 
       
   317     # origsrc: hgsep
       
   318     # abssrc: hgsep
       
   319     # relsrc: ossep
       
   320     # otarget: ossep
       
   321     def copy(origsrc, abssrc, relsrc, otarget, exact):
       
   322         abstarget = util.canonpath(repo.root, cwd, otarget)
       
   323         reltarget = repo.pathto(abstarget, cwd)
       
   324         prevsrc = targets.get(abstarget)
       
   325         src = repo.wjoin(abssrc)
       
   326         target = repo.wjoin(abstarget)
       
   327         if prevsrc is not None:
       
   328             ui.warn(_('%s: not overwriting - %s collides with %s\n') %
       
   329                     (reltarget, repo.pathto(abssrc, cwd),
       
   330                      repo.pathto(prevsrc, cwd)))
       
   331             return
       
   332         if (not opts['after'] and os.path.exists(target) or
       
   333             opts['after'] and repo.dirstate[abstarget] in 'mn'):
       
   334             if not opts['force']:
       
   335                 ui.warn(_('%s: not overwriting - file exists\n') %
       
   336                         reltarget)
       
   337                 return
       
   338             if not opts['after'] and not opts.get('dry_run'):
       
   339                 os.unlink(target)
       
   340         if opts['after']:
       
   341             if not os.path.exists(target):
       
   342                 return
       
   343         else:
       
   344             targetdir = os.path.dirname(target) or '.'
       
   345             if not os.path.isdir(targetdir) and not opts.get('dry_run'):
       
   346                 os.makedirs(targetdir)
       
   347             try:
       
   348                 restore = repo.dirstate[abstarget] == 'r'
       
   349                 if restore and not opts.get('dry_run'):
       
   350                     repo.undelete([abstarget])
       
   351                 try:
       
   352                     if not opts.get('dry_run'):
       
   353                         util.copyfile(src, target)
       
   354                     restore = False
       
   355                 finally:
       
   356                     if restore:
       
   357                         repo.remove([abstarget])
       
   358             except IOError, inst:
       
   359                 if inst.errno == errno.ENOENT:
       
   360                     ui.warn(_('%s: deleted in working copy\n') % relsrc)
       
   361                 else:
       
   362                     ui.warn(_('%s: cannot copy - %s\n') %
       
   363                             (relsrc, inst.strerror))
       
   364                     errors += 1
       
   365                     return
       
   366         if ui.verbose or not exact:
       
   367             ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
       
   368         targets[abstarget] = abssrc
       
   369         if abstarget != origsrc:
       
   370             if repo.dirstate[origsrc] == 'a':
       
   371                 if not ui.quiet:
       
   372                     ui.warn(_("%s has not been committed yet, so no copy "
       
   373                               "data will be stored for %s.\n")
       
   374                             % (repo.pathto(origsrc, cwd), reltarget))
       
   375                 if abstarget not in repo.dirstate and not opts.get('dry_run'):
       
   376                     repo.add([abstarget])
       
   377             elif not opts.get('dry_run'):
       
   378                 repo.copy(origsrc, abstarget)
       
   379         copied.append((abssrc, relsrc, exact))
       
   380 
       
   381     # pat: ossep
       
   382     # dest ossep
       
   383     # srcs: list of (hgsep, hgsep, ossep, bool)
       
   384     # return: function that takes hgsep and returns ossep
       
   385     def targetpathfn(pat, dest, srcs):
       
   386         if os.path.isdir(pat):
       
   387             abspfx = util.canonpath(repo.root, cwd, pat)
       
   388             abspfx = util.localpath(abspfx)
       
   389             if destdirexists:
       
   390                 striplen = len(os.path.split(abspfx)[0])
       
   391             else:
       
   392                 striplen = len(abspfx)
       
   393             if striplen:
       
   394                 striplen += len(os.sep)
       
   395             res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
       
   396         elif destdirexists:
       
   397             res = lambda p: os.path.join(dest,
       
   398                                          os.path.basename(util.localpath(p)))
       
   399         else:
       
   400             res = lambda p: dest
       
   401         return res
       
   402 
       
   403     # pat: ossep
       
   404     # dest ossep
       
   405     # srcs: list of (hgsep, hgsep, ossep, bool)
       
   406     # return: function that takes hgsep and returns ossep
       
   407     def targetpathafterfn(pat, dest, srcs):
       
   408         if util.patkind(pat, None)[0]:
       
   409             # a mercurial pattern
       
   410             res = lambda p: os.path.join(dest,
       
   411                                          os.path.basename(util.localpath(p)))
       
   412         else:
       
   413             abspfx = util.canonpath(repo.root, cwd, pat)
       
   414             if len(abspfx) < len(srcs[0][0]):
       
   415                 # A directory. Either the target path contains the last
       
   416                 # component of the source path or it does not.
       
   417                 def evalpath(striplen):
       
   418                     score = 0
       
   419                     for s in srcs:
       
   420                         t = os.path.join(dest, util.localpath(s[0])[striplen:])
       
   421                         if os.path.exists(t):
       
   422                             score += 1
       
   423                     return score
       
   424 
       
   425                 abspfx = util.localpath(abspfx)
       
   426                 striplen = len(abspfx)
       
   427                 if striplen:
       
   428                     striplen += len(os.sep)
       
   429                 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
       
   430                     score = evalpath(striplen)
       
   431                     striplen1 = len(os.path.split(abspfx)[0])
       
   432                     if striplen1:
       
   433                         striplen1 += len(os.sep)
       
   434                     if evalpath(striplen1) > score:
       
   435                         striplen = striplen1
       
   436                 res = lambda p: os.path.join(dest,
       
   437                                              util.localpath(p)[striplen:])
       
   438             else:
       
   439                 # a file
       
   440                 if destdirexists:
       
   441                     res = lambda p: os.path.join(dest,
       
   442                                         os.path.basename(util.localpath(p)))
       
   443                 else:
       
   444                     res = lambda p: dest
       
   445         return res
       
   446 
       
   447 
       
   448     pats = util.expand_glob(pats)
       
   449     if not pats:
       
   450         raise util.Abort(_('no source or destination specified'))
       
   451     if len(pats) == 1:
       
   452         raise util.Abort(_('no destination specified'))
       
   453     dest = pats.pop()
       
   454     destdirexists = os.path.isdir(dest)
       
   455     if not destdirexists:
       
   456         if len(pats) > 1 or util.patkind(pats[0], None)[0]:
       
   457             raise util.Abort(_('with multiple sources, destination must be an '
       
   458                                'existing directory'))
       
   459         if dest.endswith(os.sep) or os.altsep and dest.endswith(os.altsep):
       
   460             raise util.Abort(_('destination %s is not a directory') % dest)
       
   461     if opts['after']:
       
   462         tfn = targetpathafterfn
       
   463     else:
       
   464         tfn = targetpathfn
       
   465     copylist = []
       
   466     for pat in pats:
       
   467         srcs = []
       
   468         for tag, abssrc, relsrc, exact in walk(repo, [pat], opts,
       
   469                                                globbed=True):
       
   470             origsrc = okaytocopy(abssrc, relsrc, exact)
       
   471             if origsrc:
       
   472                 srcs.append((origsrc, abssrc, relsrc, exact))
       
   473         if not srcs:
       
   474             continue
       
   475         copylist.append((tfn(pat, dest, srcs), srcs))
       
   476     if not copylist:
       
   477         raise util.Abort(_('no files to copy'))
       
   478 
       
   479     for targetpath, srcs in copylist:
       
   480         for origsrc, abssrc, relsrc, exact in srcs:
       
   481             copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
       
   482 
       
   483     if errors:
       
   484         ui.warn(_('(consider using --after)\n'))
       
   485     return errors, copied
   288 
   486 
   289 def service(opts, parentfn=None, initfn=None, runfn=None):
   487 def service(opts, parentfn=None, initfn=None, runfn=None):
   290     '''Run a command as a service.'''
   488     '''Run a command as a service.'''
   291 
   489 
   292     if opts['daemon'] and not opts['daemon_pipefds']:
   490     if opts['daemon'] and not opts['daemon_pipefds']: