mercurial/commands.py
changeset 5589 9981b6b19ecf
parent 5575 8788ff630c26
child 5610 2493a478f395
equal deleted inserted replaced
5588:083b6e3142a2 5589:9981b6b19ecf
   436     def commitfunc(ui, repo, files, message, match, opts):
   436     def commitfunc(ui, repo, files, message, match, opts):
   437         return repo.commit(files, message, opts['user'], opts['date'], match,
   437         return repo.commit(files, message, opts['user'], opts['date'], match,
   438                            force_editor=opts.get('force_editor'))
   438                            force_editor=opts.get('force_editor'))
   439     cmdutil.commit(ui, repo, commitfunc, pats, opts)
   439     cmdutil.commit(ui, repo, commitfunc, pats, opts)
   440 
   440 
   441 def docopy(ui, repo, pats, opts):
       
   442     # called with the repo lock held
       
   443     #
       
   444     # hgsep => pathname that uses "/" to separate directories
       
   445     # ossep => pathname that uses os.sep to separate directories
       
   446     cwd = repo.getcwd()
       
   447     errors = 0
       
   448     copied = []
       
   449     targets = {}
       
   450 
       
   451     # abs: hgsep
       
   452     # rel: ossep
       
   453     # return: hgsep
       
   454     def okaytocopy(abs, rel, exact):
       
   455         reasons = {'?': _('is not managed'),
       
   456                    'r': _('has been marked for remove')}
       
   457         state = repo.dirstate[abs]
       
   458         reason = reasons.get(state)
       
   459         if reason:
       
   460             if exact:
       
   461                 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
       
   462         else:
       
   463             if state == 'a':
       
   464                 origsrc = repo.dirstate.copied(abs)
       
   465                 if origsrc is not None:
       
   466                     return origsrc
       
   467             return abs
       
   468 
       
   469     # origsrc: hgsep
       
   470     # abssrc: hgsep
       
   471     # relsrc: ossep
       
   472     # otarget: ossep
       
   473     def copy(origsrc, abssrc, relsrc, otarget, exact):
       
   474         abstarget = util.canonpath(repo.root, cwd, otarget)
       
   475         reltarget = repo.pathto(abstarget, cwd)
       
   476         prevsrc = targets.get(abstarget)
       
   477         src = repo.wjoin(abssrc)
       
   478         target = repo.wjoin(abstarget)
       
   479         if prevsrc is not None:
       
   480             ui.warn(_('%s: not overwriting - %s collides with %s\n') %
       
   481                     (reltarget, repo.pathto(abssrc, cwd),
       
   482                      repo.pathto(prevsrc, cwd)))
       
   483             return
       
   484         if (not opts['after'] and os.path.exists(target) or
       
   485             opts['after'] and repo.dirstate[abstarget] in 'mn'):
       
   486             if not opts['force']:
       
   487                 ui.warn(_('%s: not overwriting - file exists\n') %
       
   488                         reltarget)
       
   489                 return
       
   490             if not opts['after'] and not opts.get('dry_run'):
       
   491                 os.unlink(target)
       
   492         if opts['after']:
       
   493             if not os.path.exists(target):
       
   494                 return
       
   495         else:
       
   496             targetdir = os.path.dirname(target) or '.'
       
   497             if not os.path.isdir(targetdir) and not opts.get('dry_run'):
       
   498                 os.makedirs(targetdir)
       
   499             try:
       
   500                 restore = repo.dirstate[abstarget] == 'r'
       
   501                 if restore and not opts.get('dry_run'):
       
   502                     repo.undelete([abstarget])
       
   503                 try:
       
   504                     if not opts.get('dry_run'):
       
   505                         util.copyfile(src, target)
       
   506                     restore = False
       
   507                 finally:
       
   508                     if restore:
       
   509                         repo.remove([abstarget])
       
   510             except IOError, inst:
       
   511                 if inst.errno == errno.ENOENT:
       
   512                     ui.warn(_('%s: deleted in working copy\n') % relsrc)
       
   513                 else:
       
   514                     ui.warn(_('%s: cannot copy - %s\n') %
       
   515                             (relsrc, inst.strerror))
       
   516                     errors += 1
       
   517                     return
       
   518         if ui.verbose or not exact:
       
   519             ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
       
   520         targets[abstarget] = abssrc
       
   521         if abstarget != origsrc:
       
   522             if repo.dirstate[origsrc] == 'a':
       
   523                 if not ui.quiet:
       
   524                     ui.warn(_("%s has not been committed yet, so no copy "
       
   525                               "data will be stored for %s.\n")
       
   526                             % (repo.pathto(origsrc, cwd), reltarget))
       
   527                 if abstarget not in repo.dirstate and not opts.get('dry_run'):
       
   528                     repo.add([abstarget])
       
   529             elif not opts.get('dry_run'):
       
   530                 repo.copy(origsrc, abstarget)
       
   531         copied.append((abssrc, relsrc, exact))
       
   532 
       
   533     # pat: ossep
       
   534     # dest ossep
       
   535     # srcs: list of (hgsep, hgsep, ossep, bool)
       
   536     # return: function that takes hgsep and returns ossep
       
   537     def targetpathfn(pat, dest, srcs):
       
   538         if os.path.isdir(pat):
       
   539             abspfx = util.canonpath(repo.root, cwd, pat)
       
   540             abspfx = util.localpath(abspfx)
       
   541             if destdirexists:
       
   542                 striplen = len(os.path.split(abspfx)[0])
       
   543             else:
       
   544                 striplen = len(abspfx)
       
   545             if striplen:
       
   546                 striplen += len(os.sep)
       
   547             res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
       
   548         elif destdirexists:
       
   549             res = lambda p: os.path.join(dest,
       
   550                                          os.path.basename(util.localpath(p)))
       
   551         else:
       
   552             res = lambda p: dest
       
   553         return res
       
   554 
       
   555     # pat: ossep
       
   556     # dest ossep
       
   557     # srcs: list of (hgsep, hgsep, ossep, bool)
       
   558     # return: function that takes hgsep and returns ossep
       
   559     def targetpathafterfn(pat, dest, srcs):
       
   560         if util.patkind(pat, None)[0]:
       
   561             # a mercurial pattern
       
   562             res = lambda p: os.path.join(dest,
       
   563                                          os.path.basename(util.localpath(p)))
       
   564         else:
       
   565             abspfx = util.canonpath(repo.root, cwd, pat)
       
   566             if len(abspfx) < len(srcs[0][0]):
       
   567                 # A directory. Either the target path contains the last
       
   568                 # component of the source path or it does not.
       
   569                 def evalpath(striplen):
       
   570                     score = 0
       
   571                     for s in srcs:
       
   572                         t = os.path.join(dest, util.localpath(s[0])[striplen:])
       
   573                         if os.path.exists(t):
       
   574                             score += 1
       
   575                     return score
       
   576 
       
   577                 abspfx = util.localpath(abspfx)
       
   578                 striplen = len(abspfx)
       
   579                 if striplen:
       
   580                     striplen += len(os.sep)
       
   581                 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
       
   582                     score = evalpath(striplen)
       
   583                     striplen1 = len(os.path.split(abspfx)[0])
       
   584                     if striplen1:
       
   585                         striplen1 += len(os.sep)
       
   586                     if evalpath(striplen1) > score:
       
   587                         striplen = striplen1
       
   588                 res = lambda p: os.path.join(dest,
       
   589                                              util.localpath(p)[striplen:])
       
   590             else:
       
   591                 # a file
       
   592                 if destdirexists:
       
   593                     res = lambda p: os.path.join(dest,
       
   594                                         os.path.basename(util.localpath(p)))
       
   595                 else:
       
   596                     res = lambda p: dest
       
   597         return res
       
   598 
       
   599 
       
   600     pats = util.expand_glob(pats)
       
   601     if not pats:
       
   602         raise util.Abort(_('no source or destination specified'))
       
   603     if len(pats) == 1:
       
   604         raise util.Abort(_('no destination specified'))
       
   605     dest = pats.pop()
       
   606     destdirexists = os.path.isdir(dest)
       
   607     if not destdirexists:
       
   608         if len(pats) > 1 or util.patkind(pats[0], None)[0]:
       
   609             raise util.Abort(_('with multiple sources, destination must be an '
       
   610                                'existing directory'))
       
   611         if dest.endswith(os.sep) or os.altsep and dest.endswith(os.altsep):
       
   612             raise util.Abort(_('destination %s is not a directory') % dest)
       
   613     if opts['after']:
       
   614         tfn = targetpathafterfn
       
   615     else:
       
   616         tfn = targetpathfn
       
   617     copylist = []
       
   618     for pat in pats:
       
   619         srcs = []
       
   620         for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
       
   621                                                        globbed=True):
       
   622             origsrc = okaytocopy(abssrc, relsrc, exact)
       
   623             if origsrc:
       
   624                 srcs.append((origsrc, abssrc, relsrc, exact))
       
   625         if not srcs:
       
   626             continue
       
   627         copylist.append((tfn(pat, dest, srcs), srcs))
       
   628     if not copylist:
       
   629         raise util.Abort(_('no files to copy'))
       
   630 
       
   631     for targetpath, srcs in copylist:
       
   632         for origsrc, abssrc, relsrc, exact in srcs:
       
   633             copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
       
   634 
       
   635     if errors:
       
   636         ui.warn(_('(consider using --after)\n'))
       
   637     return errors, copied
       
   638 
       
   639 def copy(ui, repo, *pats, **opts):
   441 def copy(ui, repo, *pats, **opts):
   640     """mark files as copied for the next commit
   442     """mark files as copied for the next commit
   641 
   443 
   642     Mark dest as having copies of source files.  If dest is a
   444     Mark dest as having copies of source files.  If dest is a
   643     directory, copies are put in that directory.  If dest is a file,
   445     directory, copies are put in that directory.  If dest is a file,
   650     This command takes effect in the next commit. To undo a copy
   452     This command takes effect in the next commit. To undo a copy
   651     before that, see hg revert.
   453     before that, see hg revert.
   652     """
   454     """
   653     wlock = repo.wlock(False)
   455     wlock = repo.wlock(False)
   654     try:
   456     try:
   655         errs, copied = docopy(ui, repo, pats, opts)
   457         errs, copied = cmdutil.copy(ui, repo, pats, opts)
   656     finally:
   458     finally:
   657         del wlock
   459         del wlock
   658     return errs
   460     return errs
   659 
   461 
   660 def debugancestor(ui, index, rev1, rev2):
   462 def debugancestor(ui, index, rev1, rev2):
  2258     This command takes effect in the next commit. To undo a rename
  2060     This command takes effect in the next commit. To undo a rename
  2259     before that, see hg revert.
  2061     before that, see hg revert.
  2260     """
  2062     """
  2261     wlock = repo.wlock(False)
  2063     wlock = repo.wlock(False)
  2262     try:
  2064     try:
  2263         errs, copied = docopy(ui, repo, pats, opts)
  2065         errs, copied = cmdutil.copy(ui, repo, pats, opts)
  2264         names = []
  2066         names = []
  2265         for abs, rel, exact in copied:
  2067         for abs, rel, exact in copied:
  2266             if ui.verbose or not exact:
  2068             if ui.verbose or not exact:
  2267                 ui.status(_('removing %s\n') % rel)
  2069                 ui.status(_('removing %s\n') % rel)
  2268             names.append(abs)
  2070             names.append(abs)