D680: scmutil: handle conflicting files and dirs in origbackuppath

mbthomas (Mark Thomas) phabricator at mercurial-scm.org
Tue Sep 19 06:19:33 EDT 2017


mbthomas updated this revision to Diff 1894.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D680?vs=1893&id=1894

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,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
+
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"""



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


More information about the Mercurial-devel mailing list