##// END OF EJS Templates
merge: when rename was made on both sides, use ancestor as merge base...
Martin von Zweigbergk -
r45099:b4057d00 default
parent child Browse files
Show More
@@ -1,1172 +1,1169 b''
1 1 # copies.py - copy detection for Mercurial
2 2 #
3 3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import collections
11 11 import multiprocessing
12 12 import os
13 13
14 14 from .i18n import _
15 15
16 16
17 17 from .revlogutils.flagutil import REVIDX_SIDEDATA
18 18
19 19 from . import (
20 20 error,
21 21 match as matchmod,
22 22 node,
23 23 pathutil,
24 24 pycompat,
25 25 util,
26 26 )
27 27
28 28 from .revlogutils import sidedata as sidedatamod
29 29
30 30 from .utils import stringutil
31 31
32 32
33 33 def _filter(src, dst, t):
34 34 """filters out invalid copies after chaining"""
35 35
36 36 # When _chain()'ing copies in 'a' (from 'src' via some other commit 'mid')
37 37 # with copies in 'b' (from 'mid' to 'dst'), we can get the different cases
38 38 # in the following table (not including trivial cases). For example, case 2
39 39 # is where a file existed in 'src' and remained under that name in 'mid' and
40 40 # then was renamed between 'mid' and 'dst'.
41 41 #
42 42 # case src mid dst result
43 43 # 1 x y - -
44 44 # 2 x y y x->y
45 45 # 3 x y x -
46 46 # 4 x y z x->z
47 47 # 5 - x y -
48 48 # 6 x x y x->y
49 49 #
50 50 # _chain() takes care of chaining the copies in 'a' and 'b', but it
51 51 # cannot tell the difference between cases 1 and 2, between 3 and 4, or
52 52 # between 5 and 6, so it includes all cases in its result.
53 53 # Cases 1, 3, and 5 are then removed by _filter().
54 54
55 55 for k, v in list(t.items()):
56 56 # remove copies from files that didn't exist
57 57 if v not in src:
58 58 del t[k]
59 59 # remove criss-crossed copies
60 60 elif k in src and v in dst:
61 61 del t[k]
62 62 # remove copies to files that were then removed
63 63 elif k not in dst:
64 64 del t[k]
65 65
66 66
67 67 def _chain(prefix, suffix):
68 68 """chain two sets of copies 'prefix' and 'suffix'"""
69 69 result = prefix.copy()
70 70 for key, value in pycompat.iteritems(suffix):
71 71 result[key] = prefix.get(value, value)
72 72 return result
73 73
74 74
75 75 def _tracefile(fctx, am, basemf):
76 76 """return file context that is the ancestor of fctx present in ancestor
77 77 manifest am
78 78
79 79 Note: we used to try and stop after a given limit, however checking if that
80 80 limit is reached turned out to be very expensive. we are better off
81 81 disabling that feature."""
82 82
83 83 for f in fctx.ancestors():
84 84 path = f.path()
85 85 if am.get(path, None) == f.filenode():
86 86 return path
87 87 if basemf and basemf.get(path, None) == f.filenode():
88 88 return path
89 89
90 90
91 91 def _dirstatecopies(repo, match=None):
92 92 ds = repo.dirstate
93 93 c = ds.copies().copy()
94 94 for k in list(c):
95 95 if ds[k] not in b'anm' or (match and not match(k)):
96 96 del c[k]
97 97 return c
98 98
99 99
100 100 def _computeforwardmissing(a, b, match=None):
101 101 """Computes which files are in b but not a.
102 102 This is its own function so extensions can easily wrap this call to see what
103 103 files _forwardcopies is about to process.
104 104 """
105 105 ma = a.manifest()
106 106 mb = b.manifest()
107 107 return mb.filesnotin(ma, match=match)
108 108
109 109
110 110 def usechangesetcentricalgo(repo):
111 111 """Checks if we should use changeset-centric copy algorithms"""
112 112 if repo.filecopiesmode == b'changeset-sidedata':
113 113 return True
114 114 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
115 115 changesetsource = (b'changeset-only', b'compatibility')
116 116 return readfrom in changesetsource
117 117
118 118
119 119 def _committedforwardcopies(a, b, base, match):
120 120 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
121 121 # files might have to be traced back to the fctx parent of the last
122 122 # one-side-only changeset, but not further back than that
123 123 repo = a._repo
124 124
125 125 if usechangesetcentricalgo(repo):
126 126 return _changesetforwardcopies(a, b, match)
127 127
128 128 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
129 129 dbg = repo.ui.debug
130 130 if debug:
131 131 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
132 132 am = a.manifest()
133 133 basemf = None if base is None else base.manifest()
134 134
135 135 # find where new files came from
136 136 # we currently don't try to find where old files went, too expensive
137 137 # this means we can miss a case like 'hg rm b; hg cp a b'
138 138 cm = {}
139 139
140 140 # Computing the forward missing is quite expensive on large manifests, since
141 141 # it compares the entire manifests. We can optimize it in the common use
142 142 # case of computing what copies are in a commit versus its parent (like
143 143 # during a rebase or histedit). Note, we exclude merge commits from this
144 144 # optimization, since the ctx.files() for a merge commit is not correct for
145 145 # this comparison.
146 146 forwardmissingmatch = match
147 147 if b.p1() == a and b.p2().node() == node.nullid:
148 148 filesmatcher = matchmod.exact(b.files())
149 149 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
150 150 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
151 151
152 152 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
153 153
154 154 if debug:
155 155 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
156 156
157 157 for f in sorted(missing):
158 158 if debug:
159 159 dbg(b'debug.copies: tracing file: %s\n' % f)
160 160 fctx = b[f]
161 161 fctx._ancestrycontext = ancestrycontext
162 162
163 163 if debug:
164 164 start = util.timer()
165 165 opath = _tracefile(fctx, am, basemf)
166 166 if opath:
167 167 if debug:
168 168 dbg(b'debug.copies: rename of: %s\n' % opath)
169 169 cm[f] = opath
170 170 if debug:
171 171 dbg(
172 172 b'debug.copies: time: %f seconds\n'
173 173 % (util.timer() - start)
174 174 )
175 175 return cm
176 176
177 177
178 178 def _revinfogetter(repo):
179 179 """return a function that return multiple data given a <rev>"i
180 180
181 181 * p1: revision number of first parent
182 182 * p2: revision number of first parent
183 183 * p1copies: mapping of copies from p1
184 184 * p2copies: mapping of copies from p2
185 185 * removed: a list of removed files
186 186 """
187 187 cl = repo.changelog
188 188 parents = cl.parentrevs
189 189
190 190 if repo.filecopiesmode == b'changeset-sidedata':
191 191 changelogrevision = cl.changelogrevision
192 192 flags = cl.flags
193 193
194 194 # A small cache to avoid doing the work twice for merges
195 195 #
196 196 # In the vast majority of cases, if we ask information for a revision
197 197 # about 1 parent, we'll later ask it for the other. So it make sense to
198 198 # keep the information around when reaching the first parent of a merge
199 199 # and dropping it after it was provided for the second parents.
200 200 #
201 201 # It exists cases were only one parent of the merge will be walked. It
202 202 # happens when the "destination" the copy tracing is descendant from a
203 203 # new root, not common with the "source". In that case, we will only walk
204 204 # through merge parents that are descendant of changesets common
205 205 # between "source" and "destination".
206 206 #
207 207 # With the current case implementation if such changesets have a copy
208 208 # information, we'll keep them in memory until the end of
209 209 # _changesetforwardcopies. We don't expect the case to be frequent
210 210 # enough to matters.
211 211 #
212 212 # In addition, it would be possible to reach pathological case, were
213 213 # many first parent are met before any second parent is reached. In
214 214 # that case the cache could grow. If this even become an issue one can
215 215 # safely introduce a maximum cache size. This would trade extra CPU/IO
216 216 # time to save memory.
217 217 merge_caches = {}
218 218
219 219 def revinfo(rev):
220 220 p1, p2 = parents(rev)
221 221 if flags(rev) & REVIDX_SIDEDATA:
222 222 e = merge_caches.pop(rev, None)
223 223 if e is not None:
224 224 return e
225 225 c = changelogrevision(rev)
226 226 p1copies = c.p1copies
227 227 p2copies = c.p2copies
228 228 removed = c.filesremoved
229 229 if p1 != node.nullrev and p2 != node.nullrev:
230 230 # XXX some case we over cache, IGNORE
231 231 merge_caches[rev] = (p1, p2, p1copies, p2copies, removed)
232 232 else:
233 233 p1copies = {}
234 234 p2copies = {}
235 235 removed = []
236 236 return p1, p2, p1copies, p2copies, removed
237 237
238 238 else:
239 239
240 240 def revinfo(rev):
241 241 p1, p2 = parents(rev)
242 242 ctx = repo[rev]
243 243 p1copies, p2copies = ctx._copies
244 244 removed = ctx.filesremoved()
245 245 return p1, p2, p1copies, p2copies, removed
246 246
247 247 return revinfo
248 248
249 249
250 250 def _changesetforwardcopies(a, b, match):
251 251 if a.rev() in (node.nullrev, b.rev()):
252 252 return {}
253 253
254 254 repo = a.repo().unfiltered()
255 255 children = {}
256 256 revinfo = _revinfogetter(repo)
257 257
258 258 cl = repo.changelog
259 259 missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
260 260 mrset = set(missingrevs)
261 261 roots = set()
262 262 for r in missingrevs:
263 263 for p in cl.parentrevs(r):
264 264 if p == node.nullrev:
265 265 continue
266 266 if p not in children:
267 267 children[p] = [r]
268 268 else:
269 269 children[p].append(r)
270 270 if p not in mrset:
271 271 roots.add(p)
272 272 if not roots:
273 273 # no common revision to track copies from
274 274 return {}
275 275 min_root = min(roots)
276 276
277 277 from_head = set(
278 278 cl.reachableroots(min_root, [b.rev()], list(roots), includepath=True)
279 279 )
280 280
281 281 iterrevs = set(from_head)
282 282 iterrevs &= mrset
283 283 iterrevs.update(roots)
284 284 iterrevs.remove(b.rev())
285 285 revs = sorted(iterrevs)
286 286 return _combinechangesetcopies(revs, children, b.rev(), revinfo, match)
287 287
288 288
289 289 def _combinechangesetcopies(revs, children, targetrev, revinfo, match):
290 290 """combine the copies information for each item of iterrevs
291 291
292 292 revs: sorted iterable of revision to visit
293 293 children: a {parent: [children]} mapping.
294 294 targetrev: the final copies destination revision (not in iterrevs)
295 295 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
296 296 match: a matcher
297 297
298 298 It returns the aggregated copies information for `targetrev`.
299 299 """
300 300 all_copies = {}
301 301 alwaysmatch = match.always()
302 302 for r in revs:
303 303 copies = all_copies.pop(r, None)
304 304 if copies is None:
305 305 # this is a root
306 306 copies = {}
307 307 for i, c in enumerate(children[r]):
308 308 p1, p2, p1copies, p2copies, removed = revinfo(c)
309 309 if r == p1:
310 310 parent = 1
311 311 childcopies = p1copies
312 312 else:
313 313 assert r == p2
314 314 parent = 2
315 315 childcopies = p2copies
316 316 if not alwaysmatch:
317 317 childcopies = {
318 318 dst: src for dst, src in childcopies.items() if match(dst)
319 319 }
320 320 newcopies = copies
321 321 if childcopies:
322 322 newcopies = _chain(newcopies, childcopies)
323 323 # _chain makes a copies, we can avoid doing so in some
324 324 # simple/linear cases.
325 325 assert newcopies is not copies
326 326 for f in removed:
327 327 if f in newcopies:
328 328 if newcopies is copies:
329 329 # copy on write to avoid affecting potential other
330 330 # branches. when there are no other branches, this
331 331 # could be avoided.
332 332 newcopies = copies.copy()
333 333 del newcopies[f]
334 334 othercopies = all_copies.get(c)
335 335 if othercopies is None:
336 336 all_copies[c] = newcopies
337 337 else:
338 338 # we are the second parent to work on c, we need to merge our
339 339 # work with the other.
340 340 #
341 341 # Unlike when copies are stored in the filelog, we consider
342 342 # it a copy even if the destination already existed on the
343 343 # other branch. It's simply too expensive to check if the
344 344 # file existed in the manifest.
345 345 #
346 346 # In case of conflict, parent 1 take precedence over parent 2.
347 347 # This is an arbitrary choice made anew when implementing
348 348 # changeset based copies. It was made without regards with
349 349 # potential filelog related behavior.
350 350 if parent == 1:
351 351 othercopies.update(newcopies)
352 352 else:
353 353 newcopies.update(othercopies)
354 354 all_copies[c] = newcopies
355 355 return all_copies[targetrev]
356 356
357 357
358 358 def _forwardcopies(a, b, base=None, match=None):
359 359 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
360 360
361 361 if base is None:
362 362 base = a
363 363 match = a.repo().narrowmatch(match)
364 364 # check for working copy
365 365 if b.rev() is None:
366 366 cm = _committedforwardcopies(a, b.p1(), base, match)
367 367 # combine copies from dirstate if necessary
368 368 copies = _chain(cm, _dirstatecopies(b._repo, match))
369 369 else:
370 370 copies = _committedforwardcopies(a, b, base, match)
371 371 return copies
372 372
373 373
374 374 def _backwardrenames(a, b, match):
375 375 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
376 376 return {}
377 377
378 378 # Even though we're not taking copies into account, 1:n rename situations
379 379 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
380 380 # arbitrarily pick one of the renames.
381 381 # We don't want to pass in "match" here, since that would filter
382 382 # the destination by it. Since we're reversing the copies, we want
383 383 # to filter the source instead.
384 384 f = _forwardcopies(b, a)
385 385 r = {}
386 386 for k, v in sorted(pycompat.iteritems(f)):
387 387 if match and not match(v):
388 388 continue
389 389 # remove copies
390 390 if v in a:
391 391 continue
392 392 r[v] = k
393 393 return r
394 394
395 395
396 396 def pathcopies(x, y, match=None):
397 397 """find {dst@y: src@x} copy mapping for directed compare"""
398 398 repo = x._repo
399 399 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
400 400 if debug:
401 401 repo.ui.debug(
402 402 b'debug.copies: searching copies from %s to %s\n' % (x, y)
403 403 )
404 404 if x == y or not x or not y:
405 405 return {}
406 406 a = y.ancestor(x)
407 407 if a == x:
408 408 if debug:
409 409 repo.ui.debug(b'debug.copies: search mode: forward\n')
410 410 if y.rev() is None and x == y.p1():
411 411 # short-circuit to avoid issues with merge states
412 412 return _dirstatecopies(repo, match)
413 413 copies = _forwardcopies(x, y, match=match)
414 414 elif a == y:
415 415 if debug:
416 416 repo.ui.debug(b'debug.copies: search mode: backward\n')
417 417 copies = _backwardrenames(x, y, match=match)
418 418 else:
419 419 if debug:
420 420 repo.ui.debug(b'debug.copies: search mode: combined\n')
421 421 base = None
422 422 if a.rev() != node.nullrev:
423 423 base = x
424 424 copies = _chain(
425 425 _backwardrenames(x, a, match=match),
426 426 _forwardcopies(a, y, base, match=match),
427 427 )
428 428 _filter(x, y, copies)
429 429 return copies
430 430
431 431
432 432 def mergecopies(repo, c1, c2, base):
433 433 """
434 434 Finds moves and copies between context c1 and c2 that are relevant for
435 435 merging. 'base' will be used as the merge base.
436 436
437 437 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
438 438 files that were moved/ copied in one merge parent and modified in another.
439 439 For example:
440 440
441 441 o ---> 4 another commit
442 442 |
443 443 | o ---> 3 commit that modifies a.txt
444 444 | /
445 445 o / ---> 2 commit that moves a.txt to b.txt
446 446 |/
447 447 o ---> 1 merge base
448 448
449 449 If we try to rebase revision 3 on revision 4, since there is no a.txt in
450 450 revision 4, and if user have copytrace disabled, we prints the following
451 451 message:
452 452
453 453 ```other changed <file> which local deleted```
454 454
455 455 Returns a tuple where:
456 456
457 457 "branch_copies" an instance of branch_copies.
458 458
459 459 "diverge" is a mapping of source name -> list of destination names
460 460 for divergent renames.
461 461
462 462 This function calls different copytracing algorithms based on config.
463 463 """
464 464 # avoid silly behavior for update from empty dir
465 465 if not c1 or not c2 or c1 == c2:
466 466 return branch_copies(), branch_copies(), {}
467 467
468 468 narrowmatch = c1.repo().narrowmatch()
469 469
470 470 # avoid silly behavior for parent -> working dir
471 471 if c2.node() is None and c1.node() == repo.dirstate.p1():
472 472 return (
473 473 branch_copies(_dirstatecopies(repo, narrowmatch)),
474 474 branch_copies(),
475 475 {},
476 476 )
477 477
478 478 copytracing = repo.ui.config(b'experimental', b'copytrace')
479 479 if stringutil.parsebool(copytracing) is False:
480 480 # stringutil.parsebool() returns None when it is unable to parse the
481 481 # value, so we should rely on making sure copytracing is on such cases
482 482 return branch_copies(), branch_copies(), {}
483 483
484 484 if usechangesetcentricalgo(repo):
485 485 # The heuristics don't make sense when we need changeset-centric algos
486 486 return _fullcopytracing(repo, c1, c2, base)
487 487
488 488 # Copy trace disabling is explicitly below the node == p1 logic above
489 489 # because the logic above is required for a simple copy to be kept across a
490 490 # rebase.
491 491 if copytracing == b'heuristics':
492 492 # Do full copytracing if only non-public revisions are involved as
493 493 # that will be fast enough and will also cover the copies which could
494 494 # be missed by heuristics
495 495 if _isfullcopytraceable(repo, c1, base):
496 496 return _fullcopytracing(repo, c1, c2, base)
497 497 return _heuristicscopytracing(repo, c1, c2, base)
498 498 else:
499 499 return _fullcopytracing(repo, c1, c2, base)
500 500
501 501
502 502 def _isfullcopytraceable(repo, c1, base):
503 503 """ Checks that if base, source and destination are all no-public branches,
504 504 if yes let's use the full copytrace algorithm for increased capabilities
505 505 since it will be fast enough.
506 506
507 507 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
508 508 number of changesets from c1 to base such that if number of changesets are
509 509 more than the limit, full copytracing algorithm won't be used.
510 510 """
511 511 if c1.rev() is None:
512 512 c1 = c1.p1()
513 513 if c1.mutable() and base.mutable():
514 514 sourcecommitlimit = repo.ui.configint(
515 515 b'experimental', b'copytrace.sourcecommitlimit'
516 516 )
517 517 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
518 518 return commits < sourcecommitlimit
519 519 return False
520 520
521 521
522 522 def _checksinglesidecopies(
523 523 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
524 524 ):
525 525 if src not in m2:
526 526 # deleted on side 2
527 527 if src not in m1:
528 528 # renamed on side 1, deleted on side 2
529 529 renamedelete[src] = dsts1
530 530 elif src not in mb:
531 531 # Work around the "short-circuit to avoid issues with merge states"
532 532 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
533 533 # destination doesn't exist in y.
534 534 pass
535 535 elif m2[src] != mb[src]:
536 536 if not _related(c2[src], base[src]):
537 537 return
538 538 # modified on side 2
539 539 for dst in dsts1:
540 if dst not in m2:
541 # dst not added on side 2 (handle as regular
542 # "both created" case in manifestmerge otherwise)
543 540 copy[dst] = src
544 541
545 542
546 543 class branch_copies(object):
547 544 """Information about copies made on one side of a merge/graft.
548 545
549 546 "copy" is a mapping from destination name -> source name,
550 547 where source is in c1 and destination is in c2 or vice-versa.
551 548
552 549 "movewithdir" is a mapping from source name -> destination name,
553 550 where the file at source present in one context but not the other
554 551 needs to be moved to destination by the merge process, because the
555 552 other context moved the directory it is in.
556 553
557 554 "renamedelete" is a mapping of source name -> list of destination
558 555 names for files deleted in c1 that were renamed in c2 or vice-versa.
559 556
560 557 "dirmove" is a mapping of detected source dir -> destination dir renames.
561 558 This is needed for handling changes to new files previously grafted into
562 559 renamed directories.
563 560 """
564 561
565 562 def __init__(
566 563 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
567 564 ):
568 565 self.copy = {} if copy is None else copy
569 566 self.renamedelete = {} if renamedelete is None else renamedelete
570 567 self.dirmove = {} if dirmove is None else dirmove
571 568 self.movewithdir = {} if movewithdir is None else movewithdir
572 569
573 570
574 571 def _fullcopytracing(repo, c1, c2, base):
575 572 """ The full copytracing algorithm which finds all the new files that were
576 573 added from merge base up to the top commit and for each file it checks if
577 574 this file was copied from another file.
578 575
579 576 This is pretty slow when a lot of changesets are involved but will track all
580 577 the copies.
581 578 """
582 579 m1 = c1.manifest()
583 580 m2 = c2.manifest()
584 581 mb = base.manifest()
585 582
586 583 copies1 = pathcopies(base, c1)
587 584 copies2 = pathcopies(base, c2)
588 585
589 586 if not (copies1 or copies2):
590 587 return branch_copies(), branch_copies(), {}
591 588
592 589 inversecopies1 = {}
593 590 inversecopies2 = {}
594 591 for dst, src in copies1.items():
595 592 inversecopies1.setdefault(src, []).append(dst)
596 593 for dst, src in copies2.items():
597 594 inversecopies2.setdefault(src, []).append(dst)
598 595
599 596 copy1 = {}
600 597 copy2 = {}
601 598 diverge = {}
602 599 renamedelete1 = {}
603 600 renamedelete2 = {}
604 601 allsources = set(inversecopies1) | set(inversecopies2)
605 602 for src in allsources:
606 603 dsts1 = inversecopies1.get(src)
607 604 dsts2 = inversecopies2.get(src)
608 605 if dsts1 and dsts2:
609 606 # copied/renamed on both sides
610 607 if src not in m1 and src not in m2:
611 608 # renamed on both sides
612 609 dsts1 = set(dsts1)
613 610 dsts2 = set(dsts2)
614 611 # If there's some overlap in the rename destinations, we
615 612 # consider it not divergent. For example, if side 1 copies 'a'
616 613 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
617 614 # and 'd' and deletes 'a'.
618 615 if dsts1 & dsts2:
619 616 for dst in dsts1 & dsts2:
620 617 copy1[dst] = src
621 618 copy2[dst] = src
622 619 else:
623 620 diverge[src] = sorted(dsts1 | dsts2)
624 621 elif src in m1 and src in m2:
625 622 # copied on both sides
626 623 dsts1 = set(dsts1)
627 624 dsts2 = set(dsts2)
628 625 for dst in dsts1 & dsts2:
629 626 copy1[dst] = src
630 627 copy2[dst] = src
631 628 # TODO: Handle cases where it was renamed on one side and copied
632 629 # on the other side
633 630 elif dsts1:
634 631 # copied/renamed only on side 1
635 632 _checksinglesidecopies(
636 633 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
637 634 )
638 635 elif dsts2:
639 636 # copied/renamed only on side 2
640 637 _checksinglesidecopies(
641 638 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
642 639 )
643 640
644 641 # find interesting file sets from manifests
645 642 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
646 643 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
647 644 u1 = sorted(addedinm1 - addedinm2)
648 645 u2 = sorted(addedinm2 - addedinm1)
649 646
650 647 header = b" unmatched files in %s"
651 648 if u1:
652 649 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
653 650 if u2:
654 651 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
655 652
656 653 if repo.ui.debugflag:
657 654 renamedeleteset = set()
658 655 divergeset = set()
659 656 for dsts in diverge.values():
660 657 divergeset.update(dsts)
661 658 for dsts in renamedelete1.values():
662 659 renamedeleteset.update(dsts)
663 660 for dsts in renamedelete2.values():
664 661 renamedeleteset.update(dsts)
665 662
666 663 repo.ui.debug(
667 664 b" all copies found (* = to merge, ! = divergent, "
668 665 b"% = renamed and deleted):\n"
669 666 )
670 667 for side, copies in ((b"local", copies1), (b"remote", copies2)):
671 668 if not copies:
672 669 continue
673 670 repo.ui.debug(b" on %s side:\n" % side)
674 671 for f in sorted(copies):
675 672 note = b""
676 673 if f in copy1 or f in copy2:
677 674 note += b"*"
678 675 if f in divergeset:
679 676 note += b"!"
680 677 if f in renamedeleteset:
681 678 note += b"%"
682 679 repo.ui.debug(
683 680 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
684 681 )
685 682 del renamedeleteset
686 683 del divergeset
687 684
688 685 repo.ui.debug(b" checking for directory renames\n")
689 686
690 687 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
691 688 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
692 689
693 690 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
694 691 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
695 692
696 693 return branch_copies1, branch_copies2, diverge
697 694
698 695
699 696 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
700 697 """Finds moved directories and files that should move with them.
701 698
702 699 ctx: the context for one of the sides
703 700 copy: files copied on the same side (as ctx)
704 701 fullcopy: files copied on the same side (as ctx), including those that
705 702 merge.manifestmerge() won't care about
706 703 addedfiles: added files on the other side (compared to ctx)
707 704 """
708 705 # generate a directory move map
709 706 d = ctx.dirs()
710 707 invalid = set()
711 708 dirmove = {}
712 709
713 710 # examine each file copy for a potential directory move, which is
714 711 # when all the files in a directory are moved to a new directory
715 712 for dst, src in pycompat.iteritems(fullcopy):
716 713 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
717 714 if dsrc in invalid:
718 715 # already seen to be uninteresting
719 716 continue
720 717 elif dsrc in d and ddst in d:
721 718 # directory wasn't entirely moved locally
722 719 invalid.add(dsrc)
723 720 elif dsrc in dirmove and dirmove[dsrc] != ddst:
724 721 # files from the same directory moved to two different places
725 722 invalid.add(dsrc)
726 723 else:
727 724 # looks good so far
728 725 dirmove[dsrc] = ddst
729 726
730 727 for i in invalid:
731 728 if i in dirmove:
732 729 del dirmove[i]
733 730 del d, invalid
734 731
735 732 if not dirmove:
736 733 return {}, {}
737 734
738 735 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
739 736
740 737 for d in dirmove:
741 738 repo.ui.debug(
742 739 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
743 740 )
744 741
745 742 movewithdir = {}
746 743 # check unaccounted nonoverlapping files against directory moves
747 744 for f in addedfiles:
748 745 if f not in fullcopy:
749 746 for d in dirmove:
750 747 if f.startswith(d):
751 748 # new file added in a directory that was moved, move it
752 749 df = dirmove[d] + f[len(d) :]
753 750 if df not in copy:
754 751 movewithdir[f] = df
755 752 repo.ui.debug(
756 753 b" pending file src: '%s' -> dst: '%s'\n"
757 754 % (f, df)
758 755 )
759 756 break
760 757
761 758 return dirmove, movewithdir
762 759
763 760
764 761 def _heuristicscopytracing(repo, c1, c2, base):
765 762 """ Fast copytracing using filename heuristics
766 763
767 764 Assumes that moves or renames are of following two types:
768 765
769 766 1) Inside a directory only (same directory name but different filenames)
770 767 2) Move from one directory to another
771 768 (same filenames but different directory names)
772 769
773 770 Works only when there are no merge commits in the "source branch".
774 771 Source branch is commits from base up to c2 not including base.
775 772
776 773 If merge is involved it fallbacks to _fullcopytracing().
777 774
778 775 Can be used by setting the following config:
779 776
780 777 [experimental]
781 778 copytrace = heuristics
782 779
783 780 In some cases the copy/move candidates found by heuristics can be very large
784 781 in number and that will make the algorithm slow. The number of possible
785 782 candidates to check can be limited by using the config
786 783 `experimental.copytrace.movecandidateslimit` which defaults to 100.
787 784 """
788 785
789 786 if c1.rev() is None:
790 787 c1 = c1.p1()
791 788 if c2.rev() is None:
792 789 c2 = c2.p1()
793 790
794 791 changedfiles = set()
795 792 m1 = c1.manifest()
796 793 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
797 794 # If base is not in c2 branch, we switch to fullcopytracing
798 795 repo.ui.debug(
799 796 b"switching to full copytracing as base is not "
800 797 b"an ancestor of c2\n"
801 798 )
802 799 return _fullcopytracing(repo, c1, c2, base)
803 800
804 801 ctx = c2
805 802 while ctx != base:
806 803 if len(ctx.parents()) == 2:
807 804 # To keep things simple let's not handle merges
808 805 repo.ui.debug(b"switching to full copytracing because of merges\n")
809 806 return _fullcopytracing(repo, c1, c2, base)
810 807 changedfiles.update(ctx.files())
811 808 ctx = ctx.p1()
812 809
813 810 copies2 = {}
814 811 cp = _forwardcopies(base, c2)
815 812 for dst, src in pycompat.iteritems(cp):
816 813 if src in m1:
817 814 copies2[dst] = src
818 815
819 816 # file is missing if it isn't present in the destination, but is present in
820 817 # the base and present in the source.
821 818 # Presence in the base is important to exclude added files, presence in the
822 819 # source is important to exclude removed files.
823 820 filt = lambda f: f not in m1 and f in base and f in c2
824 821 missingfiles = [f for f in changedfiles if filt(f)]
825 822
826 823 copies1 = {}
827 824 if missingfiles:
828 825 basenametofilename = collections.defaultdict(list)
829 826 dirnametofilename = collections.defaultdict(list)
830 827
831 828 for f in m1.filesnotin(base.manifest()):
832 829 basename = os.path.basename(f)
833 830 dirname = os.path.dirname(f)
834 831 basenametofilename[basename].append(f)
835 832 dirnametofilename[dirname].append(f)
836 833
837 834 for f in missingfiles:
838 835 basename = os.path.basename(f)
839 836 dirname = os.path.dirname(f)
840 837 samebasename = basenametofilename[basename]
841 838 samedirname = dirnametofilename[dirname]
842 839 movecandidates = samebasename + samedirname
843 840 # f is guaranteed to be present in c2, that's why
844 841 # c2.filectx(f) won't fail
845 842 f2 = c2.filectx(f)
846 843 # we can have a lot of candidates which can slow down the heuristics
847 844 # config value to limit the number of candidates moves to check
848 845 maxcandidates = repo.ui.configint(
849 846 b'experimental', b'copytrace.movecandidateslimit'
850 847 )
851 848
852 849 if len(movecandidates) > maxcandidates:
853 850 repo.ui.status(
854 851 _(
855 852 b"skipping copytracing for '%s', more "
856 853 b"candidates than the limit: %d\n"
857 854 )
858 855 % (f, len(movecandidates))
859 856 )
860 857 continue
861 858
862 859 for candidate in movecandidates:
863 860 f1 = c1.filectx(candidate)
864 861 if _related(f1, f2):
865 862 # if there are a few related copies then we'll merge
866 863 # changes into all of them. This matches the behaviour
867 864 # of upstream copytracing
868 865 copies1[candidate] = f
869 866
870 867 return branch_copies(copies1), branch_copies(copies2), {}
871 868
872 869
873 870 def _related(f1, f2):
874 871 """return True if f1 and f2 filectx have a common ancestor
875 872
876 873 Walk back to common ancestor to see if the two files originate
877 874 from the same file. Since workingfilectx's rev() is None it messes
878 875 up the integer comparison logic, hence the pre-step check for
879 876 None (f1 and f2 can only be workingfilectx's initially).
880 877 """
881 878
882 879 if f1 == f2:
883 880 return True # a match
884 881
885 882 g1, g2 = f1.ancestors(), f2.ancestors()
886 883 try:
887 884 f1r, f2r = f1.linkrev(), f2.linkrev()
888 885
889 886 if f1r is None:
890 887 f1 = next(g1)
891 888 if f2r is None:
892 889 f2 = next(g2)
893 890
894 891 while True:
895 892 f1r, f2r = f1.linkrev(), f2.linkrev()
896 893 if f1r > f2r:
897 894 f1 = next(g1)
898 895 elif f2r > f1r:
899 896 f2 = next(g2)
900 897 else: # f1 and f2 point to files in the same linkrev
901 898 return f1 == f2 # true if they point to the same file
902 899 except StopIteration:
903 900 return False
904 901
905 902
906 903 def graftcopies(wctx, ctx, base):
907 904 """reproduce copies between base and ctx in the wctx
908 905
909 906 Unlike mergecopies(), this function will only consider copies between base
910 907 and ctx; it will ignore copies between base and wctx. Also unlike
911 908 mergecopies(), this function will apply copies to the working copy (instead
912 909 of just returning information about the copies). That makes it cheaper
913 910 (especially in the common case of base==ctx.p1()) and useful also when
914 911 experimental.copytrace=off.
915 912
916 913 merge.update() will have already marked most copies, but it will only
917 914 mark copies if it thinks the source files are related (see
918 915 merge._related()). It will also not mark copies if the file wasn't modified
919 916 on the local side. This function adds the copies that were "missed"
920 917 by merge.update().
921 918 """
922 919 new_copies = pathcopies(base, ctx)
923 920 _filter(wctx.p1(), wctx, new_copies)
924 921 for dst, src in pycompat.iteritems(new_copies):
925 922 wctx[dst].markcopied(src)
926 923
927 924
928 925 def computechangesetfilesadded(ctx):
929 926 """return the list of files added in a changeset
930 927 """
931 928 added = []
932 929 for f in ctx.files():
933 930 if not any(f in p for p in ctx.parents()):
934 931 added.append(f)
935 932 return added
936 933
937 934
938 935 def computechangesetfilesremoved(ctx):
939 936 """return the list of files removed in a changeset
940 937 """
941 938 removed = []
942 939 for f in ctx.files():
943 940 if f not in ctx:
944 941 removed.append(f)
945 942 return removed
946 943
947 944
948 945 def computechangesetcopies(ctx):
949 946 """return the copies data for a changeset
950 947
951 948 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
952 949
953 950 Each dictionnary are in the form: `{newname: oldname}`
954 951 """
955 952 p1copies = {}
956 953 p2copies = {}
957 954 p1 = ctx.p1()
958 955 p2 = ctx.p2()
959 956 narrowmatch = ctx._repo.narrowmatch()
960 957 for dst in ctx.files():
961 958 if not narrowmatch(dst) or dst not in ctx:
962 959 continue
963 960 copied = ctx[dst].renamed()
964 961 if not copied:
965 962 continue
966 963 src, srcnode = copied
967 964 if src in p1 and p1[src].filenode() == srcnode:
968 965 p1copies[dst] = src
969 966 elif src in p2 and p2[src].filenode() == srcnode:
970 967 p2copies[dst] = src
971 968 return p1copies, p2copies
972 969
973 970
974 971 def encodecopies(files, copies):
975 972 items = []
976 973 for i, dst in enumerate(files):
977 974 if dst in copies:
978 975 items.append(b'%d\0%s' % (i, copies[dst]))
979 976 if len(items) != len(copies):
980 977 raise error.ProgrammingError(
981 978 b'some copy targets missing from file list'
982 979 )
983 980 return b"\n".join(items)
984 981
985 982
986 983 def decodecopies(files, data):
987 984 try:
988 985 copies = {}
989 986 if not data:
990 987 return copies
991 988 for l in data.split(b'\n'):
992 989 strindex, src = l.split(b'\0')
993 990 i = int(strindex)
994 991 dst = files[i]
995 992 copies[dst] = src
996 993 return copies
997 994 except (ValueError, IndexError):
998 995 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
999 996 # used different syntax for the value.
1000 997 return None
1001 998
1002 999
1003 1000 def encodefileindices(files, subset):
1004 1001 subset = set(subset)
1005 1002 indices = []
1006 1003 for i, f in enumerate(files):
1007 1004 if f in subset:
1008 1005 indices.append(b'%d' % i)
1009 1006 return b'\n'.join(indices)
1010 1007
1011 1008
1012 1009 def decodefileindices(files, data):
1013 1010 try:
1014 1011 subset = []
1015 1012 if not data:
1016 1013 return subset
1017 1014 for strindex in data.split(b'\n'):
1018 1015 i = int(strindex)
1019 1016 if i < 0 or i >= len(files):
1020 1017 return None
1021 1018 subset.append(files[i])
1022 1019 return subset
1023 1020 except (ValueError, IndexError):
1024 1021 # Perhaps someone had chosen the same key name (e.g. "added") and
1025 1022 # used different syntax for the value.
1026 1023 return None
1027 1024
1028 1025
1029 1026 def _getsidedata(srcrepo, rev):
1030 1027 ctx = srcrepo[rev]
1031 1028 filescopies = computechangesetcopies(ctx)
1032 1029 filesadded = computechangesetfilesadded(ctx)
1033 1030 filesremoved = computechangesetfilesremoved(ctx)
1034 1031 sidedata = {}
1035 1032 if any([filescopies, filesadded, filesremoved]):
1036 1033 sortedfiles = sorted(ctx.files())
1037 1034 p1copies, p2copies = filescopies
1038 1035 p1copies = encodecopies(sortedfiles, p1copies)
1039 1036 p2copies = encodecopies(sortedfiles, p2copies)
1040 1037 filesadded = encodefileindices(sortedfiles, filesadded)
1041 1038 filesremoved = encodefileindices(sortedfiles, filesremoved)
1042 1039 if p1copies:
1043 1040 sidedata[sidedatamod.SD_P1COPIES] = p1copies
1044 1041 if p2copies:
1045 1042 sidedata[sidedatamod.SD_P2COPIES] = p2copies
1046 1043 if filesadded:
1047 1044 sidedata[sidedatamod.SD_FILESADDED] = filesadded
1048 1045 if filesremoved:
1049 1046 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
1050 1047 return sidedata
1051 1048
1052 1049
1053 1050 def getsidedataadder(srcrepo, destrepo):
1054 1051 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
1055 1052 if pycompat.iswindows or not use_w:
1056 1053 return _get_simple_sidedata_adder(srcrepo, destrepo)
1057 1054 else:
1058 1055 return _get_worker_sidedata_adder(srcrepo, destrepo)
1059 1056
1060 1057
1061 1058 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
1062 1059 """The function used by worker precomputing sidedata
1063 1060
1064 1061 It read an input queue containing revision numbers
1065 1062 It write in an output queue containing (rev, <sidedata-map>)
1066 1063
1067 1064 The `None` input value is used as a stop signal.
1068 1065
1069 1066 The `tokens` semaphore is user to avoid having too many unprocessed
1070 1067 entries. The workers needs to acquire one token before fetching a task.
1071 1068 They will be released by the consumer of the produced data.
1072 1069 """
1073 1070 tokens.acquire()
1074 1071 rev = revs_queue.get()
1075 1072 while rev is not None:
1076 1073 data = _getsidedata(srcrepo, rev)
1077 1074 sidedata_queue.put((rev, data))
1078 1075 tokens.acquire()
1079 1076 rev = revs_queue.get()
1080 1077 # processing of `None` is completed, release the token.
1081 1078 tokens.release()
1082 1079
1083 1080
1084 1081 BUFF_PER_WORKER = 50
1085 1082
1086 1083
1087 1084 def _get_worker_sidedata_adder(srcrepo, destrepo):
1088 1085 """The parallel version of the sidedata computation
1089 1086
1090 1087 This code spawn a pool of worker that precompute a buffer of sidedata
1091 1088 before we actually need them"""
1092 1089 # avoid circular import copies -> scmutil -> worker -> copies
1093 1090 from . import worker
1094 1091
1095 1092 nbworkers = worker._numworkers(srcrepo.ui)
1096 1093
1097 1094 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
1098 1095 revsq = multiprocessing.Queue()
1099 1096 sidedataq = multiprocessing.Queue()
1100 1097
1101 1098 assert srcrepo.filtername is None
1102 1099 # queue all tasks beforehand, revision numbers are small and it make
1103 1100 # synchronisation simpler
1104 1101 #
1105 1102 # Since the computation for each node can be quite expensive, the overhead
1106 1103 # of using a single queue is not revelant. In practice, most computation
1107 1104 # are fast but some are very expensive and dominate all the other smaller
1108 1105 # cost.
1109 1106 for r in srcrepo.changelog.revs():
1110 1107 revsq.put(r)
1111 1108 # queue the "no more tasks" markers
1112 1109 for i in range(nbworkers):
1113 1110 revsq.put(None)
1114 1111
1115 1112 allworkers = []
1116 1113 for i in range(nbworkers):
1117 1114 args = (srcrepo, revsq, sidedataq, tokens)
1118 1115 w = multiprocessing.Process(target=_sidedata_worker, args=args)
1119 1116 allworkers.append(w)
1120 1117 w.start()
1121 1118
1122 1119 # dictionnary to store results for revision higher than we one we are
1123 1120 # looking for. For example, if we need the sidedatamap for 42, and 43 is
1124 1121 # received, when shelve 43 for later use.
1125 1122 staging = {}
1126 1123
1127 1124 def sidedata_companion(revlog, rev):
1128 1125 sidedata = {}
1129 1126 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
1130 1127 # Is the data previously shelved ?
1131 1128 sidedata = staging.pop(rev, None)
1132 1129 if sidedata is None:
1133 1130 # look at the queued result until we find the one we are lookig
1134 1131 # for (shelve the other ones)
1135 1132 r, sidedata = sidedataq.get()
1136 1133 while r != rev:
1137 1134 staging[r] = sidedata
1138 1135 r, sidedata = sidedataq.get()
1139 1136 tokens.release()
1140 1137 return False, (), sidedata
1141 1138
1142 1139 return sidedata_companion
1143 1140
1144 1141
1145 1142 def _get_simple_sidedata_adder(srcrepo, destrepo):
1146 1143 """The simple version of the sidedata computation
1147 1144
1148 1145 It just compute it in the same thread on request"""
1149 1146
1150 1147 def sidedatacompanion(revlog, rev):
1151 1148 sidedata = {}
1152 1149 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1153 1150 sidedata = _getsidedata(srcrepo, rev)
1154 1151 return False, (), sidedata
1155 1152
1156 1153 return sidedatacompanion
1157 1154
1158 1155
1159 1156 def getsidedataremover(srcrepo, destrepo):
1160 1157 def sidedatacompanion(revlog, rev):
1161 1158 f = ()
1162 1159 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1163 1160 if revlog.flags(rev) & REVIDX_SIDEDATA:
1164 1161 f = (
1165 1162 sidedatamod.SD_P1COPIES,
1166 1163 sidedatamod.SD_P2COPIES,
1167 1164 sidedatamod.SD_FILESADDED,
1168 1165 sidedatamod.SD_FILESREMOVED,
1169 1166 )
1170 1167 return False, f, {}
1171 1168
1172 1169 return sidedatacompanion
@@ -1,262 +1,236 b''
1 1 $ hg init
2 2
3 3 $ echo "[merge]" >> .hg/hgrc
4 4 $ echo "followcopies = 1" >> .hg/hgrc
5 5
6 6 $ echo foo > a
7 7 $ echo foo > a2
8 8 $ hg add a a2
9 9 $ hg ci -m "start"
10 10
11 11 $ hg mv a b
12 12 $ hg mv a2 b2
13 13 $ hg ci -m "rename"
14 14
15 15 $ hg co 0
16 16 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
17 17
18 18 $ echo blahblah > a
19 19 $ echo blahblah > a2
20 20 $ hg mv a2 c2
21 21 $ hg ci -m "modify"
22 22 created new head
23 23
24 24 $ hg merge -y --debug
25 25 unmatched files in local:
26 26 c2
27 27 unmatched files in other:
28 28 b
29 29 b2
30 30 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
31 31 on local side:
32 32 src: 'a2' -> dst: 'c2' !
33 33 on remote side:
34 34 src: 'a' -> dst: 'b' *
35 35 src: 'a2' -> dst: 'b2' !
36 36 checking for directory renames
37 37 resolving manifests
38 38 branchmerge: True, force: False, partial: False
39 39 ancestor: af1939970a1c, local: 044f8520aeeb+, remote: 85c198ef2f6c
40 40 note: possible conflict - a2 was renamed multiple times to:
41 41 b2
42 42 c2
43 43 preserving a for resolve of b
44 44 removing a
45 45 b2: remote created -> g
46 46 getting b2
47 47 b: remote moved from a -> m (premerge)
48 48 picked tool ':merge' for b (binary False symlink False changedelete False)
49 49 merging a and b to b
50 50 my b@044f8520aeeb+ other b@85c198ef2f6c ancestor a@af1939970a1c
51 51 premerge successful
52 52 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
53 53 (branch merge, don't forget to commit)
54 54
55 55 $ hg status -AC
56 56 M b
57 57 a
58 58 M b2
59 59 R a
60 60 C c2
61 61
62 62 $ cat b
63 63 blahblah
64 64
65 65 $ hg ci -m "merge"
66 66
67 67 $ hg debugindex b
68 68 rev linkrev nodeid p1 p2
69 69 0 1 57eacc201a7f 000000000000 000000000000
70 70 1 3 4727ba907962 000000000000 57eacc201a7f
71 71
72 72 $ hg debugrename b
73 73 b renamed from a:dd03b83622e78778b403775d0d074b9ac7387a66
74 74
75 75 This used to trigger a "divergent renames" warning, despite no renames
76 76
77 77 $ hg cp b b3
78 78 $ hg cp b b4
79 79 $ hg ci -A -m 'copy b twice'
80 80 $ hg up '.^'
81 81 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
82 82 $ hg up
83 83 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 84 $ hg rm b3 b4
85 85 $ hg ci -m 'clean up a bit of our mess'
86 86
87 87 We'd rather not warn on divergent renames done in the same changeset (issue2113)
88 88
89 89 $ hg cp b b3
90 90 $ hg mv b b4
91 91 $ hg ci -A -m 'divergent renames in same changeset'
92 92 $ hg up '.^'
93 93 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
94 94 $ hg up
95 95 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
96 96
97 97 Check for issue2642
98 98
99 99 $ hg init t
100 100 $ cd t
101 101
102 102 $ echo c0 > f1
103 103 $ hg ci -Aqm0
104 104
105 105 $ hg up null -q
106 106 $ echo c1 > f1 # backport
107 107 $ hg ci -Aqm1
108 108 $ hg mv f1 f2
109 109 $ hg ci -qm2
110 110
111 111 $ hg up 0 -q
112 112 $ hg merge 1 -q --tool internal:local
113 113 $ hg ci -qm3
114 114
115 115 $ hg merge 2
116 116 merging f1 and f2 to f2
117 117 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
118 118 (branch merge, don't forget to commit)
119 119
120 120 $ cat f2
121 121 c0
122 122
123 123 $ cd ..
124 124
125 125 Check for issue2089
126 126
127 127 $ hg init repo2089
128 128 $ cd repo2089
129 129
130 130 $ echo c0 > f1
131 131 $ hg ci -Aqm0
132 132
133 133 $ hg up null -q
134 134 $ echo c1 > f1
135 135 $ hg ci -Aqm1
136 136
137 137 $ hg up 0 -q
138 138 $ hg merge 1 -q --tool internal:local
139 139 $ echo c2 > f1
140 140 $ hg ci -qm2
141 141
142 142 $ hg up 1 -q
143 143 $ hg mv f1 f2
144 144 $ hg ci -Aqm3
145 145
146 146 $ hg up 2 -q
147 147 $ hg merge 3
148 148 merging f1 and f2 to f2
149 149 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
150 150 (branch merge, don't forget to commit)
151 151
152 152 $ cat f2
153 153 c2
154 154
155 155 $ cd ..
156 156
157 157 Check for issue3074
158 158
159 159 $ hg init repo3074
160 160 $ cd repo3074
161 161 $ echo foo > file
162 162 $ hg add file
163 163 $ hg commit -m "added file"
164 164 $ hg mv file newfile
165 165 $ hg commit -m "renamed file"
166 166 $ hg update 0
167 167 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
168 168 $ hg rm file
169 169 $ hg commit -m "deleted file"
170 170 created new head
171 171 $ hg merge --debug
172 172 unmatched files in other:
173 173 newfile
174 174 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
175 175 on remote side:
176 176 src: 'file' -> dst: 'newfile' %
177 177 checking for directory renames
178 178 resolving manifests
179 179 branchmerge: True, force: False, partial: False
180 180 ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
181 181 note: possible conflict - file was deleted and renamed to:
182 182 newfile
183 183 newfile: remote created -> g
184 184 getting newfile
185 185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 186 (branch merge, don't forget to commit)
187 187 $ hg status
188 188 M newfile
189 189 $ cd ..
190 190
191 191 Create x and y, then modify y and rename x to z on one side of merge, and
192 192 modify x and rename y to z on the other side.
193 193 $ hg init conflicting-target
194 194 $ cd conflicting-target
195 195 $ echo x > x
196 196 $ echo y > y
197 197 $ hg ci -Aqm 'add x and y'
198 198 $ hg mv x z
199 199 $ echo foo >> y
200 200 $ hg ci -qm 'modify y, rename x to z'
201 201 $ hg co -q 0
202 202 $ hg mv y z
203 203 $ echo foo >> x
204 204 $ hg ci -qm 'modify x, rename y to z'
205 205 # We should probably tell the user about the conflicting rename sources.
206 206 # Depending on which side they pick, we should take that rename and get
207 207 # the changes to the source from the other side. The unchanged file should
208 208 # remain.
209 # we should not get the prompts about modify/delete conflicts
210 209 $ hg merge --debug 1 -t :merge3
211 210 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
212 211 on local side:
213 src: 'y' -> dst: 'z'
212 src: 'y' -> dst: 'z' *
214 213 on remote side:
215 src: 'x' -> dst: 'z'
214 src: 'x' -> dst: 'z' *
216 215 checking for directory renames
217 216 resolving manifests
218 217 branchmerge: True, force: False, partial: False
219 218 ancestor: 5151c134577e, local: 07fcbc9a74ed+, remote: f21419739508
220 preserving x for resolve of x
221 219 preserving z for resolve of z
222 220 starting 4 threads for background file closing (?)
223 x: prompt changed/deleted -> m (premerge)
224 picked tool ':prompt' for x (binary False symlink False changedelete True)
225 file 'x' was deleted in other [merge rev] but was modified in local [working copy].
226 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
227 What do you want to do? u
228 y: prompt deleted/changed -> m (premerge)
229 picked tool ':prompt' for y (binary False symlink False changedelete True)
230 file 'y' was deleted in local [working copy] but was modified in other [merge rev].
231 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
232 What do you want to do? u
233 z: both created -> m (premerge)
221 z: both renamed from y -> m (premerge)
234 222 picked tool ':merge3' for z (binary False symlink False changedelete False)
235 223 merging z
236 my z@07fcbc9a74ed+ other z@f21419739508 ancestor z@000000000000
237 z: both created -> m (merge)
238 picked tool ':merge3' for z (binary False symlink False changedelete False)
239 my z@07fcbc9a74ed+ other z@f21419739508 ancestor z@000000000000
240 warning: conflicts while merging z! (edit, then use 'hg resolve --mark')
241 0 files updated, 0 files merged, 0 files removed, 3 files unresolved
242 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
243 [1]
224 my z@07fcbc9a74ed+ other z@f21419739508 ancestor y@5151c134577e
225 premerge successful
226 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
227 (branch merge, don't forget to commit)
244 228 $ ls
245 229 x
246 y
247 230 z
248 z.orig
249 231 $ cat x
250 232 x
251 233 foo
252 $ cat y
253 y
254 foo
255 234 # 'z' should have had the added 'foo' line
256 235 $ cat z
257 <<<<<<< working copy: 07fcbc9a74ed - test: modify x, rename y to z
258 y
259 ||||||| base
260 =======
261 236 x
262 >>>>>>> merge rev: f21419739508 - test: modify y, rename x to z
@@ -1,1082 +1,1048 b''
1 1
2 2 $ mkdir -p t
3 3 $ cd t
4 4 $ cat <<EOF > merge
5 5 > import sys, os
6 6 > f = open(sys.argv[1], "w")
7 7 > f.write("merge %s %s %s" % (sys.argv[1], sys.argv[2], sys.argv[3]))
8 8 > f.close()
9 9 > EOF
10 10
11 11 perform a test merge with possible renaming
12 12 args:
13 13 $1 = action in local branch
14 14 $2 = action in remote branch
15 15 $3 = action in working dir
16 16 $4 = expected result
17 17
18 18 $ tm()
19 19 > {
20 20 > hg init t
21 21 > cd t
22 22 > echo "[merge]" >> .hg/hgrc
23 23 > echo "followcopies = 1" >> .hg/hgrc
24 24 >
25 25 > # base
26 26 > echo base > a
27 27 > echo base > rev # used to force commits
28 28 > hg add a rev
29 29 > hg ci -m "base"
30 30 >
31 31 > # remote
32 32 > echo remote > rev
33 33 > if [ "$2" != "" ] ; then $2 ; fi
34 34 > hg ci -m "remote"
35 35 >
36 36 > # local
37 37 > hg co -q 0
38 38 > echo local > rev
39 39 > if [ "$1" != "" ] ; then $1 ; fi
40 40 > hg ci -m "local"
41 41 >
42 42 > # working dir
43 43 > echo local > rev
44 44 > if [ "$3" != "" ] ; then $3 ; fi
45 45 >
46 46 > # merge
47 47 > echo "--------------"
48 48 > echo "test L:$1 R:$2 W:$3 - $4"
49 49 > echo "--------------"
50 50 > hg merge -y --debug --traceback --tool="\"$PYTHON\" ../merge"
51 51 >
52 52 > echo "--------------"
53 53 > hg status -camC -X rev
54 54 >
55 55 > hg ci -m "merge"
56 56 >
57 57 > echo "--------------"
58 58 > echo
59 59 >
60 60 > cd ..
61 61 > rm -r t
62 62 > }
63 63 $ up() {
64 64 > cp rev $1
65 65 > hg add $1 2> /dev/null
66 66 > if [ "$2" != "" ] ; then
67 67 > cp rev $2
68 68 > hg add $2 2> /dev/null
69 69 > fi
70 70 > }
71 71 $ um() { up $1; hg mv $1 $2; }
72 72 $ nc() { hg cp $1 $2; } # just copy
73 73 $ nm() { hg mv $1 $2; } # just move
74 74 $ tm "up a " "nc a b" " " "1 get local a to b"
75 75 created new head
76 76 --------------
77 77 test L:up a R:nc a b W: - 1 get local a to b
78 78 --------------
79 79 unmatched files in other:
80 80 b
81 81 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
82 82 on remote side:
83 83 src: 'a' -> dst: 'b' *
84 84 checking for directory renames
85 85 resolving manifests
86 86 branchmerge: True, force: False, partial: False
87 87 ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
88 88 preserving a for resolve of b
89 89 preserving rev for resolve of rev
90 90 starting 4 threads for background file closing (?)
91 91 b: remote copied from a -> m (premerge)
92 92 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
93 93 merging a and b to b
94 94 my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
95 95 premerge successful
96 96 rev: versions differ -> m (premerge)
97 97 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
98 98 merging rev
99 99 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
100 100 rev: versions differ -> m (merge)
101 101 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
102 102 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
103 103 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
104 104 merge tool returned: 0
105 105 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
106 106 (branch merge, don't forget to commit)
107 107 --------------
108 108 M b
109 109 a
110 110 C a
111 111 --------------
112 112
113 113 $ tm "nc a b" "up a " " " "2 get rem change to a and b"
114 114 created new head
115 115 --------------
116 116 test L:nc a b R:up a W: - 2 get rem change to a and b
117 117 --------------
118 118 unmatched files in local:
119 119 b
120 120 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
121 121 on local side:
122 122 src: 'a' -> dst: 'b' *
123 123 checking for directory renames
124 124 resolving manifests
125 125 branchmerge: True, force: False, partial: False
126 126 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
127 127 preserving b for resolve of b
128 128 preserving rev for resolve of rev
129 129 a: remote is newer -> g
130 130 getting a
131 131 b: local copied/moved from a -> m (premerge)
132 132 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
133 133 merging b and a to b
134 134 my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
135 135 premerge successful
136 136 rev: versions differ -> m (premerge)
137 137 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
138 138 merging rev
139 139 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
140 140 rev: versions differ -> m (merge)
141 141 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
142 142 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
143 143 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
144 144 merge tool returned: 0
145 145 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
146 146 (branch merge, don't forget to commit)
147 147 --------------
148 148 M a
149 149 M b
150 150 a
151 151 --------------
152 152
153 153 $ tm "up a " "nm a b" " " "3 get local a change to b, remove a"
154 154 created new head
155 155 --------------
156 156 test L:up a R:nm a b W: - 3 get local a change to b, remove a
157 157 --------------
158 158 unmatched files in other:
159 159 b
160 160 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
161 161 on remote side:
162 162 src: 'a' -> dst: 'b' *
163 163 checking for directory renames
164 164 resolving manifests
165 165 branchmerge: True, force: False, partial: False
166 166 ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
167 167 preserving a for resolve of b
168 168 preserving rev for resolve of rev
169 169 removing a
170 170 starting 4 threads for background file closing (?)
171 171 b: remote moved from a -> m (premerge)
172 172 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
173 173 merging a and b to b
174 174 my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
175 175 premerge successful
176 176 rev: versions differ -> m (premerge)
177 177 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
178 178 merging rev
179 179 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
180 180 rev: versions differ -> m (merge)
181 181 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
182 182 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
183 183 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
184 184 merge tool returned: 0
185 185 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
186 186 (branch merge, don't forget to commit)
187 187 --------------
188 188 M b
189 189 a
190 190 --------------
191 191
192 192 $ tm "nm a b" "up a " " " "4 get remote change to b"
193 193 created new head
194 194 --------------
195 195 test L:nm a b R:up a W: - 4 get remote change to b
196 196 --------------
197 197 unmatched files in local:
198 198 b
199 199 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
200 200 on local side:
201 201 src: 'a' -> dst: 'b' *
202 202 checking for directory renames
203 203 resolving manifests
204 204 branchmerge: True, force: False, partial: False
205 205 ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
206 206 preserving b for resolve of b
207 207 preserving rev for resolve of rev
208 208 starting 4 threads for background file closing (?)
209 209 b: local copied/moved from a -> m (premerge)
210 210 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
211 211 merging b and a to b
212 212 my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
213 213 premerge successful
214 214 rev: versions differ -> m (premerge)
215 215 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
216 216 merging rev
217 217 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
218 218 rev: versions differ -> m (merge)
219 219 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
220 220 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
221 221 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
222 222 merge tool returned: 0
223 223 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
224 224 (branch merge, don't forget to commit)
225 225 --------------
226 226 M b
227 227 a
228 228 --------------
229 229
230 230 $ tm " " "nc a b" " " "5 get b"
231 231 created new head
232 232 --------------
233 233 test L: R:nc a b W: - 5 get b
234 234 --------------
235 235 unmatched files in other:
236 236 b
237 237 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
238 238 on remote side:
239 239 src: 'a' -> dst: 'b'
240 240 checking for directory renames
241 241 resolving manifests
242 242 branchmerge: True, force: False, partial: False
243 243 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
244 244 preserving rev for resolve of rev
245 245 b: remote created -> g
246 246 getting b
247 247 rev: versions differ -> m (premerge)
248 248 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
249 249 merging rev
250 250 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
251 251 rev: versions differ -> m (merge)
252 252 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
253 253 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
254 254 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
255 255 merge tool returned: 0
256 256 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
257 257 (branch merge, don't forget to commit)
258 258 --------------
259 259 M b
260 260 C a
261 261 --------------
262 262
263 263 $ tm "nc a b" " " " " "6 nothing"
264 264 created new head
265 265 --------------
266 266 test L:nc a b R: W: - 6 nothing
267 267 --------------
268 268 unmatched files in local:
269 269 b
270 270 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
271 271 on local side:
272 272 src: 'a' -> dst: 'b'
273 273 checking for directory renames
274 274 resolving manifests
275 275 branchmerge: True, force: False, partial: False
276 276 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
277 277 preserving rev for resolve of rev
278 278 starting 4 threads for background file closing (?)
279 279 rev: versions differ -> m (premerge)
280 280 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
281 281 merging rev
282 282 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
283 283 rev: versions differ -> m (merge)
284 284 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
285 285 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
286 286 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
287 287 merge tool returned: 0
288 288 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
289 289 (branch merge, don't forget to commit)
290 290 --------------
291 291 C a
292 292 C b
293 293 --------------
294 294
295 295 $ tm " " "nm a b" " " "7 get b"
296 296 created new head
297 297 --------------
298 298 test L: R:nm a b W: - 7 get b
299 299 --------------
300 300 unmatched files in other:
301 301 b
302 302 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
303 303 on remote side:
304 304 src: 'a' -> dst: 'b'
305 305 checking for directory renames
306 306 resolving manifests
307 307 branchmerge: True, force: False, partial: False
308 308 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
309 309 preserving rev for resolve of rev
310 310 a: other deleted -> r
311 311 removing a
312 312 b: remote created -> g
313 313 getting b
314 314 rev: versions differ -> m (premerge)
315 315 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
316 316 merging rev
317 317 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
318 318 rev: versions differ -> m (merge)
319 319 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
320 320 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
321 321 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
322 322 merge tool returned: 0
323 323 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
324 324 (branch merge, don't forget to commit)
325 325 --------------
326 326 M b
327 327 --------------
328 328
329 329 $ tm "nm a b" " " " " "8 nothing"
330 330 created new head
331 331 --------------
332 332 test L:nm a b R: W: - 8 nothing
333 333 --------------
334 334 unmatched files in local:
335 335 b
336 336 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
337 337 on local side:
338 338 src: 'a' -> dst: 'b'
339 339 checking for directory renames
340 340 resolving manifests
341 341 branchmerge: True, force: False, partial: False
342 342 ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
343 343 preserving rev for resolve of rev
344 344 starting 4 threads for background file closing (?)
345 345 rev: versions differ -> m (premerge)
346 346 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
347 347 merging rev
348 348 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
349 349 rev: versions differ -> m (merge)
350 350 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
351 351 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
352 352 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
353 353 merge tool returned: 0
354 354 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
355 355 (branch merge, don't forget to commit)
356 356 --------------
357 357 C b
358 358 --------------
359 359
360 360 $ tm "um a b" "um a b" " " "9 do merge with ancestor in a"
361 361 created new head
362 362 --------------
363 363 test L:um a b R:um a b W: - 9 do merge with ancestor in a
364 364 --------------
365 365 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
366 366 on local side:
367 367 src: 'a' -> dst: 'b' *
368 368 on remote side:
369 369 src: 'a' -> dst: 'b' *
370 370 checking for directory renames
371 371 resolving manifests
372 372 branchmerge: True, force: False, partial: False
373 373 ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
374 374 preserving b for resolve of b
375 375 preserving rev for resolve of rev
376 376 starting 4 threads for background file closing (?)
377 377 b: both renamed from a -> m (premerge)
378 378 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
379 379 merging b
380 380 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
381 381 rev: versions differ -> m (premerge)
382 382 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
383 383 merging rev
384 384 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
385 385 b: both renamed from a -> m (merge)
386 386 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
387 387 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
388 388 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
389 389 merge tool returned: 0
390 390 rev: versions differ -> m (merge)
391 391 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
392 392 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
393 393 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
394 394 merge tool returned: 0
395 395 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
396 396 (branch merge, don't forget to commit)
397 397 --------------
398 398 M b
399 399 --------------
400 400
401 401
402 402 m "um a c" "um x c" " " "10 do merge with no ancestor"
403 403
404 404 $ tm "nm a b" "nm a c" " " "11 get c, keep b"
405 405 created new head
406 406 --------------
407 407 test L:nm a b R:nm a c W: - 11 get c, keep b
408 408 --------------
409 409 unmatched files in local:
410 410 b
411 411 unmatched files in other:
412 412 c
413 413 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
414 414 on local side:
415 415 src: 'a' -> dst: 'b' !
416 416 on remote side:
417 417 src: 'a' -> dst: 'c' !
418 418 checking for directory renames
419 419 resolving manifests
420 420 branchmerge: True, force: False, partial: False
421 421 ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
422 422 note: possible conflict - a was renamed multiple times to:
423 423 b
424 424 c
425 425 preserving rev for resolve of rev
426 426 c: remote created -> g
427 427 getting c
428 428 rev: versions differ -> m (premerge)
429 429 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
430 430 merging rev
431 431 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
432 432 rev: versions differ -> m (merge)
433 433 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
434 434 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
435 435 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
436 436 merge tool returned: 0
437 437 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
438 438 (branch merge, don't forget to commit)
439 439 --------------
440 440 M c
441 441 C b
442 442 --------------
443 443
444 444 $ tm "nc a b" "up b " " " "12 merge b no ancestor"
445 445 created new head
446 446 --------------
447 447 test L:nc a b R:up b W: - 12 merge b no ancestor
448 448 --------------
449 449 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
450 450 on local side:
451 451 src: 'a' -> dst: 'b'
452 452 checking for directory renames
453 453 resolving manifests
454 454 branchmerge: True, force: False, partial: False
455 455 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
456 456 preserving b for resolve of b
457 457 preserving rev for resolve of rev
458 458 starting 4 threads for background file closing (?)
459 459 b: both created -> m (premerge)
460 460 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
461 461 merging b
462 462 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
463 463 rev: versions differ -> m (premerge)
464 464 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
465 465 merging rev
466 466 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
467 467 b: both created -> m (merge)
468 468 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
469 469 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
470 470 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
471 471 merge tool returned: 0
472 472 rev: versions differ -> m (merge)
473 473 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
474 474 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
475 475 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
476 476 merge tool returned: 0
477 477 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
478 478 (branch merge, don't forget to commit)
479 479 --------------
480 480 M b
481 481 C a
482 482 --------------
483 483
484 484 $ tm "up b " "nm a b" " " "13 merge b no ancestor"
485 485 created new head
486 486 --------------
487 487 test L:up b R:nm a b W: - 13 merge b no ancestor
488 488 --------------
489 489 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
490 490 on remote side:
491 491 src: 'a' -> dst: 'b'
492 492 checking for directory renames
493 493 resolving manifests
494 494 branchmerge: True, force: False, partial: False
495 495 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
496 496 preserving b for resolve of b
497 497 preserving rev for resolve of rev
498 498 a: other deleted -> r
499 499 removing a
500 500 starting 4 threads for background file closing (?)
501 501 b: both created -> m (premerge)
502 502 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
503 503 merging b
504 504 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
505 505 rev: versions differ -> m (premerge)
506 506 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
507 507 merging rev
508 508 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
509 509 b: both created -> m (merge)
510 510 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
511 511 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
512 512 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
513 513 merge tool returned: 0
514 514 rev: versions differ -> m (merge)
515 515 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
516 516 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
517 517 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
518 518 merge tool returned: 0
519 519 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
520 520 (branch merge, don't forget to commit)
521 521 --------------
522 522 M b
523 523 --------------
524 524
525 525 $ tm "nc a b" "up a b" " " "14 merge b no ancestor"
526 526 created new head
527 527 --------------
528 528 test L:nc a b R:up a b W: - 14 merge b no ancestor
529 529 --------------
530 530 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
531 531 on local side:
532 src: 'a' -> dst: 'b'
532 src: 'a' -> dst: 'b' *
533 533 checking for directory renames
534 534 resolving manifests
535 535 branchmerge: True, force: False, partial: False
536 536 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
537 537 preserving b for resolve of b
538 538 preserving rev for resolve of rev
539 539 a: remote is newer -> g
540 540 getting a
541 b: both created -> m (premerge)
541 b: both renamed from a -> m (premerge)
542 542 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
543 543 merging b
544 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
544 my b@86a2aa42fc76+ other b@8dbce441892a ancestor a@924404dff337
545 premerge successful
545 546 rev: versions differ -> m (premerge)
546 547 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
547 548 merging rev
548 549 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
549 b: both created -> m (merge)
550 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
551 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
552 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
553 merge tool returned: 0
554 550 rev: versions differ -> m (merge)
555 551 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
556 552 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
557 553 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
558 554 merge tool returned: 0
559 555 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
560 556 (branch merge, don't forget to commit)
561 557 --------------
562 558 M a
563 559 M b
564 560 --------------
565 561
566 562 $ tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
567 563 created new head
568 564 --------------
569 565 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
570 566 --------------
571 567 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
572 568 on remote side:
573 569 src: 'a' -> dst: 'b'
574 570 checking for directory renames
575 571 resolving manifests
576 572 branchmerge: True, force: False, partial: False
577 573 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
578 574 preserving b for resolve of b
579 575 preserving rev for resolve of rev
580 576 a: other deleted -> r
581 577 removing a
582 578 starting 4 threads for background file closing (?)
583 579 b: both created -> m (premerge)
584 580 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
585 581 merging b
586 582 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
587 583 rev: versions differ -> m (premerge)
588 584 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
589 585 merging rev
590 586 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
591 587 b: both created -> m (merge)
592 588 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
593 589 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
594 590 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
595 591 merge tool returned: 0
596 592 rev: versions differ -> m (merge)
597 593 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
598 594 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
599 595 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
600 596 merge tool returned: 0
601 597 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
602 598 (branch merge, don't forget to commit)
603 599 --------------
604 600 M b
605 601 --------------
606 602
607 603 $ tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
608 604 created new head
609 605 --------------
610 606 test L:nc a b R:up a b W: - 16 get a, merge b no ancestor
611 607 --------------
612 608 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
613 609 on local side:
614 src: 'a' -> dst: 'b'
610 src: 'a' -> dst: 'b' *
615 611 checking for directory renames
616 612 resolving manifests
617 613 branchmerge: True, force: False, partial: False
618 614 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
619 615 preserving b for resolve of b
620 616 preserving rev for resolve of rev
621 617 a: remote is newer -> g
622 618 getting a
623 b: both created -> m (premerge)
619 b: both renamed from a -> m (premerge)
624 620 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
625 621 merging b
626 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
622 my b@86a2aa42fc76+ other b@8dbce441892a ancestor a@924404dff337
623 premerge successful
627 624 rev: versions differ -> m (premerge)
628 625 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
629 626 merging rev
630 627 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
631 b: both created -> m (merge)
632 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
633 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
634 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
635 merge tool returned: 0
636 628 rev: versions differ -> m (merge)
637 629 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
638 630 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
639 631 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
640 632 merge tool returned: 0
641 633 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
642 634 (branch merge, don't forget to commit)
643 635 --------------
644 636 M a
645 637 M b
646 638 --------------
647 639
648 640 $ tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
649 641 created new head
650 642 --------------
651 643 test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor
652 644 --------------
653 645 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
654 646 on remote side:
655 src: 'a' -> dst: 'b'
647 src: 'a' -> dst: 'b' *
656 648 checking for directory renames
657 649 resolving manifests
658 650 branchmerge: True, force: False, partial: False
659 651 ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
660 652 preserving b for resolve of b
661 653 preserving rev for resolve of rev
662 654 starting 4 threads for background file closing (?)
663 b: both created -> m (premerge)
655 b: both renamed from a -> m (premerge)
664 656 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
665 657 merging b
666 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
658 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor a@924404dff337
659 premerge successful
667 660 rev: versions differ -> m (premerge)
668 661 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
669 662 merging rev
670 663 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
671 b: both created -> m (merge)
672 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
673 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
674 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
675 merge tool returned: 0
676 664 rev: versions differ -> m (merge)
677 665 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
678 666 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
679 667 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
680 668 merge tool returned: 0
681 669 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
682 670 (branch merge, don't forget to commit)
683 671 --------------
684 672 M b
685 673 C a
686 674 --------------
687 675
688 676 $ tm "nm a b" "up a b" " " "18 merge b no ancestor"
689 677 created new head
690 678 --------------
691 679 test L:nm a b R:up a b W: - 18 merge b no ancestor
692 680 --------------
693 681 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
694 682 on local side:
695 src: 'a' -> dst: 'b'
683 src: 'a' -> dst: 'b' *
696 684 checking for directory renames
697 685 resolving manifests
698 686 branchmerge: True, force: False, partial: False
699 687 ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
700 688 preserving b for resolve of b
701 689 preserving rev for resolve of rev
702 690 starting 4 threads for background file closing (?)
703 a: prompt deleted/changed -> m (premerge)
704 picked tool ':prompt' for a (binary False symlink False changedelete True)
705 file 'a' was deleted in local [working copy] but was modified in other [merge rev].
706 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
707 What do you want to do? u
708 b: both created -> m (premerge)
691 b: both renamed from a -> m (premerge)
709 692 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
710 693 merging b
711 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
694 my b@02963e448370+ other b@8dbce441892a ancestor a@924404dff337
695 premerge successful
712 696 rev: versions differ -> m (premerge)
713 697 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
714 698 merging rev
715 699 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
716 b: both created -> m (merge)
717 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
718 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
719 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
720 merge tool returned: 0
721 700 rev: versions differ -> m (merge)
722 701 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
723 702 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
724 703 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
725 704 merge tool returned: 0
726 0 files updated, 2 files merged, 0 files removed, 1 files unresolved
727 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
705 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
706 (branch merge, don't forget to commit)
728 707 --------------
729 M a
730 708 M b
731 abort: unresolved merge conflicts (see 'hg help resolve')
732 709 --------------
733 710
734 711 $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
735 712 created new head
736 713 --------------
737 714 test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a
738 715 --------------
739 716 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
740 717 on remote side:
741 src: 'a' -> dst: 'b'
718 src: 'a' -> dst: 'b' *
742 719 checking for directory renames
743 720 resolving manifests
744 721 branchmerge: True, force: False, partial: False
745 722 ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
746 preserving a for resolve of a
747 723 preserving b for resolve of b
748 724 preserving rev for resolve of rev
725 b: both renamed from a -> m (premerge)
749 726 starting 4 threads for background file closing (?)
750 a: prompt changed/deleted -> m (premerge)
751 picked tool ':prompt' for a (binary False symlink False changedelete True)
752 file 'a' was deleted in other [merge rev] but was modified in local [working copy].
753 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
754 What do you want to do? u
755 b: both created -> m (premerge)
756 727 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
757 728 merging b
758 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
729 my b@0b76e65c8289+ other b@bdb19105162a ancestor a@924404dff337
730 premerge successful
759 731 rev: versions differ -> m (premerge)
760 732 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
761 733 merging rev
762 734 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
763 b: both created -> m (merge)
764 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
765 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
766 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
767 merge tool returned: 0
768 735 rev: versions differ -> m (merge)
769 736 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
770 737 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
771 738 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
772 739 merge tool returned: 0
773 0 files updated, 2 files merged, 0 files removed, 1 files unresolved
774 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
740 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
741 (branch merge, don't forget to commit)
775 742 --------------
776 743 M b
777 744 C a
778 abort: unresolved merge conflicts (see 'hg help resolve')
779 745 --------------
780 746
781 747 $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
782 748 created new head
783 749 --------------
784 750 test L:up a R:um a b W: - 20 merge a and b to b, remove a
785 751 --------------
786 752 unmatched files in other:
787 753 b
788 754 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
789 755 on remote side:
790 756 src: 'a' -> dst: 'b' *
791 757 checking for directory renames
792 758 resolving manifests
793 759 branchmerge: True, force: False, partial: False
794 760 ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
795 761 preserving a for resolve of b
796 762 preserving rev for resolve of rev
797 763 removing a
798 764 starting 4 threads for background file closing (?)
799 765 b: remote moved from a -> m (premerge)
800 766 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
801 767 merging a and b to b
802 768 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
803 769 rev: versions differ -> m (premerge)
804 770 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
805 771 merging rev
806 772 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
807 773 b: remote moved from a -> m (merge)
808 774 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
809 775 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
810 776 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
811 777 merge tool returned: 0
812 778 rev: versions differ -> m (merge)
813 779 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
814 780 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
815 781 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
816 782 merge tool returned: 0
817 783 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
818 784 (branch merge, don't forget to commit)
819 785 --------------
820 786 M b
821 787 a
822 788 --------------
823 789
824 790 $ tm "um a b" "up a " " " "21 merge a and b to b"
825 791 created new head
826 792 --------------
827 793 test L:um a b R:up a W: - 21 merge a and b to b
828 794 --------------
829 795 unmatched files in local:
830 796 b
831 797 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
832 798 on local side:
833 799 src: 'a' -> dst: 'b' *
834 800 checking for directory renames
835 801 resolving manifests
836 802 branchmerge: True, force: False, partial: False
837 803 ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
838 804 preserving b for resolve of b
839 805 preserving rev for resolve of rev
840 806 starting 4 threads for background file closing (?)
841 807 b: local copied/moved from a -> m (premerge)
842 808 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
843 809 merging b and a to b
844 810 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
845 811 rev: versions differ -> m (premerge)
846 812 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
847 813 merging rev
848 814 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
849 815 b: local copied/moved from a -> m (merge)
850 816 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
851 817 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
852 818 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
853 819 merge tool returned: 0
854 820 rev: versions differ -> m (merge)
855 821 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
856 822 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
857 823 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
858 824 merge tool returned: 0
859 825 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
860 826 (branch merge, don't forget to commit)
861 827 --------------
862 828 M b
863 829 a
864 830 --------------
865 831
866 832
867 833 m "nm a b" "um x a" " " "22 get a, keep b"
868 834
869 835 $ tm "nm a b" "up a c" " " "23 get c, keep b"
870 836 created new head
871 837 --------------
872 838 test L:nm a b R:up a c W: - 23 get c, keep b
873 839 --------------
874 840 unmatched files in local:
875 841 b
876 842 unmatched files in other:
877 843 c
878 844 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
879 845 on local side:
880 846 src: 'a' -> dst: 'b' *
881 847 checking for directory renames
882 848 resolving manifests
883 849 branchmerge: True, force: False, partial: False
884 850 ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
885 851 preserving b for resolve of b
886 852 preserving rev for resolve of rev
887 853 c: remote created -> g
888 854 getting c
889 855 b: local copied/moved from a -> m (premerge)
890 856 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
891 857 merging b and a to b
892 858 my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
893 859 premerge successful
894 860 rev: versions differ -> m (premerge)
895 861 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
896 862 merging rev
897 863 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
898 864 rev: versions differ -> m (merge)
899 865 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
900 866 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
901 867 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
902 868 merge tool returned: 0
903 869 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
904 870 (branch merge, don't forget to commit)
905 871 --------------
906 872 M b
907 873 a
908 874 M c
909 875 --------------
910 876
911 877
912 878 $ cd ..
913 879
914 880
915 881 Systematic and terse testing of merge merges and ancestor calculation:
916 882
917 883 Expected result:
918 884
919 885 \ a m1 m2 dst
920 886 0 - f f f "versions differ"
921 887 1 f g g g "versions differ"
922 888 2 f f f f "versions differ"
923 889 3 f f g f+g "remote copied to " + f
924 890 4 f f g g "remote moved to " + f
925 891 5 f g f f+g "local copied to " + f2
926 892 6 f g f g "local moved to " + f2
927 893 7 - (f) f f "remote differs from untracked local"
928 894 8 f (f) f f "remote differs from untracked local"
929 895
930 896 $ hg init ancestortest
931 897 $ cd ancestortest
932 898 $ for x in 1 2 3 4 5 6 8; do mkdir $x; echo a > $x/f; done
933 899 $ hg ci -Aqm "a"
934 900 $ mkdir 0
935 901 $ touch 0/f
936 902 $ hg mv 1/f 1/g
937 903 $ hg cp 5/f 5/g
938 904 $ hg mv 6/f 6/g
939 905 $ hg rm 8/f
940 906 $ for x in */*; do echo m1 > $x; done
941 907 $ hg ci -Aqm "m1"
942 908 $ hg up -qr0
943 909 $ mkdir 0 7
944 910 $ touch 0/f 7/f
945 911 $ hg mv 1/f 1/g
946 912 $ hg cp 3/f 3/g
947 913 $ hg mv 4/f 4/g
948 914 $ for x in */*; do echo m2 > $x; done
949 915 $ hg ci -Aqm "m2"
950 916 $ hg up -qr1
951 917 $ mkdir 7 8
952 918 $ echo m > 7/f
953 919 $ echo m > 8/f
954 920 $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^resolving manifests/,$d' 2> /dev/null
955 921 unmatched files in local:
956 922 5/g
957 923 6/g
958 924 unmatched files in other:
959 925 3/g
960 926 4/g
961 927 7/f
962 928 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
963 929 on local side:
964 930 src: '1/f' -> dst: '1/g' *
965 931 src: '5/f' -> dst: '5/g' *
966 932 src: '6/f' -> dst: '6/g' *
967 933 on remote side:
968 934 src: '1/f' -> dst: '1/g' *
969 935 src: '3/f' -> dst: '3/g' *
970 936 src: '4/f' -> dst: '4/g' *
971 937 checking for directory renames
972 938 $ hg mani
973 939 0/f
974 940 1/g
975 941 2/f
976 942 3/f
977 943 4/f
978 944 5/f
979 945 5/g
980 946 6/g
981 947 $ for f in */*; do echo $f:; cat $f; done
982 948 0/f:
983 949 m1
984 950 0/f.base:
985 951 0/f.local:
986 952 m1
987 953 0/f.orig:
988 954 m1
989 955 0/f.other:
990 956 m2
991 957 1/g:
992 958 m1
993 959 1/g.base:
994 960 a
995 961 1/g.local:
996 962 m1
997 963 1/g.orig:
998 964 m1
999 965 1/g.other:
1000 966 m2
1001 967 2/f:
1002 968 m1
1003 969 2/f.base:
1004 970 a
1005 971 2/f.local:
1006 972 m1
1007 973 2/f.orig:
1008 974 m1
1009 975 2/f.other:
1010 976 m2
1011 977 3/f:
1012 978 m1
1013 979 3/f.base:
1014 980 a
1015 981 3/f.local:
1016 982 m1
1017 983 3/f.orig:
1018 984 m1
1019 985 3/f.other:
1020 986 m2
1021 987 3/g:
1022 988 m1
1023 989 3/g.base:
1024 990 a
1025 991 3/g.local:
1026 992 m1
1027 993 3/g.orig:
1028 994 m1
1029 995 3/g.other:
1030 996 m2
1031 997 4/g:
1032 998 m1
1033 999 4/g.base:
1034 1000 a
1035 1001 4/g.local:
1036 1002 m1
1037 1003 4/g.orig:
1038 1004 m1
1039 1005 4/g.other:
1040 1006 m2
1041 1007 5/f:
1042 1008 m1
1043 1009 5/f.base:
1044 1010 a
1045 1011 5/f.local:
1046 1012 m1
1047 1013 5/f.orig:
1048 1014 m1
1049 1015 5/f.other:
1050 1016 m2
1051 1017 5/g:
1052 1018 m1
1053 1019 5/g.base:
1054 1020 a
1055 1021 5/g.local:
1056 1022 m1
1057 1023 5/g.orig:
1058 1024 m1
1059 1025 5/g.other:
1060 1026 m2
1061 1027 6/g:
1062 1028 m1
1063 1029 6/g.base:
1064 1030 a
1065 1031 6/g.local:
1066 1032 m1
1067 1033 6/g.orig:
1068 1034 m1
1069 1035 6/g.other:
1070 1036 m2
1071 1037 7/f:
1072 1038 m
1073 1039 7/f.base:
1074 1040 7/f.local:
1075 1041 m
1076 1042 7/f.orig:
1077 1043 m
1078 1044 7/f.other:
1079 1045 m2
1080 1046 8/f:
1081 1047 m2
1082 1048 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now