181 * p1: revision number of first parent |
181 * p1: revision number of first parent |
182 * p2: revision number of first parent |
182 * p2: revision number of first parent |
183 * p1copies: mapping of copies from p1 |
183 * p1copies: mapping of copies from p1 |
184 * p2copies: mapping of copies from p2 |
184 * p2copies: mapping of copies from p2 |
185 * removed: a list of removed files |
185 * removed: a list of removed files |
|
186 * ismerged: a callback to know if file was merged in that revision |
186 """ |
187 """ |
187 cl = repo.changelog |
188 cl = repo.changelog |
188 parents = cl.parentrevs |
189 parents = cl.parentrevs |
|
190 |
|
191 def get_ismerged(rev): |
|
192 ctx = repo[rev] |
|
193 |
|
194 def ismerged(path): |
|
195 if path not in ctx.files(): |
|
196 return False |
|
197 fctx = ctx[path] |
|
198 parents = fctx._filelog.parents(fctx._filenode) |
|
199 nb_parents = 0 |
|
200 for n in parents: |
|
201 if n != node.nullid: |
|
202 nb_parents += 1 |
|
203 return nb_parents >= 2 |
|
204 |
|
205 return ismerged |
189 |
206 |
190 if repo.filecopiesmode == b'changeset-sidedata': |
207 if repo.filecopiesmode == b'changeset-sidedata': |
191 changelogrevision = cl.changelogrevision |
208 changelogrevision = cl.changelogrevision |
192 flags = cl.flags |
209 flags = cl.flags |
193 |
210 |
216 # time to save memory. |
233 # time to save memory. |
217 merge_caches = {} |
234 merge_caches = {} |
218 |
235 |
219 def revinfo(rev): |
236 def revinfo(rev): |
220 p1, p2 = parents(rev) |
237 p1, p2 = parents(rev) |
|
238 value = None |
221 if flags(rev) & REVIDX_SIDEDATA: |
239 if flags(rev) & REVIDX_SIDEDATA: |
222 e = merge_caches.pop(rev, None) |
240 e = merge_caches.pop(rev, None) |
223 if e is not None: |
241 if e is not None: |
224 return e |
242 return e |
225 c = changelogrevision(rev) |
243 c = changelogrevision(rev) |
226 p1copies = c.p1copies |
244 p1copies = c.p1copies |
227 p2copies = c.p2copies |
245 p2copies = c.p2copies |
228 removed = c.filesremoved |
246 removed = c.filesremoved |
229 if p1 != node.nullrev and p2 != node.nullrev: |
247 if p1 != node.nullrev and p2 != node.nullrev: |
230 # XXX some case we over cache, IGNORE |
248 # XXX some case we over cache, IGNORE |
231 merge_caches[rev] = (p1, p2, p1copies, p2copies, removed) |
249 value = merge_caches[rev] = ( |
|
250 p1, |
|
251 p2, |
|
252 p1copies, |
|
253 p2copies, |
|
254 removed, |
|
255 get_ismerged(rev), |
|
256 ) |
232 else: |
257 else: |
233 p1copies = {} |
258 p1copies = {} |
234 p2copies = {} |
259 p2copies = {} |
235 removed = [] |
260 removed = [] |
236 return p1, p2, p1copies, p2copies, removed |
261 |
|
262 if value is None: |
|
263 value = (p1, p2, p1copies, p2copies, removed, get_ismerged(rev)) |
|
264 return value |
237 |
265 |
238 else: |
266 else: |
239 |
267 |
240 def revinfo(rev): |
268 def revinfo(rev): |
241 p1, p2 = parents(rev) |
269 p1, p2 = parents(rev) |
242 ctx = repo[rev] |
270 ctx = repo[rev] |
243 p1copies, p2copies = ctx._copies |
271 p1copies, p2copies = ctx._copies |
244 removed = ctx.filesremoved() |
272 removed = ctx.filesremoved() |
245 return p1, p2, p1copies, p2copies, removed |
273 return p1, p2, p1copies, p2copies, removed, get_ismerged(rev) |
246 |
274 |
247 return revinfo |
275 return revinfo |
248 |
276 |
249 |
277 |
250 def _changesetforwardcopies(a, b, match): |
278 def _changesetforwardcopies(a, b, match): |
281 iterrevs = set(from_head) |
310 iterrevs = set(from_head) |
282 iterrevs &= mrset |
311 iterrevs &= mrset |
283 iterrevs.update(roots) |
312 iterrevs.update(roots) |
284 iterrevs.remove(b.rev()) |
313 iterrevs.remove(b.rev()) |
285 revs = sorted(iterrevs) |
314 revs = sorted(iterrevs) |
286 return _combinechangesetcopies(revs, children, b.rev(), revinfo, match) |
315 return _combinechangesetcopies( |
287 |
316 revs, children, b.rev(), revinfo, match, isancestor |
288 |
317 ) |
289 def _combinechangesetcopies(revs, children, targetrev, revinfo, match): |
318 |
|
319 |
|
320 def _combinechangesetcopies( |
|
321 revs, children, targetrev, revinfo, match, isancestor |
|
322 ): |
290 """combine the copies information for each item of iterrevs |
323 """combine the copies information for each item of iterrevs |
291 |
324 |
292 revs: sorted iterable of revision to visit |
325 revs: sorted iterable of revision to visit |
293 children: a {parent: [children]} mapping. |
326 children: a {parent: [children]} mapping. |
294 targetrev: the final copies destination revision (not in iterrevs) |
327 targetrev: the final copies destination revision (not in iterrevs) |
317 childcopies = { |
350 childcopies = { |
318 dst: src for dst, src in childcopies.items() if match(dst) |
351 dst: src for dst, src in childcopies.items() if match(dst) |
319 } |
352 } |
320 newcopies = copies |
353 newcopies = copies |
321 if childcopies: |
354 if childcopies: |
322 newcopies = _chain(newcopies, childcopies) |
355 newcopies = copies.copy() |
323 # _chain makes a copies, we can avoid doing so in some |
356 for dest, source in pycompat.iteritems(childcopies): |
324 # simple/linear cases. |
357 prev = copies.get(source) |
|
358 if prev is not None and prev[1] is not None: |
|
359 source = prev[1] |
|
360 newcopies[dest] = (c, source) |
325 assert newcopies is not copies |
361 assert newcopies is not copies |
326 for f in removed: |
362 for f in removed: |
327 if f in newcopies: |
363 if f in newcopies: |
328 if newcopies is copies: |
364 if newcopies is copies: |
329 # copy on write to avoid affecting potential other |
365 # copy on write to avoid affecting potential other |
330 # branches. when there are no other branches, this |
366 # branches. when there are no other branches, this |
331 # could be avoided. |
367 # could be avoided. |
332 newcopies = copies.copy() |
368 newcopies = copies.copy() |
333 del newcopies[f] |
369 newcopies[f] = (c, None) |
334 othercopies = all_copies.get(c) |
370 othercopies = all_copies.get(c) |
335 if othercopies is None: |
371 if othercopies is None: |
336 all_copies[c] = newcopies |
372 all_copies[c] = newcopies |
337 else: |
373 else: |
338 # we are the second parent to work on c, we need to merge our |
374 # we are the second parent to work on c, we need to merge our |
339 # work with the other. |
375 # work with the other. |
340 # |
376 # |
341 # Unlike when copies are stored in the filelog, we consider |
|
342 # it a copy even if the destination already existed on the |
|
343 # other branch. It's simply too expensive to check if the |
|
344 # file existed in the manifest. |
|
345 # |
|
346 # In case of conflict, parent 1 take precedence over parent 2. |
377 # In case of conflict, parent 1 take precedence over parent 2. |
347 # This is an arbitrary choice made anew when implementing |
378 # This is an arbitrary choice made anew when implementing |
348 # changeset based copies. It was made without regards with |
379 # changeset based copies. It was made without regards with |
349 # potential filelog related behavior. |
380 # potential filelog related behavior. |
350 if parent == 1: |
381 if parent == 1: |
351 othercopies.update(newcopies) |
382 _merge_copies_dict( |
|
383 othercopies, newcopies, isancestor, ismerged |
|
384 ) |
352 else: |
385 else: |
353 newcopies.update(othercopies) |
386 _merge_copies_dict( |
|
387 newcopies, othercopies, isancestor, ismerged |
|
388 ) |
354 all_copies[c] = newcopies |
389 all_copies[c] = newcopies |
355 return all_copies[targetrev] |
390 |
|
391 final_copies = {} |
|
392 for dest, (tt, source) in all_copies[targetrev].items(): |
|
393 if source is not None: |
|
394 final_copies[dest] = source |
|
395 return final_copies |
|
396 |
|
397 |
|
398 def _merge_copies_dict(minor, major, isancestor, ismerged): |
|
399 """merge two copies-mapping together, minor and major |
|
400 |
|
401 In case of conflict, value from "major" will be picked. |
|
402 |
|
403 - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an |
|
404 ancestors of `high_rev`, |
|
405 |
|
406 - `ismerged(path)`: callable return True if `path` have been merged in the |
|
407 current revision, |
|
408 """ |
|
409 for dest, value in major.items(): |
|
410 other = minor.get(dest) |
|
411 if other is None: |
|
412 minor[dest] = value |
|
413 else: |
|
414 new_tt = value[0] |
|
415 other_tt = other[0] |
|
416 if value[1] == other[1]: |
|
417 continue |
|
418 # content from "major" wins, unless it is older |
|
419 # than the branch point or there is a merge |
|
420 if ( |
|
421 new_tt == other_tt |
|
422 or not isancestor(new_tt, other_tt) |
|
423 or ismerged(dest) |
|
424 ): |
|
425 minor[dest] = value |
356 |
426 |
357 |
427 |
358 def _forwardcopies(a, b, base=None, match=None): |
428 def _forwardcopies(a, b, base=None, match=None): |
359 """find {dst@b: src@a} copy mapping where a is an ancestor of b""" |
429 """find {dst@b: src@a} copy mapping where a is an ancestor of b""" |
360 |
430 |