D677: scmutil: handle conflicting files and dirs in origbackuppath

mbthomas (Mark Thomas) phabricator at mercurial-scm.org
Mon Sep 11 15:55:20 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 sets of backup.
  
  This is made worse by the .orig suffix that is appended to file names as they
  are backed up.  A file named x can conflict with a directory named x.orig from
  a different backup, or perhaps even the same backup.
  
  This change removes the .orig suffix from files stored in an out-of-tree
  origbackuppath, and deals with the case where files and directories replace
  each other in the backup directory.

TEST PLAN
  Re-run unit tests.  Add new unit test for conflicting paths.

REPOSITORY
  rHG Mercurial

BRANCH
  dirfixes (bookmark) on default (branch)

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

AFFECTED FILES
  mercurial/help/config.txt
  mercurial/scmutil.py
  tests/test-largefiles-misc.t
  tests/test-merge-local.t
  tests/test-mq-qpush-fail.t
  tests/test-mq.t
  tests/test-origbackup-conflict.t
  tests/test-resolve.t
  tests/test-revert.t
  tests/test-shelve.t
  tests/test-subrepo-git.t

CHANGE DETAILS

diff --git a/tests/test-subrepo-git.t b/tests/test-subrepo-git.t
--- a/tests/test-subrepo-git.t
+++ b/tests/test-subrepo-git.t
@@ -885,9 +885,9 @@
   $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
   reverting subrepo ../gitroot
   creating directory: $TESTTMP/tc/.hg/origbackups (glob)
-  saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar.orig (glob)
+  saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar (glob)
   $ ls .hg/origbackups
-  foobar.orig
+  foobar
   $ rm -rf .hg/origbackups
 
 show file at specific revision
diff --git a/tests/test-shelve.t b/tests/test-shelve.t
--- a/tests/test-shelve.t
+++ b/tests/test-shelve.t
@@ -1260,7 +1260,7 @@
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
   [1]
   $ ls .hg/origbackups
-  root.orig
+  root
   $ rm -rf .hg/origbackups
 
 test Abort unshelve always gets user out of the unshelved state
diff --git a/tests/test-revert.t b/tests/test-revert.t
--- a/tests/test-revert.t
+++ b/tests/test-revert.t
@@ -92,7 +92,7 @@
   $ echo z > e
   $ hg revert --all -v --config 'ui.origbackuppath=.hg/origbackups'
   creating directory: $TESTTMP/repo/.hg/origbackups (glob)
-  saving current version of e as $TESTTMP/repo/.hg/origbackups/e.orig (glob)
+  saving current version of e as $TESTTMP/repo/.hg/origbackups/e
   reverting e
   $ rm -rf .hg/origbackups
 
diff --git a/tests/test-resolve.t b/tests/test-resolve.t
--- a/tests/test-resolve.t
+++ b/tests/test-resolve.t
@@ -255,8 +255,8 @@
   warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
   [1]
   $ ls .hg/origbackups
-  file1.orig
-  file2.orig
+  file1
+  file2
   $ grep '<<<' file1 > /dev/null
   $ grep '<<<' file2 > /dev/null
 
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/tests/test-mq.t b/tests/test-mq.t
--- a/tests/test-mq.t
+++ b/tests/test-mq.t
@@ -1388,7 +1388,7 @@
   $ hg qpush -f --verbose --config 'ui.origbackuppath=.hg/origbackups'
   applying empty
   creating directory: $TESTTMP/forcepush/.hg/origbackups (glob)
-  saving current version of hello.txt as $TESTTMP/forcepush/.hg/origbackups/hello.txt.orig (glob)
+  saving current version of hello.txt as $TESTTMP/forcepush/.hg/origbackups/hello.txt (glob)
   patching file hello.txt
   committing files:
   hello.txt
@@ -1422,7 +1422,7 @@
 test that the previous call to qpush with -f (--force) and --config actually put
 the orig files out of the working copy
   $ ls .hg/origbackups
-  hello.txt.orig
+  hello.txt
 
 test popping revisions not in working dir ancestry
 
diff --git a/tests/test-mq-qpush-fail.t b/tests/test-mq-qpush-fail.t
--- a/tests/test-mq-qpush-fail.t
+++ b/tests/test-mq-qpush-fail.t
@@ -465,7 +465,7 @@
 test previous qpop (with --force and --config) saved .orig files to where user
 wants them
   $ ls .hg/origbackups
-  b.orig
+  b
   $ rm -rf .hg/origbackups
 
   $ cd ..
diff --git a/tests/test-merge-local.t b/tests/test-merge-local.t
--- a/tests/test-merge-local.t
+++ b/tests/test-merge-local.t
@@ -110,7 +110,7 @@
 
 Are orig files from the last commit where we want them?
   $ ls .hg/origbackups
-  zzz2_merge_bad.orig
+  zzz2_merge_bad
 
   $ hg diff --nodates | grep "^[+-][^<>]"
   --- a/zzz1_merge_ok
diff --git a/tests/test-largefiles-misc.t b/tests/test-largefiles-misc.t
--- a/tests/test-largefiles-misc.t
+++ b/tests/test-largefiles-misc.t
@@ -528,13 +528,13 @@
   $ echo moremore >> anotherlarge
   $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
   creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub (glob)
-  saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge.orig (glob)
+  saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge (glob)
   reverting ../.hglf/sub/anotherlarge (glob)
   creating directory: $TESTTMP/addrm2/.hg/origbackups/sub (glob)
   found 90c622cf65cebe75c5842f9136c459333faf392e in store
   found 90c622cf65cebe75c5842f9136c459333faf392e in store
   $ ls ../.hg/origbackups/sub
-  anotherlarge.orig
+  anotherlarge
   $ cd ..
 
 Test glob logging from the root dir
diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -553,7 +553,7 @@
     '''customize where .orig files are created
 
     Fetch user defined path from config file: [ui] origbackuppath = <path>
-    Fall back to default (filepath) if not specified
+    Fall back to default (filepath with .orig suffix) if not specified
     '''
     origbackuppath = ui.config('ui', 'origbackuppath')
     if origbackuppath is None:
@@ -563,11 +563,24 @@
     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)
 
-    return fullorigpath + ".orig"
+    if repo.vfs.isdir(fullorigpath):
+        ui.note(_('removing conflicting directory: %s\n') % fullorigpath)
+        repo.vfs.rmtree(fullorigpath, forcibly=True)
+
+    return fullorigpath
 
 class _containsnode(object):
     """proxy __contains__(node) to container.__contains__ which accepts revs"""
diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -2007,7 +2007,9 @@
 
 ``origbackuppath``
     The path to a directory used to store generated .orig files. If the path is
-    not a directory, one will be created.
+    not a directory, one will be created.  If set, files stored in this
+    directory have the same name as the original file and do not have a .orig
+    suffix.
 
 ``paginate``
   Control the pagination of command output (default: True). See :hg:`help pager`



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


More information about the Mercurial-devel mailing list