diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -35,6 +35,7 @@ revsetlang, similar, util, + vfs, ) if pycompat.osname == 'nt': @@ -556,18 +557,34 @@ Fall back to default (filepath with .orig suffix) if not specified ''' origbackuppath = ui.config('ui', 'origbackuppath') - if origbackuppath is None: + if not origbackuppath: return filepath + ".orig" - filepathfromroot = os.path.relpath(filepath, start=repo.root) - fullorigpath = repo.wjoin(origbackuppath, filepathfromroot) + # Convert filepath from an absolute path into a path inside the repo. + filepathfromroot = util.normpath(os.path.relpath(filepath, + start=repo.root)) + + origvfs = vfs.vfs(repo.wjoin(origbackuppath)) + origbackupdir = origvfs.dirname(filepathfromroot) + if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir): + ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir)) - origbackupdir = repo.vfs.dirname(fullorigpath) - if not repo.vfs.exists(origbackupdir): - ui.note(_('creating directory: %s\n') % origbackupdir) - util.makedirs(origbackupdir) + # Remove any files that conflict with the backup file's path + for f in reversed(list(util.finddirs(filepathfromroot))): + if origvfs.isfileorlink(f): + ui.note(_('removing conflicting file: %s\n') + % origvfs.join(f)) + origvfs.unlink(f) + break - return fullorigpath + origvfs.makedirs(origbackupdir) + + if origvfs.isdir(filepathfromroot): + ui.note(_('removing conflicting directory: %s\n') + % origvfs.join(filepathfromroot)) + origvfs.rmtree(filepathfromroot, forcibly=True) + + return origvfs.join(filepathfromroot) class _containsnode(object): """proxy __contains__(node) to container.__contains__ which accepts revs""" diff --git a/tests/test-origbackup-conflict.t b/tests/test-origbackup-conflict.t new file mode 100644 --- /dev/null +++ b/tests/test-origbackup-conflict.t @@ -0,0 +1,118 @@ +Set up repo + + $ cat << EOF >> $HGRCPATH + > [ui] + > origbackuppath=.hg/origbackups + > [merge] + > checkunknown=warn + > EOF + $ hg init repo + $ cd repo + $ echo base > base + $ hg add base + $ hg commit -m "base" + +Make a dir named b that contains a file + + $ mkdir -p b + $ echo c1 > b/c + $ hg add b/c + $ hg commit -m "c1" + $ hg bookmark c1 + +Peform an update that causes b/c to be backed up + + $ hg up -q 0 + $ mkdir -p b + $ echo c2 > b/c + $ hg up --verbose c1 + resolving manifests + b/c: replacing untracked file + getting b/c + creating directory: $TESTTMP/repo/.hg/origbackups/b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark c1) + $ test -f .hg/origbackups/b/c + +Make a file named b + + $ hg up -q 0 + $ echo b1 > b + $ hg add b + $ hg commit -m b1 + created new head + $ hg bookmark b1 + +Perform an update that causes b to be backed up - it should replace the backup b dir + + $ hg up -q 0 + $ echo b2 > b + $ hg up --verbose b1 + resolving manifests + b: replacing untracked file + getting b + removing conflicting directory: $TESTTMP/repo/.hg/origbackups/b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark b1) + $ test -f .hg/origbackups/b + +Perform an update the causes b/c to be backed up again - it should replace the backup b file + + $ hg up -q 0 + $ mkdir b + $ echo c3 > b/c + $ hg up --verbose c1 + resolving manifests + b/c: replacing untracked file + getting b/c + creating directory: $TESTTMP/repo/.hg/origbackups/b + removing conflicting file: $TESTTMP/repo/.hg/origbackups/b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark c1) + $ test -d .hg/origbackups/b + +Cause a symlink to be backed up that points to a valid location from the backup dir + + $ hg up -q 0 + $ mkdir ../sym-link-target + $ ln -s ../../../sym-link-target b + $ hg up b1 + b: replacing untracked file + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark b1) + $ readlink .hg/origbackups/b + ../../../sym-link-target + +Perform an update that causes b/c to be backed up again - it should not go into the target dir + + $ hg up -q 0 + $ mkdir b + $ echo c4 > b/c + $ hg up --verbose c1 + resolving manifests + b/c: replacing untracked file + getting b/c + creating directory: $TESTTMP/repo/.hg/origbackups/b + removing conflicting file: $TESTTMP/repo/.hg/origbackups/b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark c1) + $ cat .hg/origbackups/b/c + c4 + $ ls ../sym-link-target + +Incorrectly configure origbackuppath to be under a file + + $ echo data > .hg/badorigbackups + $ hg up -q 0 + $ mkdir b + $ echo c5 > b/c + $ hg up --verbose c1 --config ui.origbackuppath=.hg/badorigbackups + resolving manifests + b/c: replacing untracked file + getting b/c + creating directory: $TESTTMP/repo/.hg/badorigbackups/b + abort: Not a directory: '$TESTTMP/repo/.hg/badorigbackups/b' + [255] + $ cat .hg/badorigbackups + data +