changeset 1519:5b19dea9d4fd

Merge with TAH
author Matt Mackall <mpm@selenic.com>
date Wed, 09 Nov 2005 12:52:05 -0800
parents 0b1b029b4de3 (diff) ac4ca6bf2383 (current diff)
children 95ee4f12fbd9 11a58d2cdffb
files mercurial/commands.py
diffstat 10 files changed, 284 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/commands.py	Mon Nov 07 19:00:51 2005 +0100
+++ b/mercurial/commands.py	Wed Nov 09 12:52:05 2005 -0800
@@ -788,14 +788,10 @@
         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 = []
+    targets = {}
 
     def okaytocopy(abs, rel, exact):
         reasons = {'?': _('is not managed'),
@@ -806,74 +802,77 @@
         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)
+        prevsrc = targets.get(abstarget)
+        if prevsrc is not None:
+            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:
+                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']:
+            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
+        targets[abstarget] = abssrc
+        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
@@ -1734,7 +1733,9 @@
     This command tries to fix the repository status after an interrupted
     operation. It should only be necessary when Mercurial suggests it.
     """
-    repo.recover()
+    if repo.recover():
+        return repo.verify()
+    return False
 
 def remove(ui, repo, pat, *pats, **opts):
     """remove the specified files on the next commit
--- a/mercurial/dirstate.py	Mon Nov 07 19:00:51 2005 +0100
+++ b/mercurial/dirstate.py	Wed Nov 09 12:52:05 2005 -0800
@@ -175,7 +175,7 @@
             if state == "r":
                 self.map[f] = ('r', 0, 0, 0)
             else:
-                s = os.lstat(os.path.join(self.root, f))
+                s = os.lstat(self.wjoin(f))
                 st_size = kw.get('st_size', s.st_size)
                 st_mtime = kw.get('st_mtime', s.st_mtime)
                 self.map[f] = (state, s.st_mode, st_size, st_mtime)
@@ -332,7 +332,7 @@
         # step one, find all files that match our criteria
         files.sort()
         for ff in util.unique(files):
-            f = os.path.join(self.root, ff)
+            f = self.wjoin(ff)
             try:
                 st = os.lstat(f)
             except OSError, inst:
@@ -380,7 +380,7 @@
                 nonexistent = True
                 if not st:
                     try:
-                        f = os.path.join(self.root, fn)
+                        f = self.wjoin(fn)
                         st = os.lstat(f)
                     except OSError, inst:
                         if inst.errno != errno.ENOENT:
