generalize copy/rename to handle more than one source directory

This commit is contained in:
Robin Farine 2005-11-08 10:35:05 -08:00
parent aed1cbf84d
commit 7eb6f65c8b
3 changed files with 209 additions and 67 deletions

View File

@ -787,14 +787,9 @@ 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 = []
def okaytocopy(abs, rel, exact):
reasons = {'?': _('is not managed'),
@ -805,74 +800,68 @@ 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 '.'
if not opts['after']:
try:
if dir2dir: os.makedirs(mydestdir)
elif not destisfile: os.mkdir(mydestdir)
except OSError, inst:
if inst.errno != errno.EEXIST: raise
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') % (rel, myreldest))
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:
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

60
tests/test-rename Normal file
View File

@ -0,0 +1,60 @@
#!/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

93
tests/test-rename.out Normal file
View File

@ -0,0 +1,93 @@
# 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 already managed
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