fix rename --after
authorRobin Farine <robin.farine@terminus.org>
Thu, 01 Dec 2005 10:48:35 -0600
changeset 1565 4bcbc126b80b
parent 1564 34579a67fa71
child 1566 8befbb4e30b2
fix rename --after
mercurial/commands.py
tests/test-rename
tests/test-rename.out
--- a/mercurial/commands.py	Thu Dec 01 10:48:29 2005 -0600
+++ b/mercurial/commands.py	Thu Dec 01 10:48:35 2005 -0600
@@ -821,16 +821,18 @@
             ui.warn(_('%s: not overwriting - %s collides with %s\n') %
                     (reltarget, abssrc, prevsrc))
             return
-        elif os.path.exists(reltarget):
-            if opts['force']:
-                os.unlink(reltarget)
-            else:
+        if (not opts['after'] and os.path.exists(reltarget) or
+            opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
+            if not opts['force']:
                 ui.warn(_('%s: not overwriting - file exists\n') %
                         reltarget)
                 return
-        if ui.verbose or not exact:
-            ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
-        if not opts['after']:
+            if not opts['after']:
+                os.unlink(reltarget)
+        if opts['after']:
+            if not os.path.exists(reltarget):
+                return
+        else:
             targetdir = os.path.dirname(reltarget) or '.'
             if not os.path.isdir(targetdir):
                 os.makedirs(targetdir)
@@ -847,10 +849,64 @@
                             (relsrc, inst.strerror))
                     errors += 1
                     return
+        if ui.verbose or not exact:
+            ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
         targets[abstarget] = abssrc
         repo.copy(abssrc, abstarget)
         copied.append((abssrc, relsrc, exact))
 
+    def targetpathfn(pat, dest, srcs):
+        if os.path.isdir(pat):
+            if pat.endswith(os.sep):
+                pat = pat[:-len(os.sep)]
+            if destdirexists:
+                striplen = len(os.path.split(pat)[0])
+            else:
+                striplen = len(pat)
+            if striplen:
+                striplen += len(os.sep)
+            res = lambda p: os.path.join(dest, p[striplen:])
+        elif destdirexists:
+            res = lambda p: os.path.join(dest, os.path.basename(p))
+        else:
+            res = lambda p: dest
+        return res
+
+    def targetpathafterfn(pat, dest, srcs):
+        if util.patkind(pat, None)[0]:
+            # a mercurial pattern
+            res = lambda p: os.path.join(dest, os.path.basename(p))
+        elif len(util.canonpath(repo.root, cwd, pat)) < len(srcs[0][0]):
+            # A directory. Either the target path contains the last
+            # component of the source path or it does not.
+            def evalpath(striplen):
+                score = 0
+                for s in srcs:
+                    t = os.path.join(dest, s[1][striplen:])
+                    if os.path.exists(t):
+                        score += 1
+                return score
+
+            if pat.endswith(os.sep):
+                pat = pat[:-len(os.sep)]
+            striplen = len(pat) + len(os.sep)
+            if os.path.isdir(os.path.join(dest, os.path.split(pat)[1])):
+                score = evalpath(striplen)
+                striplen1 = len(os.path.split(pat)[0])
+                if striplen1:
+                    striplen1 += len(os.sep)
+                if evalpath(striplen1) > score:
+                    striplen = striplen1
+            res = lambda p: os.path.join(dest, p[striplen:])
+        else:
+            # a file
+            if destdirexists:
+                res = lambda p: os.path.join(dest, os.path.basename(p))
+            else:
+                res = lambda p: dest
+        return res
+
+
     pats = list(pats)
     if not pats:
         raise util.Abort(_('no source or destination specified'))
@@ -858,31 +914,31 @@
         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:
+    if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
         raise util.Abort(_('with multiple sources, destination must be an '
                          'existing directory'))
-
+    if opts['after']:
+        tfn = targetpathafterfn
+    else:
+        tfn = targetpathfn
+    copylist = []
     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
+        srcs = []
         for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
             if okaytocopy(abssrc, relsrc, exact):
-                copy(abssrc, relsrc, targetpath(abssrc), exact)
+                srcs.append((abssrc, relsrc, exact))
+        if not srcs:
+            continue
+        copylist.append((tfn(pat, dest, srcs), srcs))
+    if not copylist:
+        raise util.Abort(_('no files to copy'))
+
+    for targetpath, srcs in copylist:
+        for abssrc, relsrc, exact in srcs:
+            copy(abssrc, relsrc, targetpath(relsrc), exact)
 
     if errors:
         ui.warn(_('(consider using --after)\n'))
-    if len(copied) == 0:
-        raise util.Abort(_('no files to copy'))
     return errors, copied
 
 def copy(ui, repo, *pats, **opts):
--- a/tests/test-rename	Thu Dec 01 10:48:29 2005 -0600
+++ b/tests/test-rename	Thu Dec 01 10:48:35 2005 -0600
@@ -15,18 +15,56 @@
 hg status
 hg update -C
 
