220 copy = {} |
220 copy = {} |
221 movewithdir = {} |
221 movewithdir = {} |
222 fullcopy = {} |
222 fullcopy = {} |
223 diverge = {} |
223 diverge = {} |
224 |
224 |
225 def related(f1, f2, limit): |
225 def _checkcopies(f, m1, m2): |
226 # Walk back to common ancestor to see if the two files originate |
226 checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy) |
227 # from the same file. Since workingfilectx's rev() is None it messes |
|
228 # up the integer comparison logic, hence the pre-step check for |
|
229 # None (f1 and f2 can only be workingfilectx's initially). |
|
230 |
|
231 if f1 == f2: |
|
232 return f1 # a match |
|
233 |
|
234 g1, g2 = f1.ancestors(), f2.ancestors() |
|
235 try: |
|
236 f1r, f2r = f1.rev(), f2.rev() |
|
237 |
|
238 if f1r is None: |
|
239 f1 = g1.next() |
|
240 if f2r is None: |
|
241 f2 = g2.next() |
|
242 |
|
243 while True: |
|
244 f1r, f2r = f1.rev(), f2.rev() |
|
245 if f1r > f2r: |
|
246 f1 = g1.next() |
|
247 elif f2r > f1r: |
|
248 f2 = g2.next() |
|
249 elif f1 == f2: |
|
250 return f1 # a match |
|
251 elif f1r == f2r or f1r < limit or f2r < limit: |
|
252 return False # copy no longer relevant |
|
253 except StopIteration: |
|
254 return False |
|
255 |
|
256 def checkcopies(f, m1, m2): |
|
257 '''check possible copies of f from m1 to m2''' |
|
258 of = None |
|
259 seen = set([f]) |
|
260 for oc in ctx(f, m1[f]).ancestors(): |
|
261 ocr = oc.rev() |
|
262 of = oc.path() |
|
263 if of in seen: |
|
264 # check limit late - grab last rename before |
|
265 if ocr < limit: |
|
266 break |
|
267 continue |
|
268 seen.add(of) |
|
269 |
|
270 fullcopy[f] = of # remember for dir rename detection |
|
271 if of not in m2: |
|
272 continue # no match, keep looking |
|
273 if m2[of] == ma.get(of): |
|
274 break # no merge needed, quit early |
|
275 c2 = ctx(of, m2[of]) |
|
276 cr = related(oc, c2, ca.rev()) |
|
277 if cr and (of == f or of == c2.path()): # non-divergent |
|
278 copy[f] = of |
|
279 of = None |
|
280 break |
|
281 |
|
282 if of in ma: |
|
283 diverge.setdefault(of, []).append(f) |
|
284 |
227 |
285 repo.ui.debug(" searching for copies back to rev %d\n" % limit) |
228 repo.ui.debug(" searching for copies back to rev %d\n" % limit) |
286 |
229 |
287 u1 = _nonoverlap(m1, m2, ma) |
230 u1 = _nonoverlap(m1, m2, ma) |
288 u2 = _nonoverlap(m2, m1, ma) |
231 u2 = _nonoverlap(m2, m1, ma) |
293 if u2: |
236 if u2: |
294 repo.ui.debug(" unmatched files in other:\n %s\n" |
237 repo.ui.debug(" unmatched files in other:\n %s\n" |
295 % "\n ".join(u2)) |
238 % "\n ".join(u2)) |
296 |
239 |
297 for f in u1: |
240 for f in u1: |
298 checkcopies(f, m1, m2) |
241 _checkcopies(f, m1, m2) |
299 for f in u2: |
242 for f in u2: |
300 checkcopies(f, m2, m1) |
243 _checkcopies(f, m2, m1) |
301 |
244 |
302 renamedelete = {} |
245 renamedelete = {} |
303 renamedelete2 = set() |
246 renamedelete2 = set() |
304 diverge2 = set() |
247 diverge2 = set() |
305 for of, fl in diverge.items(): |
248 for of, fl in diverge.items(): |
384 repo.ui.debug((" pending file src: '%s' -> " |
327 repo.ui.debug((" pending file src: '%s' -> " |
385 "dst: '%s'\n") % (f, df)) |
328 "dst: '%s'\n") % (f, df)) |
386 break |
329 break |
387 |
330 |
388 return copy, movewithdir, diverge, renamedelete |
331 return copy, movewithdir, diverge, renamedelete |
|
332 |
|
333 def checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy): |
|
334 """ |
|
335 check possible copies of f from m1 to m2 |
|
336 |
|
337 ctx = function accepting (filename, node) that returns a filectx. |
|
338 f = the filename to check |
|
339 m1 = the source manifest |
|
340 m2 = the destination manifest |
|
341 ca = the changectx of the common ancestor |
|
342 limit = the rev number to not search beyond |
|
343 diverge = record all diverges in this dict |
|
344 copy = record all non-divergent copies in this dict |
|
345 fullcopy = record all copies in this dict |
|
346 """ |
|
347 |
|
348 ma = ca.manifest() |
|
349 |
|
350 def _related(f1, f2, limit): |
|
351 # Walk back to common ancestor to see if the two files originate |
|
352 # from the same file. Since workingfilectx's rev() is None it messes |
|
353 # up the integer comparison logic, hence the pre-step check for |
|
354 # None (f1 and f2 can only be workingfilectx's initially). |
|
355 |
|
356 if f1 == f2: |
|
357 return f1 # a match |
|
358 |
|
359 g1, g2 = f1.ancestors(), f2.ancestors() |
|
360 try: |
|
361 f1r, f2r = f1.rev(), f2.rev() |
|
362 |
|
363 if f1r is None: |
|
364 f1 = g1.next() |
|
365 if f2r is None: |
|
366 f2 = g2.next() |
|
367 |
|
368 while True: |
|
369 f1r, f2r = f1.rev(), f2.rev() |
|
370 if f1r > f2r: |
|
371 f1 = g1.next() |
|
372 elif f2r > f1r: |
|
373 f2 = g2.next() |
|
374 elif f1 == f2: |
|
375 return f1 # a match |
|
376 elif f1r == f2r or f1r < limit or f2r < limit: |
|
377 return False # copy no longer relevant |
|
378 except StopIteration: |
|
379 return False |
|
380 |
|
381 of = None |
|
382 seen = set([f]) |
|
383 for oc in ctx(f, m1[f]).ancestors(): |
|
384 ocr = oc.rev() |
|
385 of = oc.path() |
|
386 if of in seen: |
|
387 # check limit late - grab last rename before |
|
388 if ocr < limit: |
|
389 break |
|
390 continue |
|
391 seen.add(of) |
|
392 |
|
393 fullcopy[f] = of # remember for dir rename detection |
|
394 if of not in m2: |
|
395 continue # no match, keep looking |
|
396 if m2[of] == ma.get(of): |
|
397 break # no merge needed, quit early |
|
398 c2 = ctx(of, m2[of]) |
|
399 cr = _related(oc, c2, ca.rev()) |
|
400 if cr and (of == f or of == c2.path()): # non-divergent |
|
401 copy[f] = of |
|
402 of = None |
|
403 break |
|
404 |
|
405 if of in ma: |
|
406 diverge.setdefault(of, []).append(f) |