comparison mercurial/commands.py @ 5589:9981b6b19ecf

move commands.docopy to cmdutil.copy
author Matt Mackall <mpm@selenic.com>
date Sun, 02 Dec 2007 18:11:59 -0600
parents 8788ff630c26
children 2493a478f395
comparison
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)