mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 15:27:13 +03:00
generalize copy/rename to handle more than one source directory
This commit is contained in:
parent
aed1cbf84d
commit
7eb6f65c8b
@ -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
60
tests/test-rename
Normal 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
93
tests/test-rename.out
Normal 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
|
Loading…
Reference in New Issue
Block a user