D680: scmutil: handle conflicting files and dirs in origbackuppath

mbthomas (Mark Thomas) phabricator at mercurial-scm.org
Mon Sep 11 18:03:31 UTC 2017


mbthomas created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  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.

REPOSITORY
  rHG Mercurial

BRANCH
  dirfixes (bookmark) on default (branch)

REVISION DETAIL
  https://phab.mercurial-scm.org/D680

AFFECTED FILES
  mercurial/scmutil.py
  tests/test-origbackup-conflict.t

CHANGE DETAILS

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,80 @@
+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 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  (leaving bookmark c1)
+  $ 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 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  (leaving bookmark c1)
+  $ 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 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  (leaving bookmark b1)
+  $ 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 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  (leaving bookmark b1)
+  $ 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
diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -563,10 +563,23 @@
     fullorigpath = repo.wjoin(origbackuppath, filepathfromroot)
 
     origbackupdir = repo.vfs.dirname(fullorigpath)
-    if not repo.vfs.exists(origbackupdir):
+    if not repo.vfs.isdir(origbackupdir):
         ui.note(_('creating directory: %s\n') % origbackupdir)
+
+        # Remove any files that conflict with the backup file's path
+        for f in util.finddirs(fullorigpath):
+            if repo.vfs.exists(f):
+                if repo.vfs.isfile(f):
+                    ui.note(_('removing conflicting file: %s\n') % f)
+                    repo.vfs.unlink(f)
+                break
+
         util.makedirs(origbackupdir)
 
+    if repo.vfs.isdir(fullorigpath):
+        ui.note(_('removing conflicting directory: %s\n') % fullorigpath)
+        repo.vfs.rmtree(fullorigpath, forcibly=True)
+
     return fullorigpath
 
 class _containsnode(object):



To: mbthomas, ryanmce, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list