Mercurial > hg
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): |