[PATCH] merge: detect conflict between path of added file and a local file (issue29)
Evgeniy Makeev
evgeniym at fb.com
Tue Oct 9 16:18:45 CDT 2012
# HG changeset patch
# User Evgeniy Makeev <evgeniym at fb.com>
# Date 1349754802 25200
# Node ID 227411b21482b7ee80a85628989957716eb3659b
# Parent 072243d45be07be438736f125baa6a2381dd53c4
merge: detect conflict between path of added file and a local file (issue29)
The fix checks for conflicts between local files and directories being
merged/added, if there is a conflict, user is prompted to abbort or
skip merging/adding all files wich have the conflicting directories
in their paths. The check is done by building a directory set off all
partial paths of added files.
diff -r 072243d45be0 -r 227411b21482 mercurial/merge.py
--- a/mercurial/merge.py Mon Oct 08 16:30:50 2012 -0700
+++ b/mercurial/merge.py Mon Oct 08 20:53:22 2012 -0700
@@ -259,6 +259,7 @@
else:
act("other deleted", "r", f)
+ newdirset = set()
for f, n in m2.iteritems():
if partial and not partial(f):
continue
@@ -286,11 +287,13 @@
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(
+ if 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):
+ continue
+ else:
raise util.Abort(
_("cannot write file %s conflicting with local"
" directory %s") % (f, repo.wjoin(f)))
@@ -301,18 +304,25 @@
"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")
+ _("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))
+
+ act("remote created", "g", f, m2.flags(f))
+ # find all dirs from f's path, OK to reuse f string here
+ pathpos = f.rfind('/')
+ while pathpos != -1:
+ f = f[:pathpos]
+ if f in newdirset:
+ break # newdirset already contains this and above
+ newdirset.add(f)
+ pathpos = f.rfind('/')
elif n != ma[f]:
if repo.ui.promptchoice(
@@ -320,7 +330,33 @@
"use (c)hanged version or leave (d)eleted?") % f,
(_("&Changed"), _("&Deleted")), 0) == 0:
act("prompt recreating", "g", f, m2.flags(f))
-
+ for mdir in newdirset:
+ # verify if a remote dirs conflicts with an existing file
+ # everything inside the following if block only executes on error
+ if mdir in m1 and os.path.isfile(repo.wjoin(mdir)):
+ # collission - expand error message/recovery option
+ confiles = [fn for fn in p2 if fn.find(mdir+'/') == 0]
+ confilesstr = ', '.join(confiles)
+ if len(confiles) <= 1:
+ promptmsg = _("cannot write %s, local file %s conflicts with"
+ " its path\nwould you like to (A)bort operation or"
+ " (S)kip file %s? [a]")
+ abortmsg = _("local file %s conflicts with paths of files %s"
+ " to be written")
+ else:
+ promptmsg = _("cannot write %s\nlocal file %s conflicts with"
+ " their paths\nwould you like to (A)bort operation"
+ " or\n(S)kip files %s? [a]")
+ abortmsg = _("local file %s conflicts with paths of files %s"
+ " to be written")
+ if repo.ui.promptchoice(promptmsg
+ % (confilesstr, repo.wjoin(mdir), confilesstr),
+ (_("&Abort"), _("&Skip")), 0):
+ # remove conflicting file additions from action
+ action = [a for a in action
+ if a[1] != 'g' or a[0].find(mdir) != 0]
+ else:
+ raise util.Abort(abortmsg % (repo.wjoin(mdir), confilesstr))
return action
def actionkey(a):
diff -r 072243d45be0 -r 227411b21482 tests/test-merge8.t
--- a/tests/test-merge8.t Mon Oct 08 16:30:50 2012 -0700
+++ b/tests/test-merge8.t Mon Oct 08 20:53:22 2012 -0700
@@ -45,14 +45,15 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../a
$ mkdir emptydir
- $ mkdir dir
- $ echo afile > dir/afile
+ $ mkdir dirorfile
+ $ mkdir dirorfile/cdir
+ $ echo cfile > dirorfile/cdir/cfile
$ hg ci -Am m
- adding dir/afile
+ adding dirorfile/cdir/cfile
$ cd ../b
- $ touch dir
+ $ touch dirorfile
$ hg ci -Am m
- adding dir
+ adding dirorfile
$ hg pull ../a
pulling from ../a
searching for changes
@@ -62,7 +63,9 @@
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
+ cannot write dirorfile/cdir/cfile, local file $TESTTMP/b/dirorfile conflicts with its path
+ would you like to (A)bort operation or (S)kip file dirorfile/cdir/cfile? [a] a
+ abort: local file $TESTTMP/b/dirorfile conflicts with paths of files dirorfile/cdir/cfile to be written
[255]
$ cd ../a
$ hg pull ../b
@@ -74,15 +77,15 @@
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
+ cannot merge file dirorfile, existing directory $TESTTMP/a/dirorfile 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
+ abort: cannot write file dirorfile conflicting with local directory $TESTTMP/a/dirorfile
[255]
$ cd ../b
- $ rm dir
+ $ rm dirorfile
$ touch emptydir
$ hg ci -Am m
- removing dir
+ removing dirorfile
adding emptydir
$ cd ../a
$ hg pull ../b
More information about the Mercurial-devel
mailing list