##// END OF EJS Templates
move commands.docopy to cmdutil.copy
Matt Mackall -
r5589:9981b6b1 default
parent child Browse files
Show More
@@ -8,7 +8,7 b''
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
@@ -286,6 +286,204 b' def addremove(repo, pats=[], opts={}, dr'
286 if not dry_run:
286 if not dry_run:
287 repo.copy(old, new)
287 repo.copy(old, new)
288
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
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
@@ -438,204 +438,6 b' def commit(ui, repo, *pats, **opts):'
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
@@ -652,7 +454,7 b' def copy(ui, repo, *pats, **opts):'
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
@@ -2260,7 +2062,7 b' def rename(ui, repo, *pats, **opts):'
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:
General Comments 0
You need to be logged in to leave comments. Login now