+echo "# rename --after a single file"
+mv d1/d11/a1 d2/c
+hg rename --after d1/d11/a1 d2/c
+hg status
+hg update -C
+
 echo "# move a single file to an existing directory"
 hg rename d1/d11/a1 d2
 hg status
 hg update -C
 
+echo "# move --after a single file to an existing directory"
+mv d1/d11/a1 d2
+hg rename --after d1/d11/a1 d2
+hg status
+hg update -C
+
+echo "# rename a file using a relative path"
+(cd d1/d11; hg rename ../../d2/b e)
+hg status
+hg update -C
+
+echo "# rename --after a file using a relative path"
+(cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
+hg status
+hg update -C
+
 echo "# rename directory d1 as d3"
-hg rename d1 d3
+hg rename d1/ d3
+hg status
+hg update -C
+
+echo "# rename --after directory d1 as d3"
+mv d1 d3
+hg rename --after d1 d3
+hg status
+hg update -C
+
+echo "# move a directory using a relative path"
+(cd d2; mkdir d3; hg rename ../d1/d11 d3)
+hg status
+hg update -C
+
+echo "# move --after a directory using a relative path"
+(cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
 hg status
 hg update -C
 
 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
-hg rename d1/d11 d2
+hg rename d1/d11/ d2
 hg status
 hg update -C
 
@@ -36,6 +74,13 @@
 hg status
 hg update -C
 
+echo "# move --after directories d1 and d2 to a new directory d3"
+mkdir d3
+mv d1 d2 d3
+hg rename --after d1 d2 d3
+hg status
+hg update -C
+
 echo "# move everything under directory d1 to existing directory d2, do not"
 echo "# overwrite existing files (d2/b)"
 hg rename d1/* d2
@@ -53,6 +98,13 @@
 hg status
 hg update -C
 
+echo "# move --after some files under d1 to d2/d21 (glob)"
+mkdir d2/d21
+mv d1/a d1/d11/a1 d2/d21
+hg rename --after 'glob:d1/**' d2/d21
+hg status
+hg update -C
+
 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
 mkdir d2/d21
 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
--- a/tests/test-rename.out	Thu Dec 01 10:48:29 2005 -0600
+++ b/tests/test-rename.out	Thu Dec 01 10:48:35 2005 -0600
@@ -1,9 +1,21 @@
 # rename a single file
 A d2/c
 R d1/d11/a1
+# rename --after a single file
+A d2/c
+R d1/d11/a1
 # move a single file to an existing directory
 A d2/a1
 R d1/d11/a1
+# move --after a single file to an existing directory
+A d2/a1
+R d1/d11/a1
+# rename a file using a relative path
+A d1/d11/e
+R d2/b
+# rename --after a file using a relative path
+A d1/d11/e
+R d2/b
 # rename directory d1 as d3
 copying d1/a to d3/a
 copying d1/b to d3/b
@@ -21,6 +33,33 @@
 R d1/b
 R d1/ba
 R d1/d11/a1
+# rename --after directory d1 as d3
+copying d1/a to d3/a
+copying d1/b to d3/b
+copying d1/ba to d3/ba
+copying d1/d11/a1 to d3/d11/a1
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+A d3/a
+A d3/b
+A d3/ba
+A d3/d11/a1
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+# move a directory using a relative path
+copying ../d1/d11/a1 to d3/d11/a1
+removing ../d1/d11/a1
+A d2/d3/d11/a1
+R d1/d11/a1
+# move --after a directory using a relative path
+copying ../d1/d11/a1 to d3/d11/a1
+removing ../d1/d11/a1
+A d2/d3/d11/a1
+R d1/d11/a1
 # move directory d1/d11 to an existing directory d2 (removes empty d1)
 copying d1/d11/a1 to d2/d11/a1
 removing d1/d11/a1
@@ -47,6 +86,27 @@
 R d1/ba
 R d1/d11/a1
 R d2/b
+# move --after directories d1 and d2 to a new directory d3
+copying d1/a to d3/d1/a
+copying d1/b to d3/d1/b
+copying d1/ba to d3/d1/ba
+copying d1/d11/a1 to d3/d1/d11/a1
+copying d2/b to d3/d2/b
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+removing d2/b
+A d3/d1/a
+A d3/d1/b
+A d3/d1/ba
+A d3/d1/d11/a1
+A d3/d2/b
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+R d2/b
 # move everything under directory d1 to existing directory d2, do not
 # overwrite existing files (d2/b)
 d2/b: not overwriting - file exists
@@ -82,6 +142,15 @@
 R d1/b
 R d1/ba
 R d1/d11/a1
+# move --after some files under d1 to d2/d21 (glob)
+copying d1/a to d2/d21/a
+copying d1/d11/a1 to d2/d21/a1
+removing d1/a
+removing d1/d11/a1
+A d2/d21/a
+A d2/d21/a1
+R d1/a
+R d1/d11/a1
 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
 copying d1/a to d2/d21/a
 copying d1/d11/a1 to d2/d21/a1
@@ -93,7 +162,6 @@
 R d1/d11/a1
 # attempt to overwrite an existing file
 d1/ca: not overwriting - file exists
-abort: no files to copy
 ? d1/ca
 # forced overwrite of an existing file
 A d1/ca