comparison mercurial/merge.py @ 17889:ce7bc04d863b stable

icasefs: make case-folding collision detection as deletion aware (issue3648) Before this patch, case-folding collision is checked simply between manifests of each merged revisions. So, files may be considered as colliding each other, even though one of them is already deleted on one of merged branches: in such case, merge causes deleting it, so case-folding collision doesn't occur. This patch checks whether both of files colliding each other still remain after merge or not, and ignores collision if at least one of them is deleted by merge. In the case that one of colliding files is deleted on one of merged branches and changed on another, file is considered to still remain after merge, even though it may be deleted by merge, if "deleting" of it is chosen in "manifestmerge()". This avoids fail to merge by case-folding collisions after choices from "changing" and "deleting" of files. This patch adds only tests for "removed remotely" code paths in "_remains()", because other ones are tested by existing tests in "test-casecollision-merge.t".
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Wed, 31 Oct 2012 16:50:22 +0900
parents 98687cdddcb1
children 5881d5b7552f
comparison
equal deleted inserted replaced
17888:39b7052b217b 17889:ce7bc04d863b
97 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f) 97 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
98 if error: 98 if error:
99 raise util.Abort(_("untracked files in working directory differ " 99 raise util.Abort(_("untracked files in working directory differ "
100 "from files in requested revision")) 100 "from files in requested revision"))
101 101
102 def _checkcollision(mctx, wctx): 102 def _remains(f, m, ma, workingctx=False):
103 """check whether specified file remains after merge.
104
105 It is assumed that specified file is not contained in the manifest
106 of the other context.
107 """
108 if f in ma:
109 n = m[f]
110 if n != ma[f]:
111 return True # because it is changed locally
112 # even though it doesn't remain, if "remote deleted" is
113 # chosen in manifestmerge()
114 elif workingctx and n[20:] == "a":
115 return True # because it is added locally (linear merge specific)
116 else:
117 return False # because it is removed remotely
118 else:
119 return True # because it is added locally
120
121 def _checkcollision(mctx, extractxs):
103 "check for case folding collisions in the destination context" 122 "check for case folding collisions in the destination context"
104 folded = {} 123 folded = {}
105 for fn in mctx: 124 for fn in mctx:
106 fold = util.normcase(fn) 125 fold = util.normcase(fn)
107 if fold in folded: 126 if fold in folded:
108 raise util.Abort(_("case-folding collision between %s and %s") 127 raise util.Abort(_("case-folding collision between %s and %s")
109 % (fn, folded[fold])) 128 % (fn, folded[fold]))
110 folded[fold] = fn 129 folded[fold] = fn
111 130
112 if wctx: 131 if extractxs:
132 wctx, actx = extractxs
113 # class to delay looking up copy mapping 133 # class to delay looking up copy mapping
114 class pathcopies(object): 134 class pathcopies(object):
115 @util.propertycache 135 @util.propertycache
116 def map(self): 136 def map(self):
117 # {dst@mctx: src@wctx} copy mapping 137 # {dst@mctx: src@wctx} copy mapping
119 pc = pathcopies() 139 pc = pathcopies()
120 140
121 for fn in wctx: 141 for fn in wctx:
122 fold = util.normcase(fn) 142 fold = util.normcase(fn)
123 mfn = folded.get(fold, None) 143 mfn = folded.get(fold, None)
124 if mfn and mfn != fn and pc.map.get(mfn) != fn: 144 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
145 _remains(fn, wctx.manifest(), actx.manifest(), True) and
146 _remains(mfn, mctx.manifest(), actx.manifest())):
125 raise util.Abort(_("case-folding collision between %s and %s") 147 raise util.Abort(_("case-folding collision between %s and %s")
126 % (mfn, fn)) 148 % (mfn, fn))
127 149
128 def _forgetremoved(wctx, mctx, branchmerge): 150 def _forgetremoved(wctx, mctx, branchmerge):
129 """ 151 """
593 # collision check is not needed for clean update 615 # collision check is not needed for clean update
594 if (not branchmerge and 616 if (not branchmerge and
595 (force or not wc.dirty(missing=True, branch=False))): 617 (force or not wc.dirty(missing=True, branch=False))):
596 _checkcollision(p2, None) 618 _checkcollision(p2, None)
597 else: 619 else:
598 _checkcollision(p2, wc) 620 _checkcollision(p2, (wc, pa))
599 if not force: 621 if not force:
600 _checkunknown(repo, wc, p2) 622 _checkunknown(repo, wc, p2)
601 action += _forgetremoved(wc, p2, branchmerge) 623 action += _forgetremoved(wc, p2, branchmerge)
602 action += manifestmerge(repo, wc, p2, pa, overwrite, partial) 624 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
603 625