copy/rename to a removed destination file

When the destination of a copy or rename operation has been
marked for removal, we need to restore it before we overwrite
it with the new content. This also handles the case of
idempotent renames, i.e.

    hg rename "a" "b"
    hg rename "b" "a"
This commit is contained in:
Robin Farine 2006-02-28 23:47:40 -08:00
parent 296526d4db
commit 954a141878
3 changed files with 42 additions and 12 deletions

View File

@ -825,7 +825,8 @@ def commit(ui, repo, *pats, **opts):
except ValueError, inst:
raise util.Abort(str(inst))
def docopy(ui, repo, pats, opts):
def docopy(ui, repo, pats, opts, wlock):
# called with the repo lock held
cwd = repo.getcwd()
errors = 0
copied = []
@ -871,8 +872,16 @@ def docopy(ui, repo, pats, opts):
if not os.path.isdir(targetdir):
os.makedirs(targetdir)
try:
shutil.copyfile(relsrc, reltarget)
shutil.copymode(relsrc, reltarget)
restore = repo.dirstate.state(abstarget) == 'r'
if restore:
repo.undelete([abstarget], wlock)
try:
shutil.copyfile(relsrc, reltarget)
shutil.copymode(relsrc, reltarget)
restore = False
finally:
if restore:
repo.remove([abstarget], wlock)
except shutil.Error, inst:
raise util.Abort(str(inst))
except IOError, inst:
@ -886,7 +895,8 @@ def docopy(ui, repo, pats, opts):
if ui.verbose or not exact:
ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
targets[abstarget] = abssrc
repo.copy(origsrc, abstarget)
if abstarget != origsrc:
repo.copy(origsrc, abstarget, wlock)
copied.append((abssrc, relsrc, exact))
def targetpathfn(pat, dest, srcs):
@ -994,7 +1004,12 @@ def copy(ui, repo, *pats, **opts):
should properly record copied files, this information is not yet
fully used by merge, nor fully reported by log.
"""
errs, copied = docopy(ui, repo, pats, opts)
try:
wlock = repo.wlock(0)
errs, copied = docopy(ui, repo, pats, opts, wlock)
except lock.LockHeld, inst:
ui.warn(_("repository lock held by %s\n") % inst.args[0])
errs = 1
return errs
def debugancestor(ui, index, rev1, rev2):
@ -1953,13 +1968,18 @@ def rename(ui, repo, *pats, **opts):
should properly record rename files, this information is not yet
fully used by merge, nor fully reported by log.
"""
errs, copied = docopy(ui, repo, pats, opts)
names = []
for abs, rel, exact in copied:
if ui.verbose or not exact:
ui.status(_('removing %s\n') % rel)
names.append(abs)
repo.remove(names, unlink=True)
try:
wlock = repo.wlock(0)
errs, copied = docopy(ui, repo, pats, opts, wlock)
names = []
for abs, rel, exact in copied:
if ui.verbose or not exact:
ui.status(_('removing %s\n') % rel)
names.append(abs)
repo.remove(names, True, wlock)
except lock.LockHeld, inst:
ui.warn(_("repository lock held by %s\n") % inst.args[0])
errs = 1
return errs
def revert(ui, repo, *pats, **opts):

View File

@ -171,3 +171,11 @@ mv d1/bb d1/bc
hg rename --after d1/bb d1/bc
hg status
hg update -C
echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
hg rename d1/b d1/bb
echo "some stuff added to d1/bb" >> d1/bb
hg rename d1/bb d1/b
hg status
hg debugstate | grep copy
hg update -C

View File

@ -252,3 +252,5 @@ R d1/b
# transitive rename --after
A d1/bc
R d1/b
# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
M d1/b