[RFC] make copy & rename behave like cp -r & mv
Robin Farine
robin.farine at terminus.org
Fri Sep 23 20:26:25 CDT 2005
Hi,
The patch below changes docopy(), the code common to the copy and
rename commands, so that these commands behave like the UNIX cp -r
and mv commands respectively. The patch also removes the commands'
--parents option whose functionality, I believe, can be reproduced
with the proposed modification.
If there is any interest in this patch, I am willing to provide test
cases for it.
Thanks,
Robin
# HG changeset patch
# User Robin Farine <robin.farine at terminus.org>
# Node ID df39d966d2a4a939ea475461aeb22244bb86a408
# Parent 8c094fb47b59234088c549a9bcf8deb9316c0d7b
Make copy & rename behave like cp -r & mv.
diff -r 8c094fb47b59 -r df39d966d2a4 doc/hg.1.txt
--- a/doc/hg.1.txt Fri Sep 23 17:19:35 2005 -0700
+++ b/doc/hg.1.txt Sat Sep 24 02:57:11 2005 +0200
@@ -201,7 +201,6 @@
-I, --include <pat> include names matching the given patterns
-X, --exclude <pat> exclude names matching the given patterns
-f, --force forcibly copy over an existing managed file
- -p, --parents append source path to dest
aliases: cp
@@ -493,7 +492,6 @@
Options:
-A, --after record a rename that has already occurred
-f, --force forcibly copy over an existing managed file
- -p, --parents append source path to dest
aliases: mv
diff -r 8c094fb47b59 -r df39d966d2a4 mercurial/commands.py
--- a/mercurial/commands.py Fri Sep 23 17:19:35 2005 -0700
+++ b/mercurial/commands.py Sat Sep 24 02:57:11 2005 +0200
@@ -701,82 +701,90 @@
raise util.Abort(str(inst))
def docopy(ui, repo, pats, opts):
+ cwd = repo.getcwd()
+
+ def gensources(pats):
+ reasons = {'?': 'is not managed',
+ 'a': 'has been marked for add'}
+ roots, matchfn, anypats = matchpats(repo, cwd, pats, opts)
+ numsrcs = 0
+ srcmap = {}
+ for prefix in roots:
+ srcs = []
+ for src, fn in repo.walk(files=[prefix], match=matchfn):
+ rel = util.pathto(cwd, fn)
+ exact = fn == prefix
+ reason = reasons.get(repo.dirstate.state(fn))
+ if reason:
+ if exact:
+ ui.warn('%s: not copying - file %s\n' % (rel, reason))
+ else:
+ numsrcs += 1
+ srcs.append((fn, rel, exact))
+ if srcs:
+ srcmap[prefix] = srcs
+ return numsrcs, srcmap
+
+
if not pats:
raise util.Abort('no source or destination specified')
- elif len(pats) == 1:
+ if len(pats) == 1:
raise util.Abort('no destination specified')
pats = list(pats)
dest = pats.pop()
- sources = []
-
- def okaytocopy(abs, rel, exact):
- reasons = {'?': 'is not managed',
- 'a': 'has been marked for add'}
- reason = reasons.get(repo.dirstate.state(abs))
- if reason:
- if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
- 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 = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
-
- if destisfile:
- if opts['parents']:
- raise util.Abort('with --parents, destination must be a directory')
- elif len(sources) > 1:
- raise util.Abort('with multiple sources, destination must be a '
- 'directory')
+ numsrcs, sources = gensources(pats)
+ destisdir = os.path.isdir(reldest)
+ if numsrcs == 0:
+ raise util.Abort('no files to copy')
+ if len(sources) != 1 and not destisdir:
+ raise util.Abort('with multiple sources, destination must be an '
+ 'existing directory')
+ destisfile = numsrcs == 1 and not destisdir
+
errs, copied = 0, []
- for abs, rel, exact in sources:
- if opts['parents']:
- mydest = os.path.join(dest, rel)
- elif destisfile:
- mydest = reldest
+ for srcpfx in sources.keys():
+ if os.path.isdir(util.pathto(cwd, srcpfx)) and not destisdir:
+ striplen = len(srcpfx)
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 '.'
- if not opts['after']:
- try:
- if opts['parents']: 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)
- n = repo.manifest.tip()
- mf = repo.manifest.readflags(n)
- util.set_exec(myreldest, util.is_exec(rel, mf[abs]))
- 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)
- else:
- ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
- errs += 1
+ striplen = len(os.path.split(srcpfx)[0])
+ if striplen:
+ striplen += len(os.sep)
+ for abs, rel, exact in sources[srcpfx]:
+ if destisfile:
+ reltarget = reldest
+ abstarget = absdest
+ else:
+ suffix = abs[striplen:]
+ reltarget = os.path.join(reldest, suffix)
+ abstarget = os.path.join(absdest, suffix)
+ if not opts['force'] and repo.dirstate.state(abstarget) not in 'a?':
+ ui.warn('%s: not overwriting - file already managed\n' %
+ reltarget)
continue
- repo.copy(abs, myabsdest)
- copied.append((abs, rel, exact))
+ if ui.verbose or not exact:
+ ui.status('copying %s to %s\n' % (rel, reltarget))
+ if not opts['after']:
+ targetdir = os.path.dirname(reltarget) or '.'
+ if not os.path.isdir(targetdir):
+ os.makedirs(targetdir)
+ try:
+ shutil.copyfile(rel, reltarget)
+ n = repo.manifest.tip()
+ mf = repo.manifest.readflags(n)
+ util.set_exec(reltarget, util.is_exec(rel, mf[abs]))
+ 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)
+ else:
+ ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
+ errs += 1
+ continue
+ repo.copy(abs, abstarget)
+ copied.append((abs, rel, exact))
if errs:
ui.warn('(consider using --after)\n')
return errs, copied
@@ -1788,8 +1796,7 @@
[('I', 'include', [], 'include path in search'),
('X', 'exclude', [], 'exclude path from search'),
('A', 'after', None, 'record a copy after it has happened'),
- ('f', 'force', None, 'replace destination if it exists'),
- ('p', 'parents', None, 'append source path to dest')],
+ ('f', 'force', None, 'replace destination if it exists')],
'hg copy [OPTION]... [SOURCE]... DEST'),
"debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
"debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
@@ -1902,8 +1909,7 @@
[('I', 'include', [], 'include path in search'),
('X', 'exclude', [], 'exclude path from search'),
('A', 'after', None, 'record a copy after it has happened'),
- ('f', 'force', None, 'replace destination if it exists'),
- ('p', 'parents', None, 'append source path to dest')],
+ ('f', 'force', None, 'replace destination if it exists')],
'hg rename [OPTION]... [SOURCE]... DEST'),
"^revert":
(revert,
More information about the Mercurial
mailing list