[PATCH] merge: detect conflict between added file and directory (issue29)

Evgeniy Makeev evgeniym at fb.com
Tue Oct 9 15:28:17 CDT 2012


# HG changeset patch
# User Evgeniy Makeev <evgeniym at fb.com>
# Date 1349739050 25200
# Node ID 072243d45be07be438736f125baa6a2381dd53c4
# Parent  3c775c5a6c03ed010ce2b85dd59968c63afc77b6
merge: detect conflict between added file and directory (issue29)

The fix checks for conflicts between files being added by merge/update and
local directories during manifest processing phase. If there is
a conflict, user will be prompted to either abort the operation or skip
adding the conflicting files if the conflicting local directory is not
empty. If the conflicting local directory is empty, user will be prompted
either to remove the empty directory or abort.

diff -r 3c775c5a6c03 -r 072243d45be0 mercurial/merge.py
--- a/mercurial/merge.py	Mon Oct 08 17:50:42 2012 -0500
+++ b/mercurial/merge.py	Mon Oct 08 16:30:50 2012 -0700
@@ -282,7 +282,38 @@
                 act("remote differs from untracked local",
                     "m", f, f, f, rflags, False)
             else:
-                act("remote created", "g", f, m2.flags(f))
+                dirpath = repo.wjoin(f)
+                if os.path.isdir(dirpath):
+                # check if the added remote file collides with existing dir
+                    if f in p1.dirs(): # existing non-empty dir
+                        if not repo.ui.promptchoice(
+                            _("cannot merge file %s, existing directory %s is"
+                              " in the way\nwould you like to (A)bort operation"
+                              " or (S)kip writing the file? [a]")
+                              % (f, dirpath), (_("&Abort"), _("&Skip")), 0):
+                            raise util.Abort(
+                                _("cannot write file %s conflicting with local"
+                                  " directory %s") % (f, repo.wjoin(f)))
+                    elif len(os.listdir(dirpath)) == 0: # empty dir, can remove 
+                        if repo.ui.promptchoice(
+                            _("trying to replace empty local directory %s"
+                              " with file %s\nwould you like to (R)emove "
+                              "the empty directory or (A)bort operation? [r]")
+                              % (dirpath, f), (_("&Remove"), _("&Abort")), 0):
+                            raise util.Abort(
+                                _("cannot write file %s, remove conflicting empty "
+                                  "directory %s or rename the file")
+                                  % (f, dirpath))
+                        else:
+                            os.rmdir(dirpath)
+                            act("remote created", "g", f, m2.flags(f))
+                    else:
+                        raise util.Abort(
+                            _("file %s conflicts with local directory %s")
+                            % (f, dirpath))
+                else:
+                    act("remote created", "g", f, m2.flags(f))
+
         elif n != ma[f]:
             if repo.ui.promptchoice(
                 _("remote changed %s which local deleted\n"
diff -r 3c775c5a6c03 -r 072243d45be0 tests/test-merge8.t
--- a/tests/test-merge8.t	Mon Oct 08 17:50:42 2012 -0500
+++ b/tests/test-merge8.t	Mon Oct 08 16:30:50 2012 -0700
@@ -1,5 +1,6 @@
-Test for changeset ba7c74081861
-(update dirstate correctly for non-branchmerge updates)
+Test for changeset ba7c74081861 (update dirstate correctly for non-branchmerge updates):
+and issue29 - http://mercurial.selenic.com/bts/issue29
+
   $ hg init a
   $ cd a
   $ echo a > a
@@ -26,4 +27,74 @@
   $ hg update
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 
-  $ cd ..
+issue29 test
+  $ cd ../a
+  $ mkdir dir1
+  $ mkdir dir1/dir11
+  $ echo deepf > dir1/dir11/deepf
+  $ hg ci -Am m
+  adding dir1/dir11/deepf
+  $ cd ../b
+  $ hg pull -u ../a
+  pulling from ../a
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd ../a
+  $ mkdir emptydir
+  $ mkdir dir
+  $ echo afile > dir/afile
+  $ hg ci -Am m
+  adding dir/afile
+  $ cd ../b
+  $ touch dir
+  $ hg ci -Am m
+  adding dir
+  $ hg pull ../a
+  pulling from ../a
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg merge
+  abort: Not a directory: $TESTTMP/b/dir/afile
+  [255]
+  $ cd ../a
+  $ hg pull ../b
+  pulling from ../b
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg merge
+  cannot merge file dir, existing directory $TESTTMP/a/dir is in the way
+  would you like to (A)bort operation or (S)kip writing the file? [a] a
+  abort: cannot write file dir conflicting with local directory $TESTTMP/a/dir
+  [255]
+  $ cd ../b
+  $ rm dir
+  $ touch emptydir
+  $ hg ci -Am m
+  removing dir
+  adding emptydir
+  $ cd ../a
+  $ hg pull ../b
+  pulling from ../b
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  (run 'hg update' to get a working copy)
+  $ hg merge
+  trying to replace empty local directory $TESTTMP/a/emptydir with file emptydir
+  would you like to (R)emove the empty directory or (A)bort operation? [r] r
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)


More information about the Mercurial-devel mailing list