153 old.sort() |
153 old.sort() |
154 return old |
154 return old |
155 |
155 |
156 copy = {} |
156 copy = {} |
157 fullcopy = {} |
157 fullcopy = {} |
|
158 diverge = {} |
158 |
159 |
159 def checkcopies(c, man): |
160 def checkcopies(c, man): |
160 '''check possible copies for filectx c''' |
161 '''check possible copies for filectx c''' |
161 for of in findold(c): |
162 for of in findold(c): |
|
163 fullcopy[c.path()] = of # remember for dir rename detection |
162 if of not in man: # original file not in other manifest? |
164 if of not in man: # original file not in other manifest? |
|
165 if of in ma: |
|
166 diverge.setdefault(of, []).append(c.path()) |
163 continue |
167 continue |
164 c2 = ctx(of, man[of]) |
168 c2 = ctx(of, man[of]) |
165 ca = c.ancestor(c2) |
169 ca = c.ancestor(c2) |
166 if not ca: # unrelated? |
170 if not ca: # unrelated? |
167 continue |
171 continue |
168 # named changed on only one side? |
172 # named changed on only one side? |
169 if ca.path() == c.path() or ca.path() == c2.path(): |
173 if ca.path() == c.path() or ca.path() == c2.path(): |
170 fullcopy[c.path()] = of # remember for dir rename detection |
|
171 if c == ca or c2 == ca: # no merge needed, ignore copy |
174 if c == ca or c2 == ca: # no merge needed, ignore copy |
172 continue |
175 continue |
173 copy[c.path()] = of |
176 copy[c.path()] = of |
174 |
177 |
175 if not repo.ui.configbool("merge", "followcopies", True): |
178 if not repo.ui.configbool("merge", "followcopies", True): |
176 return {} |
179 return {}, {} |
177 |
180 |
178 # avoid silly behavior for update from empty dir |
181 # avoid silly behavior for update from empty dir |
179 if not m1 or not m2 or not ma: |
182 if not m1 or not m2 or not ma: |
180 return {} |
183 return {}, {} |
181 |
184 |
182 u1 = nonoverlap(m1, m2, ma) |
185 u1 = nonoverlap(m1, m2, ma) |
183 u2 = nonoverlap(m2, m1, ma) |
186 u2 = nonoverlap(m2, m1, ma) |
184 |
187 |
185 for f in u1: |
188 for f in u1: |
186 checkcopies(ctx(f, m1[f]), m2) |
189 checkcopies(ctx(f, m1[f]), m2) |
187 |
190 |
188 for f in u2: |
191 for f in u2: |
189 checkcopies(ctx(f, m2[f]), m1) |
192 checkcopies(ctx(f, m2[f]), m1) |
190 |
193 |
|
194 d2 = {} |
|
195 for of, fl in diverge.items(): |
|
196 for f in fl: |
|
197 fo = list(fl) |
|
198 fo.remove(f) |
|
199 d2[f] = (of, fo) |
|
200 #diverge = d2 |
|
201 |
191 if not fullcopy or not repo.ui.configbool("merge", "followdirs", True): |
202 if not fullcopy or not repo.ui.configbool("merge", "followdirs", True): |
192 return copy |
203 return copy, diverge |
193 |
204 |
194 # generate a directory move map |
205 # generate a directory move map |
195 d1, d2 = dirs(m1), dirs(m2) |
206 d1, d2 = dirs(m1), dirs(m2) |
196 invalid = {} |
207 invalid = {} |
197 dirmove = {} |
208 dirmove = {} |
221 del dirmove[i] |
232 del dirmove[i] |
222 |
233 |
223 del d1, d2, invalid |
234 del d1, d2, invalid |
224 |
235 |
225 if not dirmove: |
236 if not dirmove: |
226 return copy |
237 return copy, diverge |
227 |
238 |
228 # check unaccounted nonoverlapping files against directory moves |
239 # check unaccounted nonoverlapping files against directory moves |
229 for f in u1 + u2: |
240 for f in u1 + u2: |
230 if f not in fullcopy: |
241 if f not in fullcopy: |
231 for d in dirmove: |
242 for d in dirmove: |
232 if f.startswith(d): |
243 if f.startswith(d): |
233 # new file added in a directory that was moved, move it |
244 # new file added in a directory that was moved, move it |
234 copy[f] = dirmove[d] + f[len(d):] |
245 copy[f] = dirmove[d] + f[len(d):] |
235 break |
246 break |
236 |
247 |
237 return copy |
248 return copy, diverge |
238 |
249 |
239 def manifestmerge(repo, p1, p2, pa, overwrite, partial): |
250 def manifestmerge(repo, p1, p2, pa, overwrite, partial): |
240 """ |
251 """ |
241 Merge p1 and p2 with ancestor ma and generate merge action list |
252 Merge p1 and p2 with ancestor ma and generate merge action list |
242 |
253 |
271 def act(msg, m, f, *args): |
283 def act(msg, m, f, *args): |
272 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) |
284 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) |
273 action.append((f, m) + args) |
285 action.append((f, m) + args) |
274 |
286 |
275 if not (backwards or overwrite): |
287 if not (backwards or overwrite): |
276 copy = findcopies(repo, m1, m2, ma, pa.rev()) |
288 copy, diverge = findcopies(repo, m1, m2, ma, pa.rev()) |
|
289 |
|
290 for of, fl in diverge.items(): |
|
291 act("divergent renames", "dr", of, fl) |
|
292 |
277 copied = dict.fromkeys(copy.values()) |
293 copied = dict.fromkeys(copy.values()) |
278 |
294 |
279 # Compare manifests |
295 # Compare manifests |
280 for f, n in m1.iteritems(): |
296 for f, n in m1.iteritems(): |
281 if partial and not partial(f): |
297 if partial and not partial(f): |
408 if f2: |
424 if f2: |
409 repo.ui.note(_("getting %s to %s\n") % (f2, fd)) |
425 repo.ui.note(_("getting %s to %s\n") % (f2, fd)) |
410 t = mctx.filectx(f2).data() |
426 t = mctx.filectx(f2).data() |
411 repo.wwrite(fd, t, flags) |
427 repo.wwrite(fd, t, flags) |
412 updated += 1 |
428 updated += 1 |
|
429 elif m == "dr": # divergent renames |
|
430 fl = a[2] |
|
431 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f) |
|
432 for nf in fl: |
|
433 repo.ui.warn(" %s\n" % nf) |
413 elif m == "e": # exec |
434 elif m == "e": # exec |
414 flags = a[2] |
435 flags = a[2] |
415 util.set_exec(repo.wjoin(f), flags) |
436 util.set_exec(repo.wjoin(f), flags) |
416 |
437 |
417 return updated, merged, removed, unresolved |
438 return updated, merged, removed, unresolved |