comparison mercurial/cmdutil.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 db6633f11d59
children 4b7b21acede0
comparison
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']: