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']: |