194 children[p] = [r] |
194 children[p] = [r] |
195 else: |
195 else: |
196 children[p].append(r) |
196 children[p].append(r) |
197 |
197 |
198 roots = set(children) - set(missingrevs) |
198 roots = set(children) - set(missingrevs) |
199 # 'work' contains 3-tuples of a (revision number, parent number, copies). |
199 work = list(roots) |
200 # The parent number is only used for knowing which parent the copies dict |
200 all_copies = {r: {} for r in roots} |
201 # came from. |
|
202 # NOTE: To reduce costly copying the 'copies' dicts, we reuse the same |
|
203 # instance for *one* of the child nodes (the last one). Once an instance |
|
204 # has been put on the queue, it is thus no longer safe to modify it. |
|
205 # Conversely, it *is* safe to modify an instance popped off the queue. |
|
206 work = [(r, 1, {}) for r in roots] |
|
207 heapq.heapify(work) |
201 heapq.heapify(work) |
208 alwaysmatch = match.always() |
202 alwaysmatch = match.always() |
209 while work: |
203 while work: |
210 r, i1, copies = heapq.heappop(work) |
204 r = heapq.heappop(work) |
211 if work and work[0][0] == r: |
205 copies = all_copies.pop(r) |
212 # We are tracing copies from both parents |
|
213 r, i2, copies2 = heapq.heappop(work) |
|
214 for dst, src in copies2.items(): |
|
215 # Unlike when copies are stored in the filelog, we consider |
|
216 # it a copy even if the destination already existed on the |
|
217 # other branch. It's simply too expensive to check if the |
|
218 # file existed in the manifest. |
|
219 if dst not in copies: |
|
220 # If it was copied on the p1 side, leave it as copied from |
|
221 # that side, even if it was also copied on the p2 side. |
|
222 copies[dst] = copies2[dst] |
|
223 if r == b.rev(): |
206 if r == b.rev(): |
224 return copies |
207 return copies |
225 for i, c in enumerate(children[r]): |
208 for i, c in enumerate(children[r]): |
226 childctx = repo[c] |
209 childctx = repo[c] |
227 if r == childctx.p1().rev(): |
210 if r == childctx.p1().rev(): |
243 if childcopies: |
226 if childcopies: |
244 newcopies = _chain(newcopies, childcopies) |
227 newcopies = _chain(newcopies, childcopies) |
245 for f in childctx.filesremoved(): |
228 for f in childctx.filesremoved(): |
246 if f in newcopies: |
229 if f in newcopies: |
247 del newcopies[f] |
230 del newcopies[f] |
248 heapq.heappush(work, (c, parent, newcopies)) |
231 othercopies = all_copies.get(c) |
|
232 if othercopies is None: |
|
233 heapq.heappush(work, c) |
|
234 all_copies[c] = newcopies |
|
235 else: |
|
236 # we are the second parent to work on c, we need to merge our |
|
237 # work with the other. |
|
238 # |
|
239 # Unlike when copies are stored in the filelog, we consider |
|
240 # it a copy even if the destination already existed on the |
|
241 # other branch. It's simply too expensive to check if the |
|
242 # file existed in the manifest. |
|
243 # |
|
244 # In case of conflict, parent 1 take precedence over parent 2. |
|
245 # This is an arbitrary choice made anew when implementing |
|
246 # changeset based copies. It was made without regards with |
|
247 # potential filelog related behavior. |
|
248 if parent == 1: |
|
249 othercopies.update(newcopies) |
|
250 else: |
|
251 newcopies.update(othercopies) |
|
252 all_copies[c] = newcopies |
249 assert False |
253 assert False |
250 |
254 |
251 |
255 |
252 def _forwardcopies(a, b, base=None, match=None): |
256 def _forwardcopies(a, b, base=None, match=None): |
253 """find {dst@b: src@a} copy mapping where a is an ancestor of b""" |
257 """find {dst@b: src@a} copy mapping where a is an ancestor of b""" |