comparison mercurial/scmutil.py @ 34543:6fad8059a970

scmutil: handle conflicting files and dirs in origbackuppath When ui.origbackuppath is set, .orig files are stored outside of the working copy. However conflicts can occur when files or directories end up having the same name. These conflicts cause Mercurial to abort, even if they've been created as a result of different backups. Make sure we always replace files or directories in the origbackuppath if they conflict with another file or directory. Test Plan: Add new unit test for conflicting paths. Differential Revision: https://phab.mercurial-scm.org/D680
author Mark Thomas <mbthomas@fb.com>
date Mon, 02 Oct 2017 14:05:30 -0700
parents 153e4e05e9b3
children 18309380fb88
comparison
equal deleted inserted replaced
34542:153e4e05e9b3 34543:6fad8059a970
36 pycompat, 36 pycompat,
37 revsetlang, 37 revsetlang,
38 similar, 38 similar,
39 url, 39 url,
40 util, 40 util,
41 vfs,
41 ) 42 )
42 43
43 if pycompat.osname == 'nt': 44 if pycompat.osname == 'nt':
44 from . import scmwindows as scmplatform 45 from . import scmwindows as scmplatform
45 else: 46 else:
571 572
572 Fetch user defined path from config file: [ui] origbackuppath = <path> 573 Fetch user defined path from config file: [ui] origbackuppath = <path>
573 Fall back to default (filepath with .orig suffix) if not specified 574 Fall back to default (filepath with .orig suffix) if not specified
574 ''' 575 '''
575 origbackuppath = ui.config('ui', 'origbackuppath') 576 origbackuppath = ui.config('ui', 'origbackuppath')
576 if origbackuppath is None: 577 if not origbackuppath:
577 return filepath + ".orig" 578 return filepath + ".orig"
578 579
579 filepathfromroot = os.path.relpath(filepath, start=repo.root) 580 # Convert filepath from an absolute path into a path inside the repo.
580 fullorigpath = repo.wjoin(origbackuppath, filepathfromroot) 581 filepathfromroot = util.normpath(os.path.relpath(filepath,
581 582 start=repo.root))
582 origbackupdir = repo.vfs.dirname(fullorigpath) 583
583 if not repo.vfs.exists(origbackupdir): 584 origvfs = vfs.vfs(repo.wjoin(origbackuppath))
584 ui.note(_('creating directory: %s\n') % origbackupdir) 585 origbackupdir = origvfs.dirname(filepathfromroot)
585 util.makedirs(origbackupdir) 586 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
586 587 ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir))
587 return fullorigpath 588
589 # Remove any files that conflict with the backup file's path
590 for f in reversed(list(util.finddirs(filepathfromroot))):
591 if origvfs.isfileorlink(f):
592 ui.note(_('removing conflicting file: %s\n')
593 % origvfs.join(f))
594 origvfs.unlink(f)
595 break
596
597 origvfs.makedirs(origbackupdir)
598
599 if origvfs.isdir(filepathfromroot):
600 ui.note(_('removing conflicting directory: %s\n')
601 % origvfs.join(filepathfromroot))
602 origvfs.rmtree(filepathfromroot, forcibly=True)
603
604 return origvfs.join(filepathfromroot)
588 605
589 class _containsnode(object): 606 class _containsnode(object):
590 """proxy __contains__(node) to container.__contains__ which accepts revs""" 607 """proxy __contains__(node) to container.__contains__ which accepts revs"""
591 608
592 def __init__(self, repo, revcontainer): 609 def __init__(self, repo, revcontainer):