--- a/mercurial/hgweb.py	Mon Nov 07 19:00:51 2005 +0100
+++ b/mercurial/hgweb.py	Wed Nov 09 12:52:05 2005 -0800
@@ -165,7 +165,6 @@
 common_filters = {
     "escape": cgi.escape,
     "strip": lambda x: x.strip(),
-    "rstrip": lambda x: x.rstrip(),
     "age": age,
     "date": lambda x: util.datestr(x),
     "addbreaks": nl2br,
--- a/mercurial/localrepo.py	Mon Nov 07 19:00:51 2005 +0100
+++ b/mercurial/localrepo.py	Wed Nov 09 12:52:05 2005 -0800
@@ -43,7 +43,7 @@
 
         self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
         try:
-            self.ui.readconfig(os.path.join(self.path, "hgrc"))
+            self.ui.readconfig(self.join("hgrc"))
         except IOError: pass
 
     def hook(self, name, **args):
@@ -225,9 +225,11 @@
         lock = self.lock()
         if os.path.exists(self.join("journal")):
             self.ui.status(_("rolling back interrupted transaction\n"))
-            return transaction.rollback(self.opener, self.join("journal"))
+            transaction.rollback(self.opener, self.join("journal"))
+            return True
         else:
             self.ui.warn(_("no interrupted transaction available\n"))
+            return False
 
     def undo(self):
         lock = self.lock()
--- a/mercurial/revlog.py	Mon Nov 07 19:00:51 2005 +0100
+++ b/mercurial/revlog.py	Wed Nov 09 12:52:05 2005 -0800
@@ -784,6 +784,10 @@
                 continue
             delta = chunk[80:]
 
+            for p in (p1, p2):
+                if not p in self.nodemap:
+                    raise RevlogError(_("unknown parent %s") % short(p1))
+
             if not chain:
                 # retrieve the parent revision of the delta chain
                 chain = p1
--- a/templates/changelogentry-rss.tmpl	Mon Nov 07 19:00:51 2005 +0100
+++ b/templates/changelogentry-rss.tmpl	Wed Nov 09 12:52:05 2005 -0800
@@ -1,5 +1,5 @@
 <item>
-    <title>#desc|strip|firstline|rstrip|escape#</title>
+    <title>#desc|strip|firstline|strip|escape#</title>
     <link>#url#?cs=#node|short#</link>
     <description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
     <author>#author|obfuscate#</author>
--- a/templates/filelogentry-rss.tmpl	Mon Nov 07 19:00:51 2005 +0100
+++ b/templates/filelogentry-rss.tmpl	Wed Nov 09 12:52:05 2005 -0800
@@ -1,5 +1,5 @@
 <item>
-    <title>#desc|strip|firstline|rstrip|escape#</title>
+    <title>#desc|strip|firstline|strip|escape#</title>
     <link>#url#?f=#filenode|short#;file=#file#</link>
     <description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
     <author>#author|obfuscate#</author>
--- a/tests/test-help.out	Mon Nov 07 19:00:51 2005 +0100
+++ b/tests/test-help.out	Wed Nov 09 12:52:05 2005 -0800
@@ -191,6 +191,8 @@
     R = removed
     ? = not tracked
 
+aliases: st
+
 options:
 
  -m --modified   show only modified files
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rename	Wed Nov 09 12:52:05 2005 -0800
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+hg init
+mkdir d1 d1/d11 d2
+echo d1/a > d1/a
+echo d1/ba > d1/ba
+echo d1/a1 > d1/d11/a1
+echo d1/b > d1/b
+echo d2/b > d2/b
+hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
+hg commit -m "1" -d "0 0"
+
+echo "# rename a single file"
+hg rename 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 "# rename directory d1 as d3"
+hg rename d1 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 status
+hg update -C
+
+echo "# move directories d1 and d2 to a new directory d3"
+mkdir d3
+hg rename 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
+hg status
+diff d1/b d2/b
+hg update -C
+
+echo "# attempt to move potentially more than one file into a non-existent"
+echo "# directory"
+hg rename 'glob:d1/**' dx
+
+echo "# move every file under d1 to d2/d21 (glob)"
+mkdir d2/d21
+hg rename '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
+hg status
+hg update -C
+
+echo "# attempt to overwrite an existing file"
+echo "ca" > d1/ca
+hg rename d1/ba d1/ca
+hg status
+hg update -C
+
+echo "# forced overwrite of an existing file"
+echo "ca" > d1/ca
+hg rename --force d1/ba d1/ca
+hg status
+hg update -C
+
+echo "# replace a symlink with a file"
+ln -s ba d1/ca
+hg rename --force d1/ba d1/ca
+hg status
+hg update -C
+
+echo "# do not copy more than one source file to the same destination file"
+mkdir d3
+hg rename d1/* d2/* d3
+hg status
+hg update -C
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rename.out	Wed Nov 09 12:52:05 2005 -0800
@@ -0,0 +1,115 @@
+# rename 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
+# rename 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 directory d1/d11 to an existing directory d2 (removes empty d1)
+copying d1/d11/a1 to d2/d11/a1
+removing d1/d11/a1
+A d2/d11/a1
+R d1/d11/a1
+# move 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
+copying d1/d11/a1 to d2/d11/a1
+removing d1/d11/a1
+A d2/a
+A d2/ba
+A d2/d11/a1
+R d1/a
+R d1/ba
+R d1/d11/a1
+1c1
+< d1/b
+---
+> d2/b
+# attempt to move potentially more than one file into a non-existent
+# directory
+abort: with multiple sources, destination must be an existing directory
+# move every file under d1 to d2/d21 (glob)
+copying d1/a to d2/d21/a
+copying d1/b to d2/d21/b
+copying d1/ba to d2/d21/ba
+copying d1/d11/a1 to d2/d21/a1
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+A d2/d21/a
+A d2/d21/a1
+A d2/d21/b
+A d2/d21/ba
+R d1/a
+R d1/b
+R d1/ba
+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
+removing d1/a
+removing d1/d11/a1
+A d2/d21/a
+A d2/d21/a1
+R d1/a
+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
+R d1/ba
+# replace a symlink with a file
+A d1/ca
+R d1/ba
+# do not copy more than one source file to the same destination file
+copying d1/d11/a1 to d3/d11/a1
+d3/b: not overwriting - d2/b collides with d1/b
+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