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, |