# HG changeset patch # User Matt Mackall # Date 1131569525 28800 # Node ID 5b19dea9d4fdec823d671cdfd29e65c4895d2b69 # Parent 0b1b029b4de36228073eb9a23215276fb3d7c8f2# Parent ac4ca6bf23836a4f9ec6ba318f6843e3404681f4 Merge with TAH diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -788,14 +788,10 @@ def commit(ui, repo, *pats, **opts): 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 @@ def docopy(ui, repo, pats, opts): 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 @@ def recover(ui, repo): 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 diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -175,7 +175,7 @@ class dirstate: 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 @@ class dirstate: # 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 @@ class dirstate: 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: diff --git a/mercurial/hgweb.py b/mercurial/hgweb.py --- a/mercurial/hgweb.py +++ b/mercurial/hgweb.py @@ -165,7 +165,6 @@ class templater: 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, diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -43,7 +43,7 @@ class localrepository: 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 @@ class localrepository: 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() diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -784,6 +784,10 @@ class revlog: 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 diff --git a/templates/changelogentry-rss.tmpl b/templates/changelogentry-rss.tmpl --- a/templates/changelogentry-rss.tmpl +++ b/templates/changelogentry-rss.tmpl @@ -1,5 +1,5 @@ - #desc|strip|firstline|rstrip|escape# + #desc|strip|firstline|strip|escape# #url#?cs=#node|short# #author|obfuscate# diff --git a/templates/filelogentry-rss.tmpl b/templates/filelogentry-rss.tmpl --- a/templates/filelogentry-rss.tmpl +++ b/templates/filelogentry-rss.tmpl @@ -1,5 +1,5 @@ - #desc|strip|firstline|rstrip|escape# + #desc|strip|firstline|strip|escape# #url#?f=#filenode|short#;file=#file# #author|obfuscate# diff --git a/tests/test-help.out b/tests/test-help.out --- a/tests/test-help.out +++ b/tests/test-help.out @@ -191,6 +191,8 @@ show changed files in the working direct R = removed ? = not tracked +aliases: st + options: -m --modified show only modified files diff --git a/tests/test-rename b/tests/test-rename new file mode 100755 --- /dev/null +++ b/tests/test-rename @@ -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 diff --git a/tests/test-rename.out b/tests/test-rename.out new file mode 100644 --- /dev/null +++ b/tests/test-rename.out @@ -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