comparison mercurial/merge.py @ 18134:6c35b53cd28b

copies: separate moves via directory renames from explicit copies Currently the "copy" dict contains both explicit copies/moves made by a context and pending moves that need to happen because the other context moved the directory the file was in. For explicit copies, the dict stores a destination to source map, while for pending moves via directory renames, it stores a source to destination map. The merge code uses this fact in a non- obvious way to differentiate between these two cases. We make this explicit by storing these pending moves in a separate dict. The dict still has a source to destination map, but that is called out in the docstring.
author Siddharth Agarwal <sid0@fb.com>
date Wed, 26 Dec 2012 14:50:17 -0800
parents 551e2901192e
children 242d2f4ec01c
comparison
equal deleted inserted replaced
18133:7f5a0eba3768 18134:6c35b53cd28b
211 211
212 def act(msg, m, f, *args): 212 def act(msg, m, f, *args):
213 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) 213 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
214 action.append((f, m) + args) 214 action.append((f, m) + args)
215 215
216 action, copy = [], {} 216 action, copy, movewithdir = [], {}, {}
217 217
218 if overwrite: 218 if overwrite:
219 pa = p1 219 pa = p1
220 elif pa == p2: # backwards 220 elif pa == p2: # backwards
221 pa = p1.p1() 221 pa = p1.p1()
222 elif pa and repo.ui.configbool("merge", "followcopies", True): 222 elif pa and repo.ui.configbool("merge", "followcopies", True):
223 copy, diverge, renamedelete = copies.mergecopies(repo, p1, p2, pa) 223 ret = copies.mergecopies(repo, p1, p2, pa)
224 copy, movewithdir, diverge, renamedelete = ret
224 for of, fl in diverge.iteritems(): 225 for of, fl in diverge.iteritems():
225 act("divergent renames", "dr", of, fl) 226 act("divergent renames", "dr", of, fl)
226 for of, fl in renamedelete.iteritems(): 227 for of, fl in renamedelete.iteritems():
227 act("rename and delete", "rd", of, fl) 228 act("rename and delete", "rd", of, fl)
228 229
231 % (bool(overwrite), bool(partial))) 232 % (bool(overwrite), bool(partial)))
232 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2)) 233 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
233 234
234 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest() 235 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
235 copied = set(copy.values()) 236 copied = set(copy.values())
237 copied.update(movewithdir.values())
236 238
237 if '.hgsubstate' in m1: 239 if '.hgsubstate' in m1:
238 # check whether sub state is modified 240 # check whether sub state is modified
239 for s in p1.substate: 241 for s in p1.substate:
240 if p1.sub(s).dirty(): 242 if p1.sub(s).dirty():
257 act("remote is newer", "g", f, rflags) 259 act("remote is newer", "g", f, rflags)
258 else: # both changed 260 else: # both changed
259 act("versions differ", "m", f, f, f, rflags, False) 261 act("versions differ", "m", f, f, f, rflags, False)
260 elif f in copied: # files we'll deal with on m2 side 262 elif f in copied: # files we'll deal with on m2 side
261 pass 263 pass
262 elif f in copy: 264 elif f in movewithdir: # directory rename
265 f2 = movewithdir[f]
266 act("remote renamed directory to " + f2, "d", f, None, f2,
267 m1.flags(f))
268 elif f in copy: # case 2 A,B/B/B or case 4,21 A/B/B
263 f2 = copy[f] 269 f2 = copy[f]
264 if f2 not in m2: # directory rename 270 act("local copied/moved to " + f2, "m", f, f2, f,
265 act("remote renamed directory to " + f2, "d", 271 fmerge(f, f2, f2), False)
266 f, None, f2, m1.flags(f))
267 else: # case 2 A,B/B/B or case 4,21 A/B/B
268 act("local copied/moved to " + f2, "m",
269 f, f2, f, fmerge(f, f2, f2), False)
270 elif f in ma: # clean, a different, no remote 272 elif f in ma: # clean, a different, no remote
271 if n != ma[f]: 273 if n != ma[f]:
272 if repo.ui.promptchoice( 274 if repo.ui.promptchoice(
273 _(" local changed %s which remote deleted\n" 275 _(" local changed %s which remote deleted\n"
274 "use (c)hanged version or (d)elete?") % f, 276 "use (c)hanged version or (d)elete?") % f,
284 for f, n in m2.iteritems(): 286 for f, n in m2.iteritems():
285 if partial and not partial(f): 287 if partial and not partial(f):
286 continue 288 continue
287 if f in m1 or f in copied: # files already visited 289 if f in m1 or f in copied: # files already visited
288 continue 290 continue
289 if f in copy: 291 if f in movewithdir:
292 f2 = movewithdir[f]
293 act("local renamed directory to " + f2, "d", None, f, f2,
294 m2.flags(f))
295 elif f in copy:
290 f2 = copy[f] 296 f2 = copy[f]
291 if f2 not in m1: # directory rename 297 if f2 in m2: # rename case 1, A/A,B/A
292 act("local renamed directory to " + f2, "d",
293 None, f, f2, m2.flags(f))
294 elif f2 in m2: # rename case 1, A/A,B/A
295 act("remote copied to " + f, "m", 298 act("remote copied to " + f, "m",
296 f2, f, f, fmerge(f2, f, f2), False) 299 f2, f, f, fmerge(f2, f, f2), False)
297 else: # case 3,20 A/B/A 300 else: # case 3,20 A/B/A
298 act("remote moved to " + f, "m", 301 act("remote moved to " + f, "m",
299 f2, f, f, fmerge(f2, f, f2), True) 302 f2, f, f, fmerge(f2, f, f2), True)