##// END OF EJS Templates
merge: add debug diagnostics for findcopies
Matt Mackall -
r5371:17ed9b9a default
parent child Browse files
Show More
@@ -1,636 +1,659 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import errno, util, os, tempfile, context, heapq
11 11
12 12 def filemerge(repo, fw, fd, fo, wctx, mctx):
13 13 """perform a 3-way merge in the working directory
14 14
15 15 fw = original filename in the working directory
16 16 fd = destination filename in the working directory
17 17 fo = filename in other parent
18 18 wctx, mctx = working and merge changecontexts
19 19 """
20 20
21 21 def temp(prefix, ctx):
22 22 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
23 23 (fd, name) = tempfile.mkstemp(prefix=pre)
24 24 data = repo.wwritedata(ctx.path(), ctx.data())
25 25 f = os.fdopen(fd, "wb")
26 26 f.write(data)
27 27 f.close()
28 28 return name
29 29
30 30 fcm = wctx.filectx(fw)
31 31 fcmdata = wctx.filectx(fd).data()
32 32 fco = mctx.filectx(fo)
33 33
34 34 if not fco.cmp(fcmdata): # files identical?
35 35 return None
36 36
37 37 fca = fcm.ancestor(fco)
38 38 if not fca:
39 39 fca = repo.filectx(fw, fileid=nullrev)
40 40 a = repo.wjoin(fd)
41 41 b = temp("base", fca)
42 42 c = temp("other", fco)
43 43
44 44 if fw != fo:
45 45 repo.ui.status(_("merging %s and %s\n") % (fw, fo))
46 46 else:
47 47 repo.ui.status(_("merging %s\n") % fw)
48 48
49 49 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca))
50 50
51 51 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
52 52 or "hgmerge")
53 53 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
54 54 environ={'HG_FILE': fd,
55 55 'HG_MY_NODE': str(wctx.parents()[0]),
56 56 'HG_OTHER_NODE': str(mctx)})
57 57 if r:
58 58 repo.ui.warn(_("merging %s failed!\n") % fd)
59 59
60 60 os.unlink(b)
61 61 os.unlink(c)
62 62 return r
63 63
64 64 def checkunknown(wctx, mctx):
65 65 "check for collisions between unknown files and files in mctx"
66 66 man = mctx.manifest()
67 67 for f in wctx.unknown():
68 68 if f in man:
69 69 if mctx.filectx(f).cmp(wctx.filectx(f).data()):
70 70 raise util.Abort(_("untracked local file '%s' differs"
71 71 " from remote version") % f)
72 72
73 73 def checkcollision(mctx):
74 74 "check for case folding collisions in the destination context"
75 75 folded = {}
76 76 for fn in mctx.manifest():
77 77 fold = fn.lower()
78 78 if fold in folded:
79 79 raise util.Abort(_("case-folding collision between %s and %s")
80 80 % (fn, folded[fold]))
81 81 folded[fold] = fn
82 82
83 83 def forgetremoved(wctx, mctx):
84 84 """
85 85 Forget removed files
86 86
87 87 If we're jumping between revisions (as opposed to merging), and if
88 88 neither the working directory nor the target rev has the file,
89 89 then we need to remove it from the dirstate, to prevent the
90 90 dirstate from listing the file when it is no longer in the
91 91 manifest.
92 92 """
93 93
94 94 action = []
95 95 man = mctx.manifest()
96 96 for f in wctx.deleted() + wctx.removed():
97 97 if f not in man:
98 98 action.append((f, "f"))
99 99
100 100 return action
101 101
102 102 def findcopies(repo, m1, m2, ma, limit):
103 103 """
104 104 Find moves and copies between m1 and m2 back to limit linkrev
105 105 """
106 106
107 107 def nonoverlap(d1, d2, d3):
108 108 "Return list of elements in d1 not in d2 or d3"
109 109 l = [d for d in d1 if d not in d3 and d not in d2]
110 110 l.sort()
111 111 return l
112 112
113 113 def dirname(f):
114 114 s = f.rfind("/")
115 115 if s == -1:
116 116 return ""
117 117 return f[:s]
118 118
119 119 def dirs(files):
120 120 d = {}
121 121 for f in files:
122 122 f = dirname(f)
123 123 while f not in d:
124 124 d[f] = True
125 125 f = dirname(f)
126 126 return d
127 127
128 128 wctx = repo.workingctx()
129 129
130 130 def makectx(f, n):
131 131 if len(n) == 20:
132 132 return repo.filectx(f, fileid=n)
133 133 return wctx.filectx(f)
134 134 ctx = util.cachefunc(makectx)
135 135
136 136 def findold(fctx):
137 137 "find files that path was copied from, back to linkrev limit"
138 138 old = {}
139 139 seen = {}
140 140 orig = fctx.path()
141 141 visit = [fctx]
142 142 while visit:
143 143 fc = visit.pop()
144 144 s = str(fc)
145 145 if s in seen:
146 146 continue
147 147 seen[s] = 1
148 148 if fc.path() != orig and fc.path() not in old:
149 149 old[fc.path()] = 1
150 150 if fc.rev() < limit:
151 151 continue
152 152 visit += fc.parents()
153 153
154 154 old = old.keys()
155 155 old.sort()
156 156 return old
157 157
158 158 copy = {}
159 159 fullcopy = {}
160 160 diverge = {}
161 161
162 162 def checkcopies(c, man, aman):
163 163 '''check possible copies for filectx c'''
164 164 for of in findold(c):
165 165 fullcopy[c.path()] = of # remember for dir rename detection
166 166 if of not in man: # original file not in other manifest?
167 167 if of in ma:
168 168 diverge.setdefault(of, []).append(c.path())
169 169 continue
170 170 # if the original file is unchanged on the other branch,
171 171 # no merge needed
172 172 if man[of] == aman.get(of):
173 173 continue
174 174 c2 = ctx(of, man[of])
175 175 ca = c.ancestor(c2)
176 176 if not ca: # unrelated?
177 177 continue
178 178 # named changed on only one side?
179 179 if ca.path() == c.path() or ca.path() == c2.path():
180 180 if c == ca or c2 == ca: # no merge needed, ignore copy
181 181 continue
182 182 copy[c.path()] = of
183 183
184 184 if not repo.ui.configbool("merge", "followcopies", True):
185 185 return {}, {}
186 186
187 187 # avoid silly behavior for update from empty dir
188 188 if not m1 or not m2 or not ma:
189 189 return {}, {}
190 190
191 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit)
192
191 193 u1 = nonoverlap(m1, m2, ma)
192 194 u2 = nonoverlap(m2, m1, ma)
193 195
196 if u1:
197 repo.ui.debug(_(" unmatched files in local:\n %s\n")
198 % "\n ".join(u1))
199 if u2:
200 repo.ui.debug(_(" unmatched files in other:\n %s\n")
201 % "\n ".join(u2))
202
194 203 for f in u1:
195 204 checkcopies(ctx(f, m1[f]), m2, ma)
196 205
197 206 for f in u2:
198 207 checkcopies(ctx(f, m2[f]), m1, ma)
199 208
200 209 d2 = {}
201 210 for of, fl in diverge.items():
202 211 for f in fl:
203 212 fo = list(fl)
204 213 fo.remove(f)
205 214 d2[f] = (of, fo)
206 215
216 if fullcopy:
217 repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n"))
218 for f in fullcopy:
219 note = ""
220 if f in copy: note += "*"
221 if f in diverge: note += "!"
222 repo.ui.debug(_(" %s -> %s %s\n") % (f, fullcopy[f], note))
223
207 224 if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
208 225 return copy, diverge
209 226
227 repo.ui.debug(_(" checking for directory renames\n"))
228
210 229 # generate a directory move map
211 230 d1, d2 = dirs(m1), dirs(m2)
212 231 invalid = {}
213 232 dirmove = {}
214 233
215 234 # examine each file copy for a potential directory move, which is
216 235 # when all the files in a directory are moved to a new directory
217 236 for dst, src in fullcopy.items():
218 237 dsrc, ddst = dirname(src), dirname(dst)
219 238 if dsrc in invalid:
220 239 # already seen to be uninteresting
221 240 continue
222 241 elif dsrc in d1 and ddst in d1:
223 242 # directory wasn't entirely moved locally
224 243 invalid[dsrc] = True
225 244 elif dsrc in d2 and ddst in d2:
226 245 # directory wasn't entirely moved remotely
227 246 invalid[dsrc] = True
228 247 elif dsrc in dirmove and dirmove[dsrc] != ddst:
229 248 # files from the same directory moved to two different places
230 249 invalid[dsrc] = True
231 250 else:
232 251 # looks good so far
233 252 dirmove[dsrc + "/"] = ddst + "/"
234 253
235 254 for i in invalid:
236 255 if i in dirmove:
237 256 del dirmove[i]
238 257
239 258 del d1, d2, invalid
240 259
241 260 if not dirmove:
242 261 return copy, diverge
243 262
263 for d in dirmove:
264 repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d]))
265
244 266 # check unaccounted nonoverlapping files against directory moves
245 267 for f in u1 + u2:
246 268 if f not in fullcopy:
247 269 for d in dirmove:
248 270 if f.startswith(d):
249 271 # new file added in a directory that was moved, move it
250 272 copy[f] = dirmove[d] + f[len(d):]
273 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f]))
251 274 break
252 275
253 276 return copy, diverge
254 277
255 278 def symmetricdifference(repo, rev1, rev2):
256 279 """symmetric difference of the sets of ancestors of rev1 and rev2
257 280
258 281 I.e. revisions that are ancestors of rev1 or rev2, but not both.
259 282 """
260 283 # basic idea:
261 284 # - mark rev1 and rev2 with different colors
262 285 # - walk the graph in topological order with the help of a heap;
263 286 # for each revision r:
264 287 # - if r has only one color, we want to return it
265 288 # - add colors[r] to its parents
266 289 #
267 290 # We keep track of the number of revisions in the heap that
268 291 # we may be interested in. We stop walking the graph as soon
269 292 # as this number reaches 0.
270 293 WHITE = 1
271 294 BLACK = 2
272 295 ALLCOLORS = WHITE | BLACK
273 296 colors = {rev1: WHITE, rev2: BLACK}
274 297
275 298 cl = repo.changelog
276 299
277 300 visit = [-rev1, -rev2]
278 301 heapq.heapify(visit)
279 302 n_wanted = len(visit)
280 303 ret = []
281 304
282 305 while n_wanted:
283 306 r = -heapq.heappop(visit)
284 307 wanted = colors[r] != ALLCOLORS
285 308 n_wanted -= wanted
286 309 if wanted:
287 310 ret.append(r)
288 311
289 312 for p in cl.parentrevs(r):
290 313 if p == nullrev:
291 314 continue
292 315 if p not in colors:
293 316 # first time we see p; add it to visit
294 317 n_wanted += wanted
295 318 colors[p] = colors[r]
296 319 heapq.heappush(visit, -p)
297 320 elif colors[p] != ALLCOLORS and colors[p] != colors[r]:
298 321 # at first we thought we wanted p, but now
299 322 # we know we don't really want it
300 323 n_wanted -= 1
301 324 colors[p] |= colors[r]
302 325
303 326 del colors[r]
304 327
305 328 return ret
306 329
307 330 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
308 331 """
309 332 Merge p1 and p2 with ancestor ma and generate merge action list
310 333
311 334 overwrite = whether we clobber working files
312 335 partial = function to filter file lists
313 336 """
314 337
315 338 repo.ui.note(_("resolving manifests\n"))
316 339 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
317 340 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
318 341
319 342 m1 = p1.manifest()
320 343 m2 = p2.manifest()
321 344 ma = pa.manifest()
322 345 backwards = (pa == p2)
323 346 action = []
324 347 copy = {}
325 348 diverge = {}
326 349
327 350 def fmerge(f, f2=None, fa=None):
328 351 """merge flags"""
329 352 if not f2:
330 353 f2 = f
331 354 fa = f
332 355 a, b, c = ma.execf(fa), m1.execf(f), m2.execf(f2)
333 356 if ((a^b) | (a^c)) ^ a:
334 357 return 'x'
335 358 a, b, c = ma.linkf(fa), m1.linkf(f), m2.linkf(f2)
336 359 if ((a^b) | (a^c)) ^ a:
337 360 return 'l'
338 361 return ''
339 362
340 363 def act(msg, m, f, *args):
341 364 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
342 365 action.append((f, m) + args)
343 366
344 367 if not (backwards or overwrite):
345 368 rev1 = p1.rev()
346 369 if rev1 is None:
347 370 # p1 is a workingctx
348 371 rev1 = p1.parents()[0].rev()
349 372 limit = min(symmetricdifference(repo, rev1, p2.rev()))
350 373 copy, diverge = findcopies(repo, m1, m2, ma, limit)
351 374
352 375 for of, fl in diverge.items():
353 376 act("divergent renames", "dr", of, fl)
354 377
355 378 copied = dict.fromkeys(copy.values())
356 379
357 380 # Compare manifests
358 381 for f, n in m1.iteritems():
359 382 if partial and not partial(f):
360 383 continue
361 384 if f in m2:
362 385 # are files different?
363 386 if n != m2[f]:
364 387 a = ma.get(f, nullid)
365 388 # are both different from the ancestor?
366 389 if not overwrite and n != a and m2[f] != a:
367 390 act("versions differ", "m", f, f, f, fmerge(f), False)
368 391 # are we clobbering?
369 392 # is remote's version newer?
370 393 # or are we going back in time and clean?
371 394 elif overwrite or m2[f] != a or (backwards and not n[20:]):
372 395 act("remote is newer", "g", f, m2.flags(f))
373 396 # local is newer, not overwrite, check mode bits
374 397 elif fmerge(f) != m1.flags(f):
375 398 act("update permissions", "e", f, m2.flags(f))
376 399 # contents same, check mode bits
377 400 elif m1.flags(f) != m2.flags(f):
378 401 if overwrite or fmerge(f) != m1.flags(f):
379 402 act("update permissions", "e", f, m2.flags(f))
380 403 elif f in copied:
381 404 continue
382 405 elif f in copy:
383 406 f2 = copy[f]
384 407 if f2 not in m2: # directory rename
385 408 act("remote renamed directory to " + f2, "d",
386 409 f, None, f2, m1.flags(f))
387 410 elif f2 in m1: # case 2 A,B/B/B
388 411 act("local copied to " + f2, "m",
389 412 f, f2, f, fmerge(f, f2, f2), False)
390 413 else: # case 4,21 A/B/B
391 414 act("local moved to " + f2, "m",
392 415 f, f2, f, fmerge(f, f2, f2), False)
393 416 elif f in ma:
394 417 if n != ma[f] and not overwrite:
395 418 if repo.ui.prompt(
396 419 (_(" local changed %s which remote deleted\n") % f) +
397 420 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
398 421 act("prompt delete", "r", f)
399 422 else:
400 423 act("other deleted", "r", f)
401 424 else:
402 425 # file is created on branch or in working directory
403 426 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
404 427 act("remote deleted", "r", f)
405 428
406 429 for f, n in m2.iteritems():
407 430 if partial and not partial(f):
408 431 continue
409 432 if f in m1:
410 433 continue
411 434 if f in copied:
412 435 continue
413 436 if f in copy:
414 437 f2 = copy[f]
415 438 if f2 not in m1: # directory rename
416 439 act("local renamed directory to " + f2, "d",
417 440 None, f, f2, m2.flags(f))
418 441 elif f2 in m2: # rename case 1, A/A,B/A
419 442 act("remote copied to " + f, "m",
420 443 f2, f, f, fmerge(f2, f, f2), False)
421 444 else: # case 3,20 A/B/A
422 445 act("remote moved to " + f, "m",
423 446 f2, f, f, fmerge(f2, f, f2), True)
424 447 elif f in ma:
425 448 if overwrite or backwards:
426 449 act("recreating", "g", f, m2.flags(f))
427 450 elif n != ma[f]:
428 451 if repo.ui.prompt(
429 452 (_("remote changed %s which local deleted\n") % f) +
430 453 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
431 454 act("prompt recreating", "g", f, m2.flags(f))
432 455 else:
433 456 act("remote created", "g", f, m2.flags(f))
434 457
435 458 return action
436 459
437 460 def applyupdates(repo, action, wctx, mctx):
438 461 "apply the merge action list to the working directory"
439 462
440 463 updated, merged, removed, unresolved = 0, 0, 0, 0
441 464 action.sort()
442 465 # prescan for copy/renames
443 466 for a in action:
444 467 f, m = a[:2]
445 468 if m == 'm': # merge
446 469 f2, fd, flags, move = a[2:]
447 470 if f != fd:
448 471 repo.ui.debug(_("copying %s to %s\n") % (f, fd))
449 472 repo.wwrite(fd, repo.wread(f), flags)
450 473
451 474 audit_path = util.path_auditor(repo.root)
452 475
453 476 for a in action:
454 477 f, m = a[:2]
455 478 if f and f[0] == "/":
456 479 continue
457 480 if m == "r": # remove
458 481 repo.ui.note(_("removing %s\n") % f)
459 482 audit_path(f)
460 483 try:
461 484 util.unlink(repo.wjoin(f))
462 485 except OSError, inst:
463 486 if inst.errno != errno.ENOENT:
464 487 repo.ui.warn(_("update failed to remove %s: %s!\n") %
465 488 (f, inst.strerror))
466 489 removed += 1
467 490 elif m == "m": # merge
468 491 f2, fd, flags, move = a[2:]
469 492 r = filemerge(repo, f, fd, f2, wctx, mctx)
470 493 if r > 0:
471 494 unresolved += 1
472 495 else:
473 496 if r is None:
474 497 updated += 1
475 498 else:
476 499 merged += 1
477 500 util.set_exec(repo.wjoin(fd), "x" in flags)
478 501 if f != fd and move and util.lexists(repo.wjoin(f)):
479 502 repo.ui.debug(_("removing %s\n") % f)
480 503 os.unlink(repo.wjoin(f))
481 504 elif m == "g": # get
482 505 flags = a[2]
483 506 repo.ui.note(_("getting %s\n") % f)
484 507 t = mctx.filectx(f).data()
485 508 repo.wwrite(f, t, flags)
486 509 updated += 1
487 510 elif m == "d": # directory rename
488 511 f2, fd, flags = a[2:]
489 512 if f:
490 513 repo.ui.note(_("moving %s to %s\n") % (f, fd))
491 514 t = wctx.filectx(f).data()
492 515 repo.wwrite(fd, t, flags)
493 516 util.unlink(repo.wjoin(f))
494 517 if f2:
495 518 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
496 519 t = mctx.filectx(f2).data()
497 520 repo.wwrite(fd, t, flags)
498 521 updated += 1
499 522 elif m == "dr": # divergent renames
500 523 fl = a[2]
501 524 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
502 525 for nf in fl:
503 526 repo.ui.warn(" %s\n" % nf)
504 527 elif m == "e": # exec
505 528 flags = a[2]
506 529 util.set_exec(repo.wjoin(f), flags)
507 530
508 531 return updated, merged, removed, unresolved
509 532
510 533 def recordupdates(repo, action, branchmerge):
511 534 "record merge actions to the dirstate"
512 535
513 536 for a in action:
514 537 f, m = a[:2]
515 538 if m == "r": # remove
516 539 if branchmerge:
517 540 repo.dirstate.remove(f)
518 541 else:
519 542 repo.dirstate.forget(f)
520 543 elif m == "f": # forget
521 544 repo.dirstate.forget(f)
522 545 elif m in "ge": # get or exec change
523 546 if branchmerge:
524 547 repo.dirstate.normaldirty(f)
525 548 else:
526 549 repo.dirstate.normal(f)
527 550 elif m == "m": # merge
528 551 f2, fd, flag, move = a[2:]
529 552 if branchmerge:
530 553 # We've done a branch merge, mark this file as merged
531 554 # so that we properly record the merger later
532 555 repo.dirstate.merge(fd)
533 556 if f != f2: # copy/rename
534 557 if move:
535 558 repo.dirstate.remove(f)
536 559 if f != fd:
537 560 repo.dirstate.copy(f, fd)
538 561 else:
539 562 repo.dirstate.copy(f2, fd)
540 563 else:
541 564 # We've update-merged a locally modified file, so
542 565 # we set the dirstate to emulate a normal checkout
543 566 # of that file some time in the past. Thus our
544 567 # merge will appear as a normal local file
545 568 # modification.
546 569 repo.dirstate.normallookup(fd)
547 570 if move:
548 571 repo.dirstate.forget(f)
549 572 elif m == "d": # directory rename
550 573 f2, fd, flag = a[2:]
551 574 if not f2 and f not in repo.dirstate:
552 575 # untracked file moved
553 576 continue
554 577 if branchmerge:
555 578 repo.dirstate.add(fd)
556 579 if f:
557 580 repo.dirstate.remove(f)
558 581 repo.dirstate.copy(f, fd)
559 582 if f2:
560 583 repo.dirstate.copy(f2, fd)
561 584 else:
562 585 repo.dirstate.normal(fd)
563 586 if f:
564 587 repo.dirstate.forget(f)
565 588
566 589 def update(repo, node, branchmerge, force, partial):
567 590 """
568 591 Perform a merge between the working directory and the given node
569 592
570 593 branchmerge = whether to merge between branches
571 594 force = whether to force branch merging or file overwriting
572 595 partial = a function to filter file lists (dirstate not updated)
573 596 """
574 597
575 598 wlock = repo.wlock()
576 599 try:
577 600 wc = repo.workingctx()
578 601 if node is None:
579 602 # tip of current branch
580 603 try:
581 604 node = repo.branchtags()[wc.branch()]
582 605 except KeyError:
583 606 raise util.Abort(_("branch %s not found") % wc.branch())
584 607 overwrite = force and not branchmerge
585 608 forcemerge = force and branchmerge
586 609 pl = wc.parents()
587 610 p1, p2 = pl[0], repo.changectx(node)
588 611 pa = p1.ancestor(p2)
589 612 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
590 613 fastforward = False
591 614
592 615 ### check phase
593 616 if not overwrite and len(pl) > 1:
594 617 raise util.Abort(_("outstanding uncommitted merges"))
595 618 if pa == p1 or pa == p2: # is there a linear path from p1 to p2?
596 619 if branchmerge:
597 620 if p1.branch() != p2.branch() and pa != p2:
598 621 fastforward = True
599 622 else:
600 623 raise util.Abort(_("there is nothing to merge, just use "
601 624 "'hg update' or look at 'hg heads'"))
602 625 elif not (overwrite or branchmerge):
603 626 raise util.Abort(_("update spans branches, use 'hg merge' "
604 627 "or 'hg update -C' to lose changes"))
605 628 if branchmerge and not forcemerge:
606 629 if wc.files():
607 630 raise util.Abort(_("outstanding uncommitted changes"))
608 631
609 632 ### calculate phase
610 633 action = []
611 634 if not force:
612 635 checkunknown(wc, p2)
613 636 if not util.checkfolding(repo.path):
614 637 checkcollision(p2)
615 638 if not branchmerge:
616 639 action += forgetremoved(wc, p2)
617 640 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
618 641
619 642 ### apply phase
620 643 if not branchmerge: # just jump to the new rev
621 644 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
622 645 if not partial:
623 646 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
624 647
625 648 stats = applyupdates(repo, action, wc, p2)
626 649
627 650 if not partial:
628 651 recordupdates(repo, action, branchmerge)
629 652 repo.dirstate.setparents(fp1, fp2)
630 653 if not branchmerge and not fastforward:
631 654 repo.dirstate.setbranch(p2.branch())
632 655 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
633 656
634 657 return stats
635 658 finally:
636 659 del wlock
@@ -1,23 +1,31 b''
1 1 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
2 2 resolving manifests
3 3 overwrite None partial False
4 4 ancestor 583c7b748052 local fb3948d97f07+ remote 40da226db0f0
5 searching for copies back to rev 1
6 unmatched files in other:
7 b
8 c
9 all copies found (* = to merge, ! = divergent):
10 c -> a *
11 b -> a *
12 checking for directory renames
5 13 a: remote moved to c -> m
6 14 a: remote moved to b -> m
7 15 copying a to b
8 16 copying a to c
9 17 merging a and b
10 18 my a@fb3948d97f07+ other b@40da226db0f0 ancestor a@583c7b748052
11 19 removing a
12 20 merging a and c
13 21 my a@fb3948d97f07+ other c@40da226db0f0 ancestor a@583c7b748052
14 22 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
15 23 (branch merge, don't forget to commit)
16 24 -- b --
17 25 0
18 26 1
19 27 2
20 28 -- c --
21 29 0
22 30 1
23 31 2
@@ -1,20 +1,26 b''
1 1 resolving manifests
2 2 overwrite None partial False
3 3 ancestor 310fd17130da local 2092631ce82b+ remote 7731dad1c2b9
4 searching for copies back to rev 1
5 unmatched files in other:
6 bar
7 all copies found (* = to merge, ! = divergent):
8 bar -> foo *
9 checking for directory renames
4 10 foo: versions differ -> m
5 11 foo: remote copied to bar -> m
6 12 copying foo to bar
7 13 merging foo and bar
8 14 my foo@2092631ce82b+ other bar@7731dad1c2b9 ancestor foo@310fd17130da
9 15 merging foo
10 16 my foo@2092631ce82b+ other foo@7731dad1c2b9 ancestor foo@310fd17130da
11 17 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
12 18 (branch merge, don't forget to commit)
13 19 -- foo --
14 20 line 0
15 21 line 1
16 22 line 2-1
17 23 -- bar --
18 24 line 0
19 25 line 1
20 26 line 2-2
@@ -1,17 +1,20 b''
1 1 reverting foo
2 2 changeset 2:4d9e78aaceee backs out changeset 1:b515023e500e
3 3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 4 resolving manifests
5 5 overwrite None partial False
6 6 ancestor bbd179dfa0a7 local 71766447bdbb+ remote 4d9e78aaceee
7 searching for copies back to rev 1
8 unmatched files in local:
9 bar
7 10 foo: remote is newer -> g
8 11 getting foo
9 12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
10 13 (branch merge, don't forget to commit)
11 14 n 0 -2 unset foo
12 15 M foo
13 16 c6fc755d7e68f49f880599da29f15add41f42f5a 644 foo
14 17 rev offset length base linkrev nodeid p1 p2
15 18 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000
16 19 1 5 9 1 1 6f4310b00b9a 2ed2a3912a0b 000000000000
17 20 2 14 5 2 2 c6fc755d7e68 6f4310b00b9a 000000000000
@@ -1,33 +1,51 b''
1 1 adding 1
2 2 adding 2
3 3 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
4 4 resolving manifests
5 5 overwrite None partial False
6 6 ancestor 81f4b099af3d local c64f439569a9+ remote 2f8037f47a5c
7 searching for copies back to rev 1
8 unmatched files in other:
9 1a
10 all copies found (* = to merge, ! = divergent):
11 1a -> 1
12 checking for directory renames
7 13 1: other deleted -> r
8 14 1a: remote created -> g
9 15 removing 1
10 16 getting 1a
11 17 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
12 18 (branch merge, don't forget to commit)
13 19 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
14 20 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
15 21 resolving manifests
16 22 overwrite None partial False
17 23 ancestor c64f439569a9 local ac7575e3c052+ remote 746e9549ea96
24 searching for copies back to rev 1
25 unmatched files in local:
26 1a
27 all copies found (* = to merge, ! = divergent):
28 1a -> 1 *
29 checking for directory renames
18 30 1a: local moved to 1 -> m
19 31 merging 1a and 1
20 32 my 1a@ac7575e3c052+ other 1@746e9549ea96 ancestor 1@81f4b099af3d
21 33 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
22 34 (branch merge, don't forget to commit)
23 35 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
24 36 resolving manifests
25 37 overwrite None partial False
26 38 ancestor c64f439569a9 local 746e9549ea96+ remote ac7575e3c052
39 searching for copies back to rev 1
40 unmatched files in other:
41 1a
42 all copies found (* = to merge, ! = divergent):
43 1a -> 1 *
44 checking for directory renames
27 45 1: remote moved to 1a -> m
28 46 copying 1 to 1a
29 47 merging 1 and 1a
30 48 my 1@746e9549ea96+ other 1a@2f8037f47a5c ancestor 1@81f4b099af3d
31 49 removing 1
32 50 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
33 51 (branch merge, don't forget to commit)
@@ -1,83 +1,85 b''
1 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 2 merging bar and foo
3 3 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4 4 (branch merge, don't forget to commit)
5 5 % contents of bar should be line0 line1 line2
6 6 line0
7 7 line1
8 8 line2
9 9 rev offset length base linkrev nodeid p1 p2
10 10 0 0 77 0 2 da78c0659611 000000000000 000000000000
11 11 1 77 76 0 3 4b358025380b 000000000000 da78c0659611
12 12 bar renamed from foo:9e25c27b87571a1edee5ae4dddee5687746cc8e2
13 13 rev offset length base linkrev nodeid p1 p2
14 14 0 0 7 0 0 690b295714ae 000000000000 000000000000
15 15 1 7 13 1 1 9e25c27b8757 690b295714ae 000000000000
16 16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 17 4:2d2f9a22c82b 2:0a3ab4856510
18 18 3:7d3b554bfdf1 2:0a3ab4856510 1:5cd961e4045d
19 19 2:0a3ab4856510 0:2665aaee66e9
20 20 1:5cd961e4045d
21 21 0:2665aaee66e9
22 22 % this should use bar@rev2 as the ancestor
23 23 resolving manifests
24 24 overwrite None partial False
25 25 ancestor 0a3ab4856510 local 2d2f9a22c82b+ remote 7d3b554bfdf1
26 searching for copies back to rev 1
26 27 bar: versions differ -> m
27 28 merging bar
28 29 my bar@2d2f9a22c82b+ other bar@7d3b554bfdf1 ancestor bar@0a3ab4856510
29 30 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
30 31 (branch merge, don't forget to commit)
31 32 % contents of bar should be line1 line2
32 33 line1
33 34 line2
34 35 rev offset length base linkrev nodeid p1 p2
35 36 0 0 77 0 2 da78c0659611 000000000000 000000000000
36 37 1 77 76 0 3 4b358025380b 000000000000 da78c0659611
37 38 2 153 7 2 4 4defe5eec418 da78c0659611 000000000000
38 39 3 160 13 3 5 4663501da27b 4defe5eec418 4b358025380b
39 40
40 41
41 42 requesting all changes
42 43 adding changesets
43 44 adding manifests
44 45 adding file changes
45 46 added 3 changesets with 3 changes to 2 files (+1 heads)
46 47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 48 merging foo and bar
48 49 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
49 50 (branch merge, don't forget to commit)
50 51 % contents of bar should be line0 line1 line2
51 52 line0
52 53 line1
53 54 line2
54 55 rev offset length base linkrev nodeid p1 p2
55 56 0 0 77 0 2 da78c0659611 000000000000 000000000000
56 57 1 77 76 0 3 4b358025380b 000000000000 da78c0659611
57 58 bar renamed from foo:9e25c27b87571a1edee5ae4dddee5687746cc8e2
58 59 rev offset length base linkrev nodeid p1 p2
59 60 0 0 7 0 0 690b295714ae 000000000000 000000000000
60 61 1 7 13 1 1 9e25c27b8757 690b295714ae 000000000000
61 62 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 63 4:2d2f9a22c82b 2:0a3ab4856510
63 64 3:96ab80c60897 1:5cd961e4045d 2:0a3ab4856510
64 65 2:0a3ab4856510 0:2665aaee66e9
65 66 1:5cd961e4045d
66 67 0:2665aaee66e9
67 68 % this should use bar@rev2 as the ancestor
68 69 resolving manifests
69 70 overwrite None partial False
70 71 ancestor 0a3ab4856510 local 2d2f9a22c82b+ remote 96ab80c60897
72 searching for copies back to rev 1
71 73 bar: versions differ -> m
72 74 merging bar
73 75 my bar@2d2f9a22c82b+ other bar@96ab80c60897 ancestor bar@0a3ab4856510
74 76 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
75 77 (branch merge, don't forget to commit)
76 78 % contents of bar should be line1 line2
77 79 line1
78 80 line2
79 81 rev offset length base linkrev nodeid p1 p2
80 82 0 0 77 0 2 da78c0659611 000000000000 000000000000
81 83 1 77 76 0 3 4b358025380b 000000000000 da78c0659611
82 84 2 153 7 2 4 4defe5eec418 da78c0659611 000000000000
83 85 3 160 13 3 5 4663501da27b 4defe5eec418 4b358025380b
@@ -1,77 +1,78 b''
1 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 2 pulling from ../test-a
3 3 searching for changes
4 4 adding changesets
5 5 adding manifests
6 6 adding file changes
7 7 added 1 changesets with 1 changes to 1 files (+1 heads)
8 8 (run 'hg heads' to see heads, 'hg merge' to merge)
9 9 warning: conflicts during merge.
10 10 merging test.txt
11 11 merging test.txt failed!
12 12 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
13 13 There are unresolved merges, you can redo the full merge using:
14 14 hg update -C 1
15 15 hg merge 2
16 16 pulling from ../test-a
17 17 searching for changes
18 18 adding changesets
19 19 adding manifests
20 20 adding file changes
21 21 added 1 changesets with 1 changes to 1 files (+1 heads)
22 22 (run 'hg heads' to see heads, 'hg merge' to merge)
23 23 warning: conflicts during merge.
24 24 resolving manifests
25 25 overwrite None partial False
26 26 ancestor faaea63e63a9 local 451c744aabcc+ remote a070d41e8360
27 searching for copies back to rev 1
27 28 test.txt: versions differ -> m
28 29 merging test.txt
29 30 my test.txt@451c744aabcc+ other test.txt@a070d41e8360 ancestor test.txt@faaea63e63a9
30 31 merging test.txt failed!
31 32 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
32 33 There are unresolved merges, you can redo the full merge using:
33 34 hg update -C 3
34 35 hg merge 4
35 36 one
36 37 <<<<<<< my
37 38 two-point-five
38 39 =======
39 40 two-point-one
40 41 >>>>>>> other
41 42 three
42 43 rev offset length base linkrev nodeid p1 p2
43 44 0 0 7 0 0 01365c4cca56 000000000000 000000000000
44 45 1 7 9 1 1 7b013192566a 01365c4cca56 000000000000
45 46 2 16 15 2 2 8fe46a3eb557 01365c4cca56 000000000000
46 47 3 31 27 2 3 fc3148072371 7b013192566a 8fe46a3eb557
47 48 4 58 25 4 4 d40249267ae3 8fe46a3eb557 000000000000
48 49 changeset: 4:a070d41e8360
49 50 tag: tip
50 51 parent: 2:faaea63e63a9
51 52 user: test
52 53 date: Mon Jan 12 13:46:40 1970 +0000
53 54 summary: two -> two-point-one
54 55
55 56 changeset: 3:451c744aabcc
56 57 parent: 1:e409be6afcc0
57 58 parent: 2:faaea63e63a9
58 59 user: test
59 60 date: Mon Jan 12 13:46:40 1970 +0000
60 61 summary: Merge 1
61 62
62 63 changeset: 2:faaea63e63a9
63 64 parent: 0:095c92b91f1a
64 65 user: test
65 66 date: Mon Jan 12 13:46:40 1970 +0000
66 67 summary: Numbers as words
67 68
68 69 changeset: 1:e409be6afcc0
69 70 user: test
70 71 date: Mon Jan 12 13:46:40 1970 +0000
71 72 summary: 2 -> 2.5
72 73
73 74 changeset: 0:095c92b91f1a
74 75 user: test
75 76 date: Mon Jan 12 13:46:40 1970 +0000
76 77 summary: Initial
77 78
@@ -1,44 +1,68 b''
1 1 adding a/a
2 2 adding a/b
3 3 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 4 copying a/a to b/a
5 5 copying a/b to b/b
6 6 removing a/a
7 7 removing a/b
8 8 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
9 9 resolving manifests
10 10 overwrite None partial False
11 11 ancestor f9b20c0d4c51 local ce36d17b18fb+ remote 55119e611c80
12 searching for copies back to rev 1
13 unmatched files in local:
14 a/c
15 unmatched files in other:
16 b/a
17 b/b
18 all copies found (* = to merge, ! = divergent):
19 b/a -> a/a
20 b/b -> a/b
21 checking for directory renames
22 dir a/ -> b/
23 file a/c -> b/c
12 24 a/c: remote renamed directory to b/c -> d
13 25 a/b: other deleted -> r
14 26 a/a: other deleted -> r
15 27 b/a: remote created -> g
16 28 b/b: remote created -> g
17 29 removing a/a
18 30 removing a/b
19 31 moving a/c to b/c
20 32 getting b/a
21 33 getting b/b
22 34 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
23 35 (branch merge, don't forget to commit)
24 36 a/* b/a b/b b/c
25 37 M b/a
26 38 M b/b
27 39 A b/c
28 40 a/c
29 41 R a/a
30 42 R a/b
31 43 R a/c
32 44 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88
33 45 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
34 46 resolving manifests
35 47 overwrite None partial False
36 48 ancestor f9b20c0d4c51 local 55119e611c80+ remote ce36d17b18fb
49 searching for copies back to rev 1
50 unmatched files in local:
51 b/a
52 b/b
53 unmatched files in other:
54 a/c
55 all copies found (* = to merge, ! = divergent):
56 b/a -> a/a
57 b/b -> a/b
58 checking for directory renames
59 dir a/ -> b/
60 file a/c -> b/c
37 61 None: local renamed directory to b/c -> d
38 62 getting a/c to b/c
39 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 64 (branch merge, don't forget to commit)
41 65 a/* b/a b/b b/c
42 66 A b/c
43 67 a/c
44 68 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88
@@ -1,29 +1,40 b''
1 1 checkout
2 2 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
3 3 merge
4 4 resolving manifests
5 5 overwrite None partial False
6 6 ancestor af1939970a1c local f26ec4fc3fa3+ remote 8e765a822af2
7 searching for copies back to rev 1
8 unmatched files in local:
9 c2
10 unmatched files in other:
11 b
12 b2
13 all copies found (* = to merge, ! = divergent):
14 c2 -> a2
15 b -> a *
16 b2 -> a2
17 checking for directory renames
7 18 a2: divergent renames -> dr
8 19 a: remote moved to b -> m
9 20 b2: remote created -> g
10 21 copying a to b
11 22 merging a and b
12 23 my a@f26ec4fc3fa3+ other b@8e765a822af2 ancestor a@af1939970a1c
13 24 removing a
14 25 warning: detected divergent renames of a2 to:
15 26 c2
16 27 b2
17 28 getting b2
18 29 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
19 30 (branch merge, don't forget to commit)
20 31 M b
21 32 a
22 33 M b2
23 34 R a
24 35 C c2
25 36 blahblah
26 37 rev offset length base linkrev nodeid p1 p2
27 38 0 0 67 0 1 dc51707dfc98 000000000000 000000000000
28 39 1 67 72 1 3 b2494a44f0a9 000000000000 dc51707dfc98
29 40 b renamed from a:dd03b83622e78778b403775d0d074b9ac7387a66
@@ -1,136 +1,143 b''
1 1 adding a
2 2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 3 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 4 diff -r 33aaa84a386b a
5 5 --- a/a
6 6 +++ b/a
7 7 @@ -1,1 +1,1 @@ a
8 8 -a
9 9 +abc
10 10 adding b
11 11 M a
12 12 changeset: 0:33aaa84a386b
13 13 user: test
14 14 date: Mon Jan 12 13:46:40 1970 +0000
15 15 summary: 1
16 16
17 17 resolving manifests
18 18 overwrite False partial False
19 19 ancestor 33aaa84a386b local 33aaa84a386b+ remote 802f095af299
20 searching for copies back to rev 1
21 unmatched files in other:
22 b
20 23 a: versions differ -> m
21 24 b: remote created -> g
22 25 merging a
23 26 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
24 27 getting b
25 28 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
26 29 changeset: 1:802f095af299
27 30 tag: tip
28 31 user: test
29 32 date: Mon Jan 12 13:46:40 1970 +0000
30 33 summary: 2
31 34
32 35 resolving manifests
33 36 overwrite False partial False
34 37 ancestor 33aaa84a386b local 802f095af299+ remote 33aaa84a386b
35 38 b: remote deleted -> r
36 39 removing b
37 40 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 41 changeset: 0:33aaa84a386b
39 42 user: test
40 43 date: Mon Jan 12 13:46:40 1970 +0000
41 44 summary: 1
42 45
43 46 abort: there is nothing to merge - use "hg update" instead
44 47 failed
45 48 changeset: 0:33aaa84a386b
46 49 user: test
47 50 date: Mon Jan 12 13:46:40 1970 +0000
48 51 summary: 1
49 52
50 53 resolving manifests
51 54 overwrite False partial False
52 55 ancestor 33aaa84a386b local 33aaa84a386b+ remote 802f095af299
56 searching for copies back to rev 1
57 unmatched files in other:
58 b
53 59 a: versions differ -> m
54 60 b: remote created -> g
55 61 merging a
56 62 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
57 63 getting b
58 64 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
59 65 changeset: 1:802f095af299
60 66 tag: tip
61 67 user: test
62 68 date: Mon Jan 12 13:46:40 1970 +0000
63 69 summary: 2
64 70
65 71 changeset: 1:802f095af299
66 72 tag: tip
67 73 user: test
68 74 date: Mon Jan 12 13:46:40 1970 +0000
69 75 files: a b
70 76 description:
71 77 2
72 78
73 79
74 80 changeset: 0:33aaa84a386b
75 81 user: test
76 82 date: Mon Jan 12 13:46:40 1970 +0000
77 83 files: a
78 84 description:
79 85 1
80 86
81 87
82 88 diff -r 802f095af299 a
83 89 --- a/a
84 90 +++ b/a
85 91 @@ -1,1 +1,1 @@ a2
86 92 -a2
87 93 +abc
88 94 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
89 95 adding b
90 96 M a
91 97 changeset: 1:802f095af299
92 98 user: test
93 99 date: Mon Jan 12 13:46:40 1970 +0000
94 100 summary: 2
95 101
96 102 abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
97 103 failed
98 104 abort: outstanding uncommitted changes
99 105 failed
100 106 resolving manifests
101 107 overwrite False partial False
102 108 ancestor 33aaa84a386b local 802f095af299+ remote 030602aee63d
109 searching for copies back to rev 1
103 110 a: versions differ -> m
104 111 b: versions differ -> m
105 112 merging a
106 113 my a@802f095af299+ other a@030602aee63d ancestor a@33aaa84a386b
107 114 merging b
108 115 my b@802f095af299+ other b@030602aee63d ancestor b@000000000000
109 116 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
110 117 (branch merge, don't forget to commit)
111 118 changeset: 1:802f095af299
112 119 user: test
113 120 date: Mon Jan 12 13:46:40 1970 +0000
114 121 summary: 2
115 122
116 123 changeset: 2:030602aee63d
117 124 tag: tip
118 125 parent: 0:33aaa84a386b
119 126 user: test
120 127 date: Mon Jan 12 13:46:40 1970 +0000
121 128 summary: 3
122 129
123 130 diff -r 802f095af299 a
124 131 --- a/a
125 132 +++ b/a
126 133 @@ -1,1 +1,1 @@ a2
127 134 -a2
128 135 +abc
129 136 adding a
130 137 pulling from ../a
131 138 requesting all changes
132 139 adding changesets
133 140 adding manifests
134 141 adding file changes
135 142 added 1 changesets with 1 changes to 1 files
136 143 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now