diff mercurial/commands.py @ 1512:53ad6ee6ede4

generalize copy/rename to handle more than one source directory
author Robin Farine <robin.farine@terminus.org>
date Tue, 08 Nov 2005 10:35:05 -0800
parents cd8fadd8c689
children 5c3b93b244aa
line wrap: on
line diff
--- a/mercurial/commands.py	Tue Nov 08 10:35:00 2005 -0800
+++ b/mercurial/commands.py	Tue Nov 08 10:35:05 2005 -0800
@@ -787,14 +787,9 @@
         raise util.Abort(str(inst))
 
 def docopy(ui, repo, pats, opts):
-    if not pats:
-        raise util.Abort(_('no source or destination specified'))
-    elif len(pats) == 1:
-        raise util.Abort(_('no destination specified'))
-    pats = list(pats)
-    dest = pats.pop()
-    sources = []
-    dir2dir = len(pats) == 1 and os.path.isdir(pats[0])
+    cwd = repo.getcwd()
+    errors = 0
+    copied = []
 
     def okaytocopy(abs, rel, exact):
         reasons = {'?': _('is not managed'),
@@ -805,74 +800,68 @@
         else:
             return True
 
-    for src, abs, rel, exact in walk(repo, pats, opts):
-        if okaytocopy(abs, rel, exact):
-            sources.append((abs, rel, exact))
-    if not sources:
-        raise util.Abort(_('no files to copy'))
-
-    cwd = repo.getcwd()
-    absdest = util.canonpath(repo.root, cwd, dest)
-    reldest = util.pathto(cwd, absdest)
-    if os.path.exists(reldest):
-        destisfile = not os.path.isdir(reldest)
-    else:
-        destisfile = not dir2dir and (len(sources) == 1
-                                      or repo.dirstate.state(absdest) != '?')
-
-    if destisfile and len(sources) > 1:
-        raise util.Abort(_('with multiple sources, destination must be a '
-                           'directory'))
-
-    srcpfxlen = 0
-    if dir2dir:
-        srcpfx = util.pathto(cwd, util.canonpath(repo.root, cwd, pats[0]))
-        if os.path.exists(reldest):
-            srcpfx = os.path.split(srcpfx)[0]
-        if srcpfx:
-            srcpfx += os.sep
-        srcpfxlen = len(srcpfx)
-
-    errs, copied = 0, []
-    for abs, rel, exact in sources:
-        if destisfile:
-            mydest = reldest
-        elif dir2dir:
-            mydest = os.path.join(dest, rel[srcpfxlen:])
-        else:
-            mydest = os.path.join(dest, os.path.basename(rel))
-        myabsdest = util.canonpath(repo.root, cwd, mydest)
-        myreldest = util.pathto(cwd, myabsdest)
-        if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
-            ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
-            continue
-        mydestdir = os.path.dirname(myreldest) or '.'
+    def copy(abssrc, relsrc, target, exact):
+        abstarget = util.canonpath(repo.root, cwd, target)
+        reltarget = util.pathto(cwd, abstarget)
+        if not opts['force'] and repo.dirstate.state(abstarget) not in 'a?':
+            ui.warn(_('%s: not overwriting - file already managed\n') %
+                    reltarget)
+            return
+        if ui.verbose or not exact:
+            ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
         if not opts['after']:
+            targetdir = os.path.dirname(reltarget) or '.'
+            if not os.path.isdir(targetdir):
+                os.makedirs(targetdir)
             try:
-                if dir2dir: os.makedirs(mydestdir)
-                elif not destisfile: os.mkdir(mydestdir)
-            except OSError, inst:
-                if inst.errno != errno.EEXIST: raise
-        if ui.verbose or not exact:
-            ui.status(_('copying %s to %s\n') % (rel, myreldest))
-        if not opts['after']:
-            try:
-                shutil.copyfile(rel, myreldest)
-                shutil.copymode(rel, myreldest)
+                shutil.copyfile(relsrc, reltarget)
+                shutil.copymode(relsrc, reltarget)
             except shutil.Error, inst:
                 raise util.Abort(str(inst))
             except IOError, inst:
                 if inst.errno == errno.ENOENT:
-                    ui.warn(_('%s: deleted in working copy\n') % rel)
+                    ui.warn(_('%s: deleted in working copy\n') % relsrc)
                 else:
-                    ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
-                errs += 1
-                continue
-        repo.copy(abs, myabsdest)
-        copied.append((abs, rel, exact))
-    if errs:
+                    ui.warn(_('%s: cannot copy - %s\n') %
+                            (relsrc, inst.strerror))
+                    errors += 1
+                    return
+        repo.copy(abssrc, abstarget)
+        copied.append((abssrc, relsrc, exact))
+
+    pats = list(pats)
+    if not pats:
+        raise util.Abort(_('no source or destination specified'))
+    if len(pats) == 1:
+        raise util.Abort(_('no destination specified'))
+    dest = pats.pop()
+    destdirexists = os.path.isdir(dest)
+    if (len(pats) > 1 or not os.path.exists(pats[0])) and not destdirexists:
+        raise util.Abort(_('with multiple sources, destination must be an '
+                         'existing directory'))
+
+    for pat in pats:
+        if os.path.isdir(pat):
+            if destdirexists:
+                striplen = len(os.path.split(pat)[0])
+            else:
+                striplen = len(pat)
+            if striplen:
+                striplen += len(os.sep)
+            targetpath = lambda p: os.path.join(dest, p[striplen:])
+        elif destdirexists:
+            targetpath = lambda p: os.path.join(dest, os.path.basename(p))
+        else:
+            targetpath = lambda p: dest
+        for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
+            if okaytocopy(abssrc, relsrc, exact):
+                copy(abssrc, relsrc, targetpath(abssrc), exact)
+
+    if errors:
         ui.warn(_('(consider using --after)\n'))
-    return errs, copied
+    if len(copied) == 0:
+        raise util.Abort(_('no files to copy'))
+    return errors, copied
 
 def copy(ui, repo, *pats, **opts):
     """mark files as copied for the next commit