##// END OF EJS Templates
merge: break out "both renamed a -> b" case...
Martin von Zweigbergk -
r23397:c7c95838 default
parent child Browse files
Show More
@@ -1,1142 +1,1146
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import struct
9 9
10 10 from node import nullid, nullrev, hex, bin
11 11 from i18n import _
12 12 from mercurial import obsolete
13 13 import error as errormod, util, filemerge, copies, subrepo, worker
14 14 import errno, os, shutil
15 15
16 16 _pack = struct.pack
17 17 _unpack = struct.unpack
18 18
19 19 def _droponode(data):
20 20 # used for compatibility for v1
21 21 bits = data.split('\0')
22 22 bits = bits[:-2] + bits[-1:]
23 23 return '\0'.join(bits)
24 24
25 25 class mergestate(object):
26 26 '''track 3-way merge state of individual files
27 27
28 28 it is stored on disk when needed. Two file are used, one with an old
29 29 format, one with a new format. Both contains similar data, but the new
30 30 format can store new kind of field.
31 31
32 32 Current new format is a list of arbitrary record of the form:
33 33
34 34 [type][length][content]
35 35
36 36 Type is a single character, length is a 4 bytes integer, content is an
37 37 arbitrary suites of bytes of length `length`.
38 38
39 39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 40 should abort if they are unknown. lower case record can be safely ignored.
41 41
42 42 Currently known record:
43 43
44 44 L: the node of the "local" part of the merge (hexified version)
45 45 O: the node of the "other" part of the merge (hexified version)
46 46 F: a file to be merged entry
47 47 '''
48 48 statepathv1 = 'merge/state'
49 49 statepathv2 = 'merge/state2'
50 50
51 51 def __init__(self, repo):
52 52 self._repo = repo
53 53 self._dirty = False
54 54 self._read()
55 55
56 56 def reset(self, node=None, other=None):
57 57 self._state = {}
58 58 self._local = None
59 59 self._other = None
60 60 if node:
61 61 self._local = node
62 62 self._other = other
63 63 shutil.rmtree(self._repo.join('merge'), True)
64 64 self._dirty = False
65 65
66 66 def _read(self):
67 67 """Analyse each record content to restore a serialized state from disk
68 68
69 69 This function process "record" entry produced by the de-serialization
70 70 of on disk file.
71 71 """
72 72 self._state = {}
73 73 self._local = None
74 74 self._other = None
75 75 records = self._readrecords()
76 76 for rtype, record in records:
77 77 if rtype == 'L':
78 78 self._local = bin(record)
79 79 elif rtype == 'O':
80 80 self._other = bin(record)
81 81 elif rtype == 'F':
82 82 bits = record.split('\0')
83 83 self._state[bits[0]] = bits[1:]
84 84 elif not rtype.islower():
85 85 raise util.Abort(_('unsupported merge state record: %s')
86 86 % rtype)
87 87 self._dirty = False
88 88
89 89 def _readrecords(self):
90 90 """Read merge state from disk and return a list of record (TYPE, data)
91 91
92 92 We read data from both v1 and v2 files and decide which one to use.
93 93
94 94 V1 has been used by version prior to 2.9.1 and contains less data than
95 95 v2. We read both versions and check if no data in v2 contradicts
96 96 v1. If there is not contradiction we can safely assume that both v1
97 97 and v2 were written at the same time and use the extract data in v2. If
98 98 there is contradiction we ignore v2 content as we assume an old version
99 99 of Mercurial has overwritten the mergestate file and left an old v2
100 100 file around.
101 101
102 102 returns list of record [(TYPE, data), ...]"""
103 103 v1records = self._readrecordsv1()
104 104 v2records = self._readrecordsv2()
105 105 oldv2 = set() # old format version of v2 record
106 106 for rec in v2records:
107 107 if rec[0] == 'L':
108 108 oldv2.add(rec)
109 109 elif rec[0] == 'F':
110 110 # drop the onode data (not contained in v1)
111 111 oldv2.add(('F', _droponode(rec[1])))
112 112 for rec in v1records:
113 113 if rec not in oldv2:
114 114 # v1 file is newer than v2 file, use it
115 115 # we have to infer the "other" changeset of the merge
116 116 # we cannot do better than that with v1 of the format
117 117 mctx = self._repo[None].parents()[-1]
118 118 v1records.append(('O', mctx.hex()))
119 119 # add place holder "other" file node information
120 120 # nobody is using it yet so we do no need to fetch the data
121 121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 122 for idx, r in enumerate(v1records):
123 123 if r[0] == 'F':
124 124 bits = r[1].split('\0')
125 125 bits.insert(-2, '')
126 126 v1records[idx] = (r[0], '\0'.join(bits))
127 127 return v1records
128 128 else:
129 129 return v2records
130 130
131 131 def _readrecordsv1(self):
132 132 """read on disk merge state for version 1 file
133 133
134 134 returns list of record [(TYPE, data), ...]
135 135
136 136 Note: the "F" data from this file are one entry short
137 137 (no "other file node" entry)
138 138 """
139 139 records = []
140 140 try:
141 141 f = self._repo.opener(self.statepathv1)
142 142 for i, l in enumerate(f):
143 143 if i == 0:
144 144 records.append(('L', l[:-1]))
145 145 else:
146 146 records.append(('F', l[:-1]))
147 147 f.close()
148 148 except IOError, err:
149 149 if err.errno != errno.ENOENT:
150 150 raise
151 151 return records
152 152
153 153 def _readrecordsv2(self):
154 154 """read on disk merge state for version 2 file
155 155
156 156 returns list of record [(TYPE, data), ...]
157 157 """
158 158 records = []
159 159 try:
160 160 f = self._repo.opener(self.statepathv2)
161 161 data = f.read()
162 162 off = 0
163 163 end = len(data)
164 164 while off < end:
165 165 rtype = data[off]
166 166 off += 1
167 167 length = _unpack('>I', data[off:(off + 4)])[0]
168 168 off += 4
169 169 record = data[off:(off + length)]
170 170 off += length
171 171 records.append((rtype, record))
172 172 f.close()
173 173 except IOError, err:
174 174 if err.errno != errno.ENOENT:
175 175 raise
176 176 return records
177 177
178 178 def active(self):
179 179 """Whether mergestate is active.
180 180
181 181 Returns True if there appears to be mergestate. This is a rough proxy
182 182 for "is a merge in progress."
183 183 """
184 184 # Check local variables before looking at filesystem for performance
185 185 # reasons.
186 186 return bool(self._local) or bool(self._state) or \
187 187 self._repo.opener.exists(self.statepathv1) or \
188 188 self._repo.opener.exists(self.statepathv2)
189 189
190 190 def commit(self):
191 191 """Write current state on disk (if necessary)"""
192 192 if self._dirty:
193 193 records = []
194 194 records.append(('L', hex(self._local)))
195 195 records.append(('O', hex(self._other)))
196 196 for d, v in self._state.iteritems():
197 197 records.append(('F', '\0'.join([d] + v)))
198 198 self._writerecords(records)
199 199 self._dirty = False
200 200
201 201 def _writerecords(self, records):
202 202 """Write current state on disk (both v1 and v2)"""
203 203 self._writerecordsv1(records)
204 204 self._writerecordsv2(records)
205 205
206 206 def _writerecordsv1(self, records):
207 207 """Write current state on disk in a version 1 file"""
208 208 f = self._repo.opener(self.statepathv1, 'w')
209 209 irecords = iter(records)
210 210 lrecords = irecords.next()
211 211 assert lrecords[0] == 'L'
212 212 f.write(hex(self._local) + '\n')
213 213 for rtype, data in irecords:
214 214 if rtype == 'F':
215 215 f.write('%s\n' % _droponode(data))
216 216 f.close()
217 217
218 218 def _writerecordsv2(self, records):
219 219 """Write current state on disk in a version 2 file"""
220 220 f = self._repo.opener(self.statepathv2, 'w')
221 221 for key, data in records:
222 222 assert len(key) == 1
223 223 format = '>sI%is' % len(data)
224 224 f.write(_pack(format, key, len(data), data))
225 225 f.close()
226 226
227 227 def add(self, fcl, fco, fca, fd):
228 228 """add a new (potentially?) conflicting file the merge state
229 229 fcl: file context for local,
230 230 fco: file context for remote,
231 231 fca: file context for ancestors,
232 232 fd: file path of the resulting merge.
233 233
234 234 note: also write the local version to the `.hg/merge` directory.
235 235 """
236 236 hash = util.sha1(fcl.path()).hexdigest()
237 237 self._repo.opener.write('merge/' + hash, fcl.data())
238 238 self._state[fd] = ['u', hash, fcl.path(),
239 239 fca.path(), hex(fca.filenode()),
240 240 fco.path(), hex(fco.filenode()),
241 241 fcl.flags()]
242 242 self._dirty = True
243 243
244 244 def __contains__(self, dfile):
245 245 return dfile in self._state
246 246
247 247 def __getitem__(self, dfile):
248 248 return self._state[dfile][0]
249 249
250 250 def __iter__(self):
251 251 return iter(sorted(self._state))
252 252
253 253 def files(self):
254 254 return self._state.keys()
255 255
256 256 def mark(self, dfile, state):
257 257 self._state[dfile][0] = state
258 258 self._dirty = True
259 259
260 260 def unresolved(self):
261 261 """Obtain the paths of unresolved files."""
262 262
263 263 for f, entry in self._state.items():
264 264 if entry[0] == 'u':
265 265 yield f
266 266
267 267 def resolve(self, dfile, wctx, labels=None):
268 268 """rerun merge process for file path `dfile`"""
269 269 if self[dfile] == 'r':
270 270 return 0
271 271 stateentry = self._state[dfile]
272 272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 273 octx = self._repo[self._other]
274 274 fcd = wctx[dfile]
275 275 fco = octx[ofile]
276 276 fca = self._repo.filectx(afile, fileid=anode)
277 277 # "premerge" x flags
278 278 flo = fco.flags()
279 279 fla = fca.flags()
280 280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 281 if fca.node() == nullid:
282 282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 283 afile)
284 284 elif flags == fla:
285 285 flags = flo
286 286 # restore local
287 287 f = self._repo.opener('merge/' + hash)
288 288 self._repo.wwrite(dfile, f.read(), flags)
289 289 f.close()
290 290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
291 291 labels=labels)
292 292 if r is None:
293 293 # no real conflict
294 294 del self._state[dfile]
295 295 self._dirty = True
296 296 elif not r:
297 297 self.mark(dfile, 'r')
298 298 return r
299 299
300 300 def _checkunknownfile(repo, wctx, mctx, f):
301 301 return (not repo.dirstate._ignore(f)
302 302 and os.path.isfile(repo.wjoin(f))
303 303 and repo.wopener.audit.check(f)
304 304 and repo.dirstate.normalize(f) not in repo.dirstate
305 305 and mctx[f].cmp(wctx[f]))
306 306
307 307 def _forgetremoved(wctx, mctx, branchmerge):
308 308 """
309 309 Forget removed files
310 310
311 311 If we're jumping between revisions (as opposed to merging), and if
312 312 neither the working directory nor the target rev has the file,
313 313 then we need to remove it from the dirstate, to prevent the
314 314 dirstate from listing the file when it is no longer in the
315 315 manifest.
316 316
317 317 If we're merging, and the other revision has removed a file
318 318 that is not present in the working directory, we need to mark it
319 319 as removed.
320 320 """
321 321
322 322 ractions = []
323 323 factions = xactions = []
324 324 if branchmerge:
325 325 xactions = ractions
326 326 for f in wctx.deleted():
327 327 if f not in mctx:
328 328 xactions.append((f, None, "forget deleted"))
329 329
330 330 if not branchmerge:
331 331 for f in wctx.removed():
332 332 if f not in mctx:
333 333 factions.append((f, None, "forget removed"))
334 334
335 335 return ractions, factions
336 336
337 337 def _checkcollision(repo, wmf, actions):
338 338 # build provisional merged manifest up
339 339 pmmf = set(wmf)
340 340
341 341 if actions:
342 342 # k, dr, e and rd are no-op
343 343 for m in 'a', 'f', 'g', 'cd', 'dc':
344 344 for f, args, msg in actions[m]:
345 345 pmmf.add(f)
346 346 for f, args, msg in actions['r']:
347 347 pmmf.discard(f)
348 348 for f, args, msg in actions['dm']:
349 349 f2, flags = args
350 350 pmmf.discard(f2)
351 351 pmmf.add(f)
352 352 for f, args, msg in actions['dg']:
353 353 pmmf.add(f)
354 354 for f, args, msg in actions['m']:
355 355 f1, f2, fa, move, anc = args
356 356 if move:
357 357 pmmf.discard(f1)
358 358 pmmf.add(f)
359 359
360 360 # check case-folding collision in provisional merged manifest
361 361 foldmap = {}
362 362 for f in sorted(pmmf):
363 363 fold = util.normcase(f)
364 364 if fold in foldmap:
365 365 raise util.Abort(_("case-folding collision between %s and %s")
366 366 % (f, foldmap[fold]))
367 367 foldmap[fold] = f
368 368
369 369 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
370 370 acceptremote, followcopies):
371 371 """
372 372 Merge p1 and p2 with ancestor pa and generate merge action list
373 373
374 374 branchmerge and force are as passed in to update
375 375 partial = function to filter file lists
376 376 acceptremote = accept the incoming changes without prompting
377 377 """
378 378
379 379 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
380 380 copy, movewithdir = {}, {}
381 381
382 382 # manifests fetched in order are going to be faster, so prime the caches
383 383 [x.manifest() for x in
384 384 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
385 385
386 386 if followcopies:
387 387 ret = copies.mergecopies(repo, wctx, p2, pa)
388 388 copy, movewithdir, diverge, renamedelete = ret
389 389 for of, fl in diverge.iteritems():
390 390 actions['dr'].append((of, (fl,), "divergent renames"))
391 391 for of, fl in renamedelete.iteritems():
392 392 actions['rd'].append((of, (fl,), "rename and delete"))
393 393
394 394 repo.ui.note(_("resolving manifests\n"))
395 395 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
396 396 % (bool(branchmerge), bool(force), bool(partial)))
397 397 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
398 398
399 399 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
400 400 copied = set(copy.values())
401 401 copied.update(movewithdir.values())
402 402
403 403 if '.hgsubstate' in m1:
404 404 # check whether sub state is modified
405 405 for s in sorted(wctx.substate):
406 406 if wctx.sub(s).dirty():
407 407 m1['.hgsubstate'] += '+'
408 408 break
409 409
410 410 aborts = []
411 411 # Compare manifests
412 412 diff = m1.diff(m2)
413 413
414 414 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
415 415 if partial and not partial(f):
416 416 continue
417 417 if n1 and n2:
418 418 if f not in ma:
419 # Note: f as ancestor is wrong - we can't really make a 3-way
420 # merge without an ancestor file.
421 fa = copy.get(f, f)
419 fa = copy.get(f, None)
420 if fa is not None:
422 421 actions['m'].append((f, (f, f, fa, False, pa.node()),
422 "both renamed from " + fa))
423 else:
424 # Note: f as ancestor is wrong - we can't really make a
425 # 3-way merge without an ancestor file.
426 actions['m'].append((f, (f, f, f, False, pa.node()),
423 427 "both created"))
424 428 else:
425 429 a = ma[f]
426 430 fla = ma.flags(f)
427 431 nol = 'l' not in fl1 + fl2 + fla
428 432 if n2 == a and fl2 == fla:
429 433 actions['k'].append((f, (), "keep")) # remote unchanged
430 434 elif n1 == a and fl1 == fla: # local unchanged - use remote
431 435 if n1 == n2: # optimization: keep local content
432 436 actions['e'].append((f, (fl2,), "update permissions"))
433 437 else:
434 438 actions['g'].append((f, (fl2,), "remote is newer"))
435 439 elif nol and n2 == a: # remote only changed 'x'
436 440 actions['e'].append((f, (fl2,), "update permissions"))
437 441 elif nol and n1 == a: # local only changed 'x'
438 442 actions['g'].append((f, (fl1,), "remote is newer"))
439 443 else: # both changed something
440 444 actions['m'].append((f, (f, f, f, False, pa.node()),
441 445 "versions differ"))
442 446 elif f in copied: # files we'll deal with on m2 side
443 447 pass
444 448 elif n1 and f in movewithdir: # directory rename, move local
445 449 f2 = movewithdir[f]
446 450 actions['dm'].append((f2, (f, fl1),
447 451 "remote directory rename - move from " + f))
448 452 elif n1 and f in copy:
449 453 f2 = copy[f]
450 454 actions['m'].append((f, (f, f2, f2, False, pa.node()),
451 455 "local copied/moved from " + f2))
452 456 elif n1 and f in ma: # clean, a different, no remote
453 457 if n1 != ma[f]:
454 458 if acceptremote:
455 459 actions['r'].append((f, None, "remote delete"))
456 460 else:
457 461 actions['cd'].append((f, None, "prompt changed/deleted"))
458 462 elif n1[20:] == 'a': # added, no remote
459 463 actions['f'].append((f, None, "remote deleted"))
460 464 else:
461 465 actions['r'].append((f, None, "other deleted"))
462 466 elif n2 and f in movewithdir:
463 467 f2 = movewithdir[f]
464 468 actions['dg'].append((f2, (f, fl2),
465 469 "local directory rename - get from " + f))
466 470 elif n2 and f in copy:
467 471 f2 = copy[f]
468 472 if f2 in m2:
469 473 actions['m'].append((f, (f2, f, f2, False, pa.node()),
470 474 "remote copied from " + f2))
471 475 else:
472 476 actions['m'].append((f, (f2, f, f2, True, pa.node()),
473 477 "remote moved from " + f2))
474 478 elif n2 and f not in ma:
475 479 # local unknown, remote created: the logic is described by the
476 480 # following table:
477 481 #
478 482 # force branchmerge different | action
479 483 # n * n | get
480 484 # n * y | abort
481 485 # y n * | get
482 486 # y y n | get
483 487 # y y y | merge
484 488 #
485 489 # Checking whether the files are different is expensive, so we
486 490 # don't do that when we can avoid it.
487 491 if force and not branchmerge:
488 492 actions['g'].append((f, (fl2,), "remote created"))
489 493 else:
490 494 different = _checkunknownfile(repo, wctx, p2, f)
491 495 if force and branchmerge and different:
492 496 # FIXME: This is wrong - f is not in ma ...
493 497 actions['m'].append((f, (f, f, f, False, pa.node()),
494 498 "remote differs from untracked local"))
495 499 elif not force and different:
496 500 aborts.append((f, 'ud'))
497 501 else:
498 502 actions['g'].append((f, (fl2,), "remote created"))
499 503 elif n2 and n2 != ma[f]:
500 504 different = _checkunknownfile(repo, wctx, p2, f)
501 505 if not force and different:
502 506 aborts.append((f, 'ud'))
503 507 else:
504 508 if acceptremote:
505 509 actions['g'].append((f, (fl2,), "remote recreating"))
506 510 else:
507 511 actions['dc'].append((f, (fl2,), "prompt deleted/changed"))
508 512
509 513 for f, m in sorted(aborts):
510 514 if m == 'ud':
511 515 repo.ui.warn(_("%s: untracked file differs\n") % f)
512 516 else: assert False, m
513 517 if aborts:
514 518 raise util.Abort(_("untracked files in working directory differ "
515 519 "from files in requested revision"))
516 520
517 521 if not util.checkcase(repo.path):
518 522 # check collision between files only in p2 for clean update
519 523 if (not branchmerge and
520 524 (force or not wctx.dirty(missing=True, branch=False))):
521 525 _checkcollision(repo, m2, None)
522 526 else:
523 527 _checkcollision(repo, m1, actions)
524 528
525 529 return actions
526 530
527 531 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
528 532 acceptremote, followcopies):
529 533 "Calculate the actions needed to merge mctx into wctx using ancestors"
530 534
531 535 if len(ancestors) == 1: # default
532 536 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
533 537 branchmerge, force,
534 538 partial, acceptremote, followcopies)
535 539
536 540 else: # only when merge.preferancestor=* - the default
537 541 repo.ui.note(
538 542 _("note: merging %s and %s using bids from ancestors %s\n") %
539 543 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
540 544
541 545 # Call for bids
542 546 fbids = {} # mapping filename to bids (action method to list af actions)
543 547 for ancestor in ancestors:
544 548 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
545 549 actions = manifestmerge(repo, wctx, mctx, ancestor,
546 550 branchmerge, force,
547 551 partial, acceptremote, followcopies)
548 552 for m, l in sorted(actions.items()):
549 553 for a in l:
550 554 f, args, msg = a
551 555 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
552 556 if f in fbids:
553 557 d = fbids[f]
554 558 if m in d:
555 559 d[m].append(a)
556 560 else:
557 561 d[m] = [a]
558 562 else:
559 563 fbids[f] = {m: [a]}
560 564
561 565 # Pick the best bid for each file
562 566 repo.ui.note(_('\nauction for merging merge bids\n'))
563 567 actions = dict((m, []) for m in actions.keys())
564 568 for f, bids in sorted(fbids.items()):
565 569 # bids is a mapping from action method to list af actions
566 570 # Consensus?
567 571 if len(bids) == 1: # all bids are the same kind of method
568 572 m, l = bids.items()[0]
569 573 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
570 574 repo.ui.note(" %s: consensus for %s\n" % (f, m))
571 575 actions[m].append(l[0])
572 576 continue
573 577 # If keep is an option, just do it.
574 578 if 'k' in bids:
575 579 repo.ui.note(" %s: picking 'keep' action\n" % f)
576 580 actions['k'].append(bids['k'][0])
577 581 continue
578 582 # If there are gets and they all agree [how could they not?], do it.
579 583 if 'g' in bids:
580 584 ga0 = bids['g'][0]
581 585 if util.all(a == ga0 for a in bids['g'][1:]):
582 586 repo.ui.note(" %s: picking 'get' action\n" % f)
583 587 actions['g'].append(ga0)
584 588 continue
585 589 # TODO: Consider other simple actions such as mode changes
586 590 # Handle inefficient democrazy.
587 591 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
588 592 for m, l in sorted(bids.items()):
589 593 for _f, args, msg in l:
590 594 repo.ui.note(' %s -> %s\n' % (msg, m))
591 595 # Pick random action. TODO: Instead, prompt user when resolving
592 596 m, l = bids.items()[0]
593 597 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
594 598 (f, m))
595 599 actions[m].append(l[0])
596 600 continue
597 601 repo.ui.note(_('end of auction\n\n'))
598 602
599 603 # Prompt and create actions. TODO: Move this towards resolve phase.
600 604 for f, args, msg in actions['cd']:
601 605 if repo.ui.promptchoice(
602 606 _("local changed %s which remote deleted\n"
603 607 "use (c)hanged version or (d)elete?"
604 608 "$$ &Changed $$ &Delete") % f, 0):
605 609 actions['r'].append((f, None, "prompt delete"))
606 610 else:
607 611 actions['a'].append((f, None, "prompt keep"))
608 612 del actions['cd'][:]
609 613
610 614 for f, args, msg in actions['dc']:
611 615 flags, = args
612 616 if repo.ui.promptchoice(
613 617 _("remote changed %s which local deleted\n"
614 618 "use (c)hanged version or leave (d)eleted?"
615 619 "$$ &Changed $$ &Deleted") % f, 0) == 0:
616 620 actions['g'].append((f, (flags,), "prompt recreating"))
617 621 del actions['dc'][:]
618 622
619 623 if wctx.rev() is None:
620 624 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
621 625 actions['r'].extend(ractions)
622 626 actions['f'].extend(factions)
623 627
624 628 return actions
625 629
626 630 def batchremove(repo, actions):
627 631 """apply removes to the working directory
628 632
629 633 yields tuples for progress updates
630 634 """
631 635 verbose = repo.ui.verbose
632 636 unlink = util.unlinkpath
633 637 wjoin = repo.wjoin
634 638 audit = repo.wopener.audit
635 639 i = 0
636 640 for f, args, msg in actions:
637 641 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
638 642 if verbose:
639 643 repo.ui.note(_("removing %s\n") % f)
640 644 audit(f)
641 645 try:
642 646 unlink(wjoin(f), ignoremissing=True)
643 647 except OSError, inst:
644 648 repo.ui.warn(_("update failed to remove %s: %s!\n") %
645 649 (f, inst.strerror))
646 650 if i == 100:
647 651 yield i, f
648 652 i = 0
649 653 i += 1
650 654 if i > 0:
651 655 yield i, f
652 656
653 657 def batchget(repo, mctx, actions):
654 658 """apply gets to the working directory
655 659
656 660 mctx is the context to get from
657 661
658 662 yields tuples for progress updates
659 663 """
660 664 verbose = repo.ui.verbose
661 665 fctx = mctx.filectx
662 666 wwrite = repo.wwrite
663 667 i = 0
664 668 for f, args, msg in actions:
665 669 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
666 670 if verbose:
667 671 repo.ui.note(_("getting %s\n") % f)
668 672 wwrite(f, fctx(f).data(), args[0])
669 673 if i == 100:
670 674 yield i, f
671 675 i = 0
672 676 i += 1
673 677 if i > 0:
674 678 yield i, f
675 679
676 680 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
677 681 """apply the merge action list to the working directory
678 682
679 683 wctx is the working copy context
680 684 mctx is the context to be merged into the working copy
681 685
682 686 Return a tuple of counts (updated, merged, removed, unresolved) that
683 687 describes how many files were affected by the update.
684 688 """
685 689
686 690 updated, merged, removed, unresolved = 0, 0, 0, 0
687 691 ms = mergestate(repo)
688 692 ms.reset(wctx.p1().node(), mctx.node())
689 693 moves = []
690 694 for m, l in actions.items():
691 695 l.sort()
692 696
693 697 # prescan for merges
694 698 for f, args, msg in actions['m']:
695 699 f1, f2, fa, move, anc = args
696 700 if f == '.hgsubstate': # merged internally
697 701 continue
698 702 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
699 703 fcl = wctx[f1]
700 704 fco = mctx[f2]
701 705 actx = repo[anc]
702 706 if fa in actx:
703 707 fca = actx[fa]
704 708 else:
705 709 fca = repo.filectx(f1, fileid=nullrev)
706 710 ms.add(fcl, fco, fca, f)
707 711 if f1 != f and move:
708 712 moves.append(f1)
709 713
710 714 audit = repo.wopener.audit
711 715 _updating = _('updating')
712 716 _files = _('files')
713 717 progress = repo.ui.progress
714 718
715 719 # remove renamed files after safely stored
716 720 for f in moves:
717 721 if os.path.lexists(repo.wjoin(f)):
718 722 repo.ui.debug("removing %s\n" % f)
719 723 audit(f)
720 724 util.unlinkpath(repo.wjoin(f))
721 725
722 726 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
723 727
724 728 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
725 729 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
726 730
727 731 # remove in parallel (must come first)
728 732 z = 0
729 733 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
730 734 for i, item in prog:
731 735 z += i
732 736 progress(_updating, z, item=item, total=numupdates, unit=_files)
733 737 removed = len(actions['r'])
734 738
735 739 # get in parallel
736 740 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
737 741 for i, item in prog:
738 742 z += i
739 743 progress(_updating, z, item=item, total=numupdates, unit=_files)
740 744 updated = len(actions['g'])
741 745
742 746 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
743 747 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
744 748
745 749 # forget (manifest only, just log it) (must come first)
746 750 for f, args, msg in actions['f']:
747 751 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
748 752 z += 1
749 753 progress(_updating, z, item=f, total=numupdates, unit=_files)
750 754
751 755 # re-add (manifest only, just log it)
752 756 for f, args, msg in actions['a']:
753 757 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
754 758 z += 1
755 759 progress(_updating, z, item=f, total=numupdates, unit=_files)
756 760
757 761 # keep (noop, just log it)
758 762 for f, args, msg in actions['k']:
759 763 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
760 764 # no progress
761 765
762 766 # merge
763 767 for f, args, msg in actions['m']:
764 768 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
765 769 z += 1
766 770 progress(_updating, z, item=f, total=numupdates, unit=_files)
767 771 if f == '.hgsubstate': # subrepo states need updating
768 772 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
769 773 overwrite)
770 774 continue
771 775 audit(f)
772 776 r = ms.resolve(f, wctx, labels=labels)
773 777 if r is not None and r > 0:
774 778 unresolved += 1
775 779 else:
776 780 if r is None:
777 781 updated += 1
778 782 else:
779 783 merged += 1
780 784
781 785 # directory rename, move local
782 786 for f, args, msg in actions['dm']:
783 787 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
784 788 z += 1
785 789 progress(_updating, z, item=f, total=numupdates, unit=_files)
786 790 f0, flags = args
787 791 repo.ui.note(_("moving %s to %s\n") % (f0, f))
788 792 audit(f)
789 793 repo.wwrite(f, wctx.filectx(f0).data(), flags)
790 794 util.unlinkpath(repo.wjoin(f0))
791 795 updated += 1
792 796
793 797 # local directory rename, get
794 798 for f, args, msg in actions['dg']:
795 799 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
796 800 z += 1
797 801 progress(_updating, z, item=f, total=numupdates, unit=_files)
798 802 f0, flags = args
799 803 repo.ui.note(_("getting %s to %s\n") % (f0, f))
800 804 repo.wwrite(f, mctx.filectx(f0).data(), flags)
801 805 updated += 1
802 806
803 807 # divergent renames
804 808 for f, args, msg in actions['dr']:
805 809 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
806 810 z += 1
807 811 progress(_updating, z, item=f, total=numupdates, unit=_files)
808 812 fl, = args
809 813 repo.ui.warn(_("note: possible conflict - %s was renamed "
810 814 "multiple times to:\n") % f)
811 815 for nf in fl:
812 816 repo.ui.warn(" %s\n" % nf)
813 817
814 818 # rename and delete
815 819 for f, args, msg in actions['rd']:
816 820 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
817 821 z += 1
818 822 progress(_updating, z, item=f, total=numupdates, unit=_files)
819 823 fl, = args
820 824 repo.ui.warn(_("note: possible conflict - %s was deleted "
821 825 "and renamed to:\n") % f)
822 826 for nf in fl:
823 827 repo.ui.warn(" %s\n" % nf)
824 828
825 829 # exec
826 830 for f, args, msg in actions['e']:
827 831 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
828 832 z += 1
829 833 progress(_updating, z, item=f, total=numupdates, unit=_files)
830 834 flags, = args
831 835 audit(f)
832 836 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
833 837 updated += 1
834 838
835 839 ms.commit()
836 840 progress(_updating, None, total=numupdates, unit=_files)
837 841
838 842 return updated, merged, removed, unresolved
839 843
840 844 def recordupdates(repo, actions, branchmerge):
841 845 "record merge actions to the dirstate"
842 846 # remove (must come first)
843 847 for f, args, msg in actions['r']:
844 848 if branchmerge:
845 849 repo.dirstate.remove(f)
846 850 else:
847 851 repo.dirstate.drop(f)
848 852
849 853 # forget (must come first)
850 854 for f, args, msg in actions['f']:
851 855 repo.dirstate.drop(f)
852 856
853 857 # re-add
854 858 for f, args, msg in actions['a']:
855 859 if not branchmerge:
856 860 repo.dirstate.add(f)
857 861
858 862 # exec change
859 863 for f, args, msg in actions['e']:
860 864 repo.dirstate.normallookup(f)
861 865
862 866 # keep
863 867 for f, args, msg in actions['k']:
864 868 pass
865 869
866 870 # get
867 871 for f, args, msg in actions['g']:
868 872 if branchmerge:
869 873 repo.dirstate.otherparent(f)
870 874 else:
871 875 repo.dirstate.normal(f)
872 876
873 877 # merge
874 878 for f, args, msg in actions['m']:
875 879 f1, f2, fa, move, anc = args
876 880 if branchmerge:
877 881 # We've done a branch merge, mark this file as merged
878 882 # so that we properly record the merger later
879 883 repo.dirstate.merge(f)
880 884 if f1 != f2: # copy/rename
881 885 if move:
882 886 repo.dirstate.remove(f1)
883 887 if f1 != f:
884 888 repo.dirstate.copy(f1, f)
885 889 else:
886 890 repo.dirstate.copy(f2, f)
887 891 else:
888 892 # We've update-merged a locally modified file, so
889 893 # we set the dirstate to emulate a normal checkout
890 894 # of that file some time in the past. Thus our
891 895 # merge will appear as a normal local file
892 896 # modification.
893 897 if f2 == f: # file not locally copied/moved
894 898 repo.dirstate.normallookup(f)
895 899 if move:
896 900 repo.dirstate.drop(f1)
897 901
898 902 # directory rename, move local
899 903 for f, args, msg in actions['dm']:
900 904 f0, flag = args
901 905 if branchmerge:
902 906 repo.dirstate.add(f)
903 907 repo.dirstate.remove(f0)
904 908 repo.dirstate.copy(f0, f)
905 909 else:
906 910 repo.dirstate.normal(f)
907 911 repo.dirstate.drop(f0)
908 912
909 913 # directory rename, get
910 914 for f, args, msg in actions['dg']:
911 915 f0, flag = args
912 916 if branchmerge:
913 917 repo.dirstate.add(f)
914 918 repo.dirstate.copy(f0, f)
915 919 else:
916 920 repo.dirstate.normal(f)
917 921
918 922 def update(repo, node, branchmerge, force, partial, ancestor=None,
919 923 mergeancestor=False, labels=None):
920 924 """
921 925 Perform a merge between the working directory and the given node
922 926
923 927 node = the node to update to, or None if unspecified
924 928 branchmerge = whether to merge between branches
925 929 force = whether to force branch merging or file overwriting
926 930 partial = a function to filter file lists (dirstate not updated)
927 931 mergeancestor = whether it is merging with an ancestor. If true,
928 932 we should accept the incoming changes for any prompts that occur.
929 933 If false, merging with an ancestor (fast-forward) is only allowed
930 934 between different named branches. This flag is used by rebase extension
931 935 as a temporary fix and should be avoided in general.
932 936
933 937 The table below shows all the behaviors of the update command
934 938 given the -c and -C or no options, whether the working directory
935 939 is dirty, whether a revision is specified, and the relationship of
936 940 the parent rev to the target rev (linear, on the same named
937 941 branch, or on another named branch).
938 942
939 943 This logic is tested by test-update-branches.t.
940 944
941 945 -c -C dirty rev | linear same cross
942 946 n n n n | ok (1) x
943 947 n n n y | ok ok ok
944 948 n n y n | merge (2) (2)
945 949 n n y y | merge (3) (3)
946 950 n y * * | --- discard ---
947 951 y n y * | --- (4) ---
948 952 y n n * | --- ok ---
949 953 y y * * | --- (5) ---
950 954
951 955 x = can't happen
952 956 * = don't-care
953 957 1 = abort: not a linear update (merge or update --check to force update)
954 958 2 = abort: uncommitted changes (commit and merge, or update --clean to
955 959 discard changes)
956 960 3 = abort: uncommitted changes (commit or update --clean to discard changes)
957 961 4 = abort: uncommitted changes (checked in commands.py)
958 962 5 = incompatible options (checked in commands.py)
959 963
960 964 Return the same tuple as applyupdates().
961 965 """
962 966
963 967 onode = node
964 968 wlock = repo.wlock()
965 969 try:
966 970 wc = repo[None]
967 971 pl = wc.parents()
968 972 p1 = pl[0]
969 973 pas = [None]
970 974 if ancestor:
971 975 pas = [repo[ancestor]]
972 976
973 977 if node is None:
974 978 # Here is where we should consider bookmarks, divergent bookmarks,
975 979 # foreground changesets (successors), and tip of current branch;
976 980 # but currently we are only checking the branch tips.
977 981 try:
978 982 node = repo.branchtip(wc.branch())
979 983 except errormod.RepoLookupError:
980 984 if wc.branch() == 'default': # no default branch!
981 985 node = repo.lookup('tip') # update to tip
982 986 else:
983 987 raise util.Abort(_("branch %s not found") % wc.branch())
984 988
985 989 if p1.obsolete() and not p1.children():
986 990 # allow updating to successors
987 991 successors = obsolete.successorssets(repo, p1.node())
988 992
989 993 # behavior of certain cases is as follows,
990 994 #
991 995 # divergent changesets: update to highest rev, similar to what
992 996 # is currently done when there are more than one head
993 997 # (i.e. 'tip')
994 998 #
995 999 # replaced changesets: same as divergent except we know there
996 1000 # is no conflict
997 1001 #
998 1002 # pruned changeset: no update is done; though, we could
999 1003 # consider updating to the first non-obsolete parent,
1000 1004 # similar to what is current done for 'hg prune'
1001 1005
1002 1006 if successors:
1003 1007 # flatten the list here handles both divergent (len > 1)
1004 1008 # and the usual case (len = 1)
1005 1009 successors = [n for sub in successors for n in sub]
1006 1010
1007 1011 # get the max revision for the given successors set,
1008 1012 # i.e. the 'tip' of a set
1009 1013 node = repo.revs('max(%ln)', successors).first()
1010 1014 pas = [p1]
1011 1015
1012 1016 overwrite = force and not branchmerge
1013 1017
1014 1018 p2 = repo[node]
1015 1019 if pas[0] is None:
1016 1020 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1017 1021 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1018 1022 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1019 1023 else:
1020 1024 pas = [p1.ancestor(p2, warn=branchmerge)]
1021 1025
1022 1026 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1023 1027
1024 1028 ### check phase
1025 1029 if not overwrite and len(pl) > 1:
1026 1030 raise util.Abort(_("outstanding uncommitted merge"))
1027 1031 if branchmerge:
1028 1032 if pas == [p2]:
1029 1033 raise util.Abort(_("merging with a working directory ancestor"
1030 1034 " has no effect"))
1031 1035 elif pas == [p1]:
1032 1036 if not mergeancestor and p1.branch() == p2.branch():
1033 1037 raise util.Abort(_("nothing to merge"),
1034 1038 hint=_("use 'hg update' "
1035 1039 "or check 'hg heads'"))
1036 1040 if not force and (wc.files() or wc.deleted()):
1037 1041 raise util.Abort(_("uncommitted changes"),
1038 1042 hint=_("use 'hg status' to list changes"))
1039 1043 for s in sorted(wc.substate):
1040 1044 if wc.sub(s).dirty():
1041 1045 raise util.Abort(_("uncommitted changes in "
1042 1046 "subrepository '%s'") % s)
1043 1047
1044 1048 elif not overwrite:
1045 1049 if p1 == p2: # no-op update
1046 1050 # call the hooks and exit early
1047 1051 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1048 1052 repo.hook('update', parent1=xp2, parent2='', error=0)
1049 1053 return 0, 0, 0, 0
1050 1054
1051 1055 if pas not in ([p1], [p2]): # nonlinear
1052 1056 dirty = wc.dirty(missing=True)
1053 1057 if dirty or onode is None:
1054 1058 # Branching is a bit strange to ensure we do the minimal
1055 1059 # amount of call to obsolete.background.
1056 1060 foreground = obsolete.foreground(repo, [p1.node()])
1057 1061 # note: the <node> variable contains a random identifier
1058 1062 if repo[node].node() in foreground:
1059 1063 pas = [p1] # allow updating to successors
1060 1064 elif dirty:
1061 1065 msg = _("uncommitted changes")
1062 1066 if onode is None:
1063 1067 hint = _("commit and merge, or update --clean to"
1064 1068 " discard changes")
1065 1069 else:
1066 1070 hint = _("commit or update --clean to discard"
1067 1071 " changes")
1068 1072 raise util.Abort(msg, hint=hint)
1069 1073 else: # node is none
1070 1074 msg = _("not a linear update")
1071 1075 hint = _("merge or update --check to force update")
1072 1076 raise util.Abort(msg, hint=hint)
1073 1077 else:
1074 1078 # Allow jumping branches if clean and specific rev given
1075 1079 pas = [p1]
1076 1080
1077 1081 followcopies = False
1078 1082 if overwrite:
1079 1083 pas = [wc]
1080 1084 elif pas == [p2]: # backwards
1081 1085 pas = [wc.p1()]
1082 1086 elif not branchmerge and not wc.dirty(missing=True):
1083 1087 pass
1084 1088 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1085 1089 followcopies = True
1086 1090
1087 1091 ### calculate phase
1088 1092 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1089 1093 partial, mergeancestor, followcopies)
1090 1094
1091 1095 ### apply phase
1092 1096 if not branchmerge: # just jump to the new rev
1093 1097 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1094 1098 if not partial:
1095 1099 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1096 1100 # note that we're in the middle of an update
1097 1101 repo.vfs.write('updatestate', p2.hex())
1098 1102
1099 1103 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1100 1104
1101 1105 if not partial:
1102 1106 repo.dirstate.beginparentchange()
1103 1107 repo.setparents(fp1, fp2)
1104 1108 recordupdates(repo, actions, branchmerge)
1105 1109 # update completed, clear state
1106 1110 util.unlink(repo.join('updatestate'))
1107 1111
1108 1112 if not branchmerge:
1109 1113 repo.dirstate.setbranch(p2.branch())
1110 1114 repo.dirstate.endparentchange()
1111 1115 finally:
1112 1116 wlock.release()
1113 1117
1114 1118 if not partial:
1115 1119 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1116 1120 return stats
1117 1121
1118 1122 def graft(repo, ctx, pctx, labels):
1119 1123 """Do a graft-like merge.
1120 1124
1121 1125 This is a merge where the merge ancestor is chosen such that one
1122 1126 or more changesets are grafted onto the current changeset. In
1123 1127 addition to the merge, this fixes up the dirstate to include only
1124 1128 a single parent and tries to duplicate any renames/copies
1125 1129 appropriately.
1126 1130
1127 1131 ctx - changeset to rebase
1128 1132 pctx - merge base, usually ctx.p1()
1129 1133 labels - merge labels eg ['local', 'graft']
1130 1134
1131 1135 """
1132 1136
1133 1137 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1134 1138 labels=labels)
1135 1139 # drop the second merge parent
1136 1140 repo.dirstate.beginparentchange()
1137 1141 repo.setparents(repo['.'].node(), nullid)
1138 1142 repo.dirstate.write()
1139 1143 # fix up dirstate for copies and renames
1140 1144 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1141 1145 repo.dirstate.endparentchange()
1142 1146 return stats
@@ -1,962 +1,962
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], "wb")
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 $ uc() { up $1; hg cp $1 $2; } # update + copy
72 72 $ um() { up $1; hg mv $1 $2; }
73 73 $ nc() { hg cp $1 $2; } # just copy
74 74 $ nm() { hg mv $1 $2; } # just move
75 75 $ tm "up a " "nc a b" " " "1 get local a to b"
76 76 created new head
77 77 --------------
78 78 test L:up a R:nc a b W: - 1 get local a to b
79 79 --------------
80 80 searching for copies back to rev 1
81 81 unmatched files in other:
82 82 b
83 83 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
84 84 src: 'a' -> dst: 'b' *
85 85 checking for directory renames
86 86 resolving manifests
87 87 branchmerge: True, force: False, partial: False
88 88 ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
89 89 preserving a for resolve of b
90 90 preserving rev for resolve of rev
91 91 a: keep -> k
92 92 b: remote copied from a -> m
93 93 updating: b 1/2 files (50.00%)
94 94 picked tool 'python ../merge' for b (binary False symlink False)
95 95 merging a and b to b
96 96 my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
97 97 premerge successful
98 98 rev: versions differ -> m
99 99 updating: rev 2/2 files (100.00%)
100 100 picked tool 'python ../merge' for rev (binary False symlink False)
101 101 merging rev
102 102 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
103 103 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
104 104 (branch merge, don't forget to commit)
105 105 --------------
106 106 M b
107 107 a
108 108 C a
109 109 --------------
110 110
111 111 $ tm "nc a b" "up a " " " "2 get rem change to a and b"
112 112 created new head
113 113 --------------
114 114 test L:nc a b R:up a W: - 2 get rem change to a and b
115 115 --------------
116 116 searching for copies back to rev 1
117 117 unmatched files in local:
118 118 b
119 119 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
120 120 src: 'a' -> dst: 'b' *
121 121 checking for directory renames
122 122 resolving manifests
123 123 branchmerge: True, force: False, partial: False
124 124 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
125 125 preserving b for resolve of b
126 126 preserving rev for resolve of rev
127 127 a: remote is newer -> g
128 128 getting a
129 129 updating: a 1/3 files (33.33%)
130 130 b: local copied/moved from a -> m
131 131 updating: b 2/3 files (66.67%)
132 132 picked tool 'python ../merge' for b (binary False symlink False)
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
137 137 updating: rev 3/3 files (100.00%)
138 138 picked tool 'python ../merge' for rev (binary False symlink False)
139 139 merging rev
140 140 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
141 141 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
142 142 (branch merge, don't forget to commit)
143 143 --------------
144 144 M a
145 145 M b
146 146 a
147 147 --------------
148 148
149 149 $ tm "up a " "nm a b" " " "3 get local a change to b, remove a"
150 150 created new head
151 151 --------------
152 152 test L:up a R:nm a b W: - 3 get local a change to b, remove a
153 153 --------------
154 154 searching for copies back to rev 1
155 155 unmatched files in other:
156 156 b
157 157 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
158 158 src: 'a' -> dst: 'b' *
159 159 checking for directory renames
160 160 resolving manifests
161 161 branchmerge: True, force: False, partial: False
162 162 ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
163 163 preserving a for resolve of b
164 164 preserving rev for resolve of rev
165 165 removing a
166 166 b: remote moved from a -> m
167 167 updating: b 1/2 files (50.00%)
168 168 picked tool 'python ../merge' for b (binary False symlink False)
169 169 merging a and b to b
170 170 my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
171 171 premerge successful
172 172 rev: versions differ -> m
173 173 updating: rev 2/2 files (100.00%)
174 174 picked tool 'python ../merge' for rev (binary False symlink False)
175 175 merging rev
176 176 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
177 177 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
178 178 (branch merge, don't forget to commit)
179 179 --------------
180 180 M b
181 181 a
182 182 --------------
183 183
184 184 $ tm "nm a b" "up a " " " "4 get remote change to b"
185 185 created new head
186 186 --------------
187 187 test L:nm a b R:up a W: - 4 get remote change to b
188 188 --------------
189 189 searching for copies back to rev 1
190 190 unmatched files in local:
191 191 b
192 192 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
193 193 src: 'a' -> dst: 'b' *
194 194 checking for directory renames
195 195 resolving manifests
196 196 branchmerge: True, force: False, partial: False
197 197 ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
198 198 preserving b for resolve of b
199 199 preserving rev for resolve of rev
200 200 b: local copied/moved from a -> m
201 201 updating: b 1/2 files (50.00%)
202 202 picked tool 'python ../merge' for b (binary False symlink False)
203 203 merging b and a to b
204 204 my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
205 205 premerge successful
206 206 rev: versions differ -> m
207 207 updating: rev 2/2 files (100.00%)
208 208 picked tool 'python ../merge' for rev (binary False symlink False)
209 209 merging rev
210 210 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
211 211 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
212 212 (branch merge, don't forget to commit)
213 213 --------------
214 214 M b
215 215 a
216 216 --------------
217 217
218 218 $ tm " " "nc a b" " " "5 get b"
219 219 created new head
220 220 --------------
221 221 test L: R:nc a b W: - 5 get b
222 222 --------------
223 223 searching for copies back to rev 1
224 224 unmatched files in other:
225 225 b
226 226 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
227 227 src: 'a' -> dst: 'b'
228 228 checking for directory renames
229 229 resolving manifests
230 230 branchmerge: True, force: False, partial: False
231 231 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
232 232 preserving rev for resolve of rev
233 233 b: remote created -> g
234 234 getting b
235 235 updating: b 1/2 files (50.00%)
236 236 rev: versions differ -> m
237 237 updating: rev 2/2 files (100.00%)
238 238 picked tool 'python ../merge' for rev (binary False symlink False)
239 239 merging rev
240 240 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
241 241 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
242 242 (branch merge, don't forget to commit)
243 243 --------------
244 244 M b
245 245 C a
246 246 --------------
247 247
248 248 $ tm "nc a b" " " " " "6 nothing"
249 249 created new head
250 250 --------------
251 251 test L:nc a b R: W: - 6 nothing
252 252 --------------
253 253 searching for copies back to rev 1
254 254 unmatched files in local:
255 255 b
256 256 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
257 257 src: 'a' -> dst: 'b'
258 258 checking for directory renames
259 259 resolving manifests
260 260 branchmerge: True, force: False, partial: False
261 261 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
262 262 preserving rev for resolve of rev
263 263 rev: versions differ -> m
264 264 updating: rev 1/1 files (100.00%)
265 265 picked tool 'python ../merge' for rev (binary False symlink False)
266 266 merging rev
267 267 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
268 268 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
269 269 (branch merge, don't forget to commit)
270 270 --------------
271 271 C a
272 272 C b
273 273 --------------
274 274
275 275 $ tm " " "nm a b" " " "7 get b"
276 276 created new head
277 277 --------------
278 278 test L: R:nm a b W: - 7 get b
279 279 --------------
280 280 searching for copies back to rev 1
281 281 unmatched files in other:
282 282 b
283 283 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
284 284 src: 'a' -> dst: 'b'
285 285 checking for directory renames
286 286 resolving manifests
287 287 branchmerge: True, force: False, partial: False
288 288 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
289 289 preserving rev for resolve of rev
290 290 a: other deleted -> r
291 291 removing a
292 292 updating: a 1/3 files (33.33%)
293 293 b: remote created -> g
294 294 getting b
295 295 updating: b 2/3 files (66.67%)
296 296 rev: versions differ -> m
297 297 updating: rev 3/3 files (100.00%)
298 298 picked tool 'python ../merge' for rev (binary False symlink False)
299 299 merging rev
300 300 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
301 301 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
302 302 (branch merge, don't forget to commit)
303 303 --------------
304 304 M b
305 305 --------------
306 306
307 307 $ tm "nm a b" " " " " "8 nothing"
308 308 created new head
309 309 --------------
310 310 test L:nm a b R: W: - 8 nothing
311 311 --------------
312 312 searching for copies back to rev 1
313 313 unmatched files in local:
314 314 b
315 315 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
316 316 src: 'a' -> dst: 'b'
317 317 checking for directory renames
318 318 resolving manifests
319 319 branchmerge: True, force: False, partial: False
320 320 ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
321 321 preserving rev for resolve of rev
322 322 rev: versions differ -> m
323 323 updating: rev 1/1 files (100.00%)
324 324 picked tool 'python ../merge' for rev (binary False symlink False)
325 325 merging rev
326 326 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
327 327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
328 328 (branch merge, don't forget to commit)
329 329 --------------
330 330 C b
331 331 --------------
332 332
333 333 $ tm "um a b" "um a b" " " "9 do merge with ancestor in a"
334 334 created new head
335 335 --------------
336 336 test L:um a b R:um a b W: - 9 do merge with ancestor in a
337 337 --------------
338 338 searching for copies back to rev 1
339 339 unmatched files new in both:
340 340 b
341 341 resolving manifests
342 342 branchmerge: True, force: False, partial: False
343 343 ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
344 344 preserving b for resolve of b
345 345 preserving rev for resolve of rev
346 b: both created -> m
346 b: both renamed from a -> m
347 347 updating: b 1/2 files (50.00%)
348 348 picked tool 'python ../merge' for b (binary False symlink False)
349 349 merging b
350 350 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
351 351 rev: versions differ -> m
352 352 updating: rev 2/2 files (100.00%)
353 353 picked tool 'python ../merge' for rev (binary False symlink False)
354 354 merging rev
355 355 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
356 356 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
357 357 (branch merge, don't forget to commit)
358 358 --------------
359 359 M b
360 360 --------------
361 361
362 362
363 363 m "um a c" "um x c" " " "10 do merge with no ancestor"
364 364
365 365 $ tm "nm a b" "nm a c" " " "11 get c, keep b"
366 366 created new head
367 367 --------------
368 368 test L:nm a b R:nm a c W: - 11 get c, keep b
369 369 --------------
370 370 searching for copies back to rev 1
371 371 unmatched files in local:
372 372 b
373 373 unmatched files in other:
374 374 c
375 375 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
376 376 src: 'a' -> dst: 'b' !
377 377 src: 'a' -> dst: 'c' !
378 378 checking for directory renames
379 379 resolving manifests
380 380 branchmerge: True, force: False, partial: False
381 381 ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
382 382 preserving rev for resolve of rev
383 383 c: remote created -> g
384 384 getting c
385 385 updating: c 1/3 files (33.33%)
386 386 rev: versions differ -> m
387 387 updating: rev 2/3 files (66.67%)
388 388 picked tool 'python ../merge' for rev (binary False symlink False)
389 389 merging rev
390 390 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
391 391 a: divergent renames -> dr
392 392 updating: a 3/3 files (100.00%)
393 393 note: possible conflict - a was renamed multiple times to:
394 394 b
395 395 c
396 396 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
397 397 (branch merge, don't forget to commit)
398 398 --------------
399 399 M c
400 400 C b
401 401 --------------
402 402
403 403 $ tm "nc a b" "up b " " " "12 merge b no ancestor"
404 404 created new head
405 405 --------------
406 406 test L:nc a b R:up b W: - 12 merge b no ancestor
407 407 --------------
408 408 searching for copies back to rev 1
409 409 unmatched files new in both:
410 410 b
411 411 resolving manifests
412 412 branchmerge: True, force: False, partial: False
413 413 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
414 414 preserving b for resolve of b
415 415 preserving rev for resolve of rev
416 416 b: both created -> m
417 417 updating: b 1/2 files (50.00%)
418 418 picked tool 'python ../merge' for b (binary False symlink False)
419 419 merging b
420 420 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
421 421 rev: versions differ -> m
422 422 updating: rev 2/2 files (100.00%)
423 423 picked tool 'python ../merge' for rev (binary False symlink False)
424 424 merging rev
425 425 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
426 426 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
427 427 (branch merge, don't forget to commit)
428 428 --------------
429 429 M b
430 430 C a
431 431 --------------
432 432
433 433 $ tm "up b " "nm a b" " " "13 merge b no ancestor"
434 434 created new head
435 435 --------------
436 436 test L:up b R:nm a b W: - 13 merge b no ancestor
437 437 --------------
438 438 searching for copies back to rev 1
439 439 unmatched files new in both:
440 440 b
441 441 resolving manifests
442 442 branchmerge: True, force: False, partial: False
443 443 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
444 444 preserving b for resolve of b
445 445 preserving rev for resolve of rev
446 446 a: other deleted -> r
447 447 removing a
448 448 updating: a 1/3 files (33.33%)
449 449 b: both created -> m
450 450 updating: b 2/3 files (66.67%)
451 451 picked tool 'python ../merge' for b (binary False symlink False)
452 452 merging b
453 453 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
454 454 rev: versions differ -> m
455 455 updating: rev 3/3 files (100.00%)
456 456 picked tool 'python ../merge' for rev (binary False symlink False)
457 457 merging rev
458 458 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
459 459 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
460 460 (branch merge, don't forget to commit)
461 461 --------------
462 462 M b
463 463 --------------
464 464
465 465 $ tm "nc a b" "up a b" " " "14 merge b no ancestor"
466 466 created new head
467 467 --------------
468 468 test L:nc a b R:up a b W: - 14 merge b no ancestor
469 469 --------------
470 470 searching for copies back to rev 1
471 471 unmatched files new in both:
472 472 b
473 473 resolving manifests
474 474 branchmerge: True, force: False, partial: False
475 475 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
476 476 preserving b for resolve of b
477 477 preserving rev for resolve of rev
478 478 a: remote is newer -> g
479 479 getting a
480 480 updating: a 1/3 files (33.33%)
481 481 b: both created -> m
482 482 updating: b 2/3 files (66.67%)
483 483 picked tool 'python ../merge' for b (binary False symlink False)
484 484 merging b
485 485 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
486 486 rev: versions differ -> m
487 487 updating: rev 3/3 files (100.00%)
488 488 picked tool 'python ../merge' for rev (binary False symlink False)
489 489 merging rev
490 490 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
491 491 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
492 492 (branch merge, don't forget to commit)
493 493 --------------
494 494 M a
495 495 M b
496 496 --------------
497 497
498 498 $ tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
499 499 created new head
500 500 --------------
501 501 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
502 502 --------------
503 503 searching for copies back to rev 1
504 504 unmatched files new in both:
505 505 b
506 506 resolving manifests
507 507 branchmerge: True, force: False, partial: False
508 508 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
509 509 preserving b for resolve of b
510 510 preserving rev for resolve of rev
511 511 a: other deleted -> r
512 512 removing a
513 513 updating: a 1/3 files (33.33%)
514 514 b: both created -> m
515 515 updating: b 2/3 files (66.67%)
516 516 picked tool 'python ../merge' for b (binary False symlink False)
517 517 merging b
518 518 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
519 519 rev: versions differ -> m
520 520 updating: rev 3/3 files (100.00%)
521 521 picked tool 'python ../merge' for rev (binary False symlink False)
522 522 merging rev
523 523 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
524 524 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
525 525 (branch merge, don't forget to commit)
526 526 --------------
527 527 M b
528 528 --------------
529 529
530 530 $ tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
531 531 created new head
532 532 --------------
533 533 test L:nc a b R:up a b W: - 16 get a, merge b no ancestor
534 534 --------------
535 535 searching for copies back to rev 1
536 536 unmatched files new in both:
537 537 b
538 538 resolving manifests
539 539 branchmerge: True, force: False, partial: False
540 540 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
541 541 preserving b for resolve of b
542 542 preserving rev for resolve of rev
543 543 a: remote is newer -> g
544 544 getting a
545 545 updating: a 1/3 files (33.33%)
546 546 b: both created -> m
547 547 updating: b 2/3 files (66.67%)
548 548 picked tool 'python ../merge' for b (binary False symlink False)
549 549 merging b
550 550 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
551 551 rev: versions differ -> m
552 552 updating: rev 3/3 files (100.00%)
553 553 picked tool 'python ../merge' for rev (binary False symlink False)
554 554 merging rev
555 555 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
556 556 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
557 557 (branch merge, don't forget to commit)
558 558 --------------
559 559 M a
560 560 M b
561 561 --------------
562 562
563 563 $ tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
564 564 created new head
565 565 --------------
566 566 test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor
567 567 --------------
568 568 searching for copies back to rev 1
569 569 unmatched files new in both:
570 570 b
571 571 resolving manifests
572 572 branchmerge: True, force: False, partial: False
573 573 ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
574 574 preserving b for resolve of b
575 575 preserving rev for resolve of rev
576 576 a: keep -> k
577 577 b: both created -> m
578 578 updating: b 1/2 files (50.00%)
579 579 picked tool 'python ../merge' for b (binary False symlink False)
580 580 merging b
581 581 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
582 582 rev: versions differ -> m
583 583 updating: rev 2/2 files (100.00%)
584 584 picked tool 'python ../merge' for rev (binary False symlink False)
585 585 merging rev
586 586 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
587 587 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
588 588 (branch merge, don't forget to commit)
589 589 --------------
590 590 M b
591 591 C a
592 592 --------------
593 593
594 594 $ tm "nm a b" "up a b" " " "18 merge b no ancestor"
595 595 created new head
596 596 --------------
597 597 test L:nm a b R:up a b W: - 18 merge b no ancestor
598 598 --------------
599 599 searching for copies back to rev 1
600 600 unmatched files new in both:
601 601 b
602 602 resolving manifests
603 603 branchmerge: True, force: False, partial: False
604 604 ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
605 605 remote changed a which local deleted
606 606 use (c)hanged version or leave (d)eleted? c
607 607 preserving b for resolve of b
608 608 preserving rev for resolve of rev
609 609 a: prompt recreating -> g
610 610 getting a
611 611 updating: a 1/3 files (33.33%)
612 612 b: both created -> m
613 613 updating: b 2/3 files (66.67%)
614 614 picked tool 'python ../merge' for b (binary False symlink False)
615 615 merging b
616 616 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
617 617 rev: versions differ -> m
618 618 updating: rev 3/3 files (100.00%)
619 619 picked tool 'python ../merge' for rev (binary False symlink False)
620 620 merging rev
621 621 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
622 622 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
623 623 (branch merge, don't forget to commit)
624 624 --------------
625 625 M a
626 626 M b
627 627 --------------
628 628
629 629 $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
630 630 created new head
631 631 --------------
632 632 test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a
633 633 --------------
634 634 searching for copies back to rev 1
635 635 unmatched files new in both:
636 636 b
637 637 resolving manifests
638 638 branchmerge: True, force: False, partial: False
639 639 ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
640 640 local changed a which remote deleted
641 641 use (c)hanged version or (d)elete? c
642 642 preserving b for resolve of b
643 643 preserving rev for resolve of rev
644 644 a: prompt keep -> a
645 645 updating: a 1/3 files (33.33%)
646 646 b: both created -> m
647 647 updating: b 2/3 files (66.67%)
648 648 picked tool 'python ../merge' for b (binary False symlink False)
649 649 merging b
650 650 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
651 651 rev: versions differ -> m
652 652 updating: rev 3/3 files (100.00%)
653 653 picked tool 'python ../merge' for rev (binary False symlink False)
654 654 merging rev
655 655 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
656 656 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
657 657 (branch merge, don't forget to commit)
658 658 --------------
659 659 M b
660 660 C a
661 661 --------------
662 662
663 663 $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
664 664 created new head
665 665 --------------
666 666 test L:up a R:um a b W: - 20 merge a and b to b, remove a
667 667 --------------
668 668 searching for copies back to rev 1
669 669 unmatched files in other:
670 670 b
671 671 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
672 672 src: 'a' -> dst: 'b' *
673 673 checking for directory renames
674 674 resolving manifests
675 675 branchmerge: True, force: False, partial: False
676 676 ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
677 677 preserving a for resolve of b
678 678 preserving rev for resolve of rev
679 679 removing a
680 680 b: remote moved from a -> m
681 681 updating: b 1/2 files (50.00%)
682 682 picked tool 'python ../merge' for b (binary False symlink False)
683 683 merging a and b to b
684 684 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
685 685 rev: versions differ -> m
686 686 updating: rev 2/2 files (100.00%)
687 687 picked tool 'python ../merge' for rev (binary False symlink False)
688 688 merging rev
689 689 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
690 690 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
691 691 (branch merge, don't forget to commit)
692 692 --------------
693 693 M b
694 694 a
695 695 --------------
696 696
697 697 $ tm "um a b" "up a " " " "21 merge a and b to b"
698 698 created new head
699 699 --------------
700 700 test L:um a b R:up a W: - 21 merge a and b to b
701 701 --------------
702 702 searching for copies back to rev 1
703 703 unmatched files in local:
704 704 b
705 705 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
706 706 src: 'a' -> dst: 'b' *
707 707 checking for directory renames
708 708 resolving manifests
709 709 branchmerge: True, force: False, partial: False
710 710 ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
711 711 preserving b for resolve of b
712 712 preserving rev for resolve of rev
713 713 b: local copied/moved from a -> m
714 714 updating: b 1/2 files (50.00%)
715 715 picked tool 'python ../merge' for b (binary False symlink False)
716 716 merging b and a to b
717 717 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
718 718 rev: versions differ -> m
719 719 updating: rev 2/2 files (100.00%)
720 720 picked tool 'python ../merge' for rev (binary False symlink False)
721 721 merging rev
722 722 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
723 723 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
724 724 (branch merge, don't forget to commit)
725 725 --------------
726 726 M b
727 727 a
728 728 --------------
729 729
730 730
731 731 m "nm a b" "um x a" " " "22 get a, keep b"
732 732
733 733 $ tm "nm a b" "up a c" " " "23 get c, keep b"
734 734 created new head
735 735 --------------
736 736 test L:nm a b R:up a c W: - 23 get c, keep b
737 737 --------------
738 738 searching for copies back to rev 1
739 739 unmatched files in local:
740 740 b
741 741 unmatched files in other:
742 742 c
743 743 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
744 744 src: 'a' -> dst: 'b' *
745 745 checking for directory renames
746 746 resolving manifests
747 747 branchmerge: True, force: False, partial: False
748 748 ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
749 749 preserving b for resolve of b
750 750 preserving rev for resolve of rev
751 751 c: remote created -> g
752 752 getting c
753 753 updating: c 1/3 files (33.33%)
754 754 b: local copied/moved from a -> m
755 755 updating: b 2/3 files (66.67%)
756 756 picked tool 'python ../merge' for b (binary False symlink False)
757 757 merging b and a to b
758 758 my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
759 759 premerge successful
760 760 rev: versions differ -> m
761 761 updating: rev 3/3 files (100.00%)
762 762 picked tool 'python ../merge' for rev (binary False symlink False)
763 763 merging rev
764 764 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
765 765 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
766 766 (branch merge, don't forget to commit)
767 767 --------------
768 768 M b
769 769 a
770 770 M c
771 771 --------------
772 772
773 773
774 774 $ cd ..
775 775
776 776
777 777 Systematic and terse testing of merge merges and ancestor calculation:
778 778
779 779 Expected result:
780 780
781 781 \ a m1 m2 dst
782 782 0 - f f f "versions differ"
783 783 1 f g g g "versions differ"
784 784 2 f f f f "versions differ"
785 785 3 f f g f+g "remote copied to " + f
786 786 4 f f g g "remote moved to " + f
787 787 5 f g f f+g "local copied to " + f2
788 788 6 f g f g "local moved to " + f2
789 789 7 - (f) f f "remote differs from untracked local"
790 790 8 f (f) f f "remote differs from untracked local"
791 791
792 792 $ hg init ancestortest
793 793 $ cd ancestortest
794 794 $ for x in 1 2 3 4 5 6 8; do mkdir $x; echo a > $x/f; done
795 795 $ hg ci -Aqm "a"
796 796 $ mkdir 0
797 797 $ touch 0/f
798 798 $ hg mv 1/f 1/g
799 799 $ hg cp 5/f 5/g
800 800 $ hg mv 6/f 6/g
801 801 $ hg rm 8/f
802 802 $ for x in */*; do echo m1 > $x; done
803 803 $ hg ci -Aqm "m1"
804 804 $ hg up -qr0
805 805 $ mkdir 0 7
806 806 $ touch 0/f 7/f
807 807 $ hg mv 1/f 1/g
808 808 $ hg cp 3/f 3/g
809 809 $ hg mv 4/f 4/g
810 810 $ for x in */*; do echo m2 > $x; done
811 811 $ hg ci -Aqm "m2"
812 812 $ hg up -qr1
813 813 $ mkdir 7 8
814 814 $ echo m > 7/f
815 815 $ echo m > 8/f
816 816 $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^updating:/,$d' 2> /dev/null
817 817 searching for copies back to rev 1
818 818 unmatched files in local:
819 819 5/g
820 820 6/g
821 821 unmatched files in other:
822 822 3/g
823 823 4/g
824 824 7/f
825 825 unmatched files new in both:
826 826 0/f
827 827 1/g
828 828 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
829 829 src: '3/f' -> dst: '3/g' *
830 830 src: '4/f' -> dst: '4/g' *
831 831 src: '5/f' -> dst: '5/g' *
832 832 src: '6/f' -> dst: '6/g' *
833 833 checking for directory renames
834 834 resolving manifests
835 835 branchmerge: True, force: True, partial: False
836 836 ancestor: e6cb3cf11019, local: ec44bf929ab5+, remote: c62e34d0b898
837 837 remote changed 8/f which local deleted
838 838 use (c)hanged version or leave (d)eleted? c
839 839 preserving 0/f for resolve of 0/f
840 840 preserving 1/g for resolve of 1/g
841 841 preserving 2/f for resolve of 2/f
842 842 preserving 3/f for resolve of 3/f
843 843 preserving 3/f for resolve of 3/g
844 844 preserving 4/f for resolve of 4/g
845 845 preserving 5/f for resolve of 5/f
846 846 preserving 5/g for resolve of 5/g
847 847 preserving 6/g for resolve of 6/g
848 848 preserving 7/f for resolve of 7/f
849 849 removing 4/f
850 850 8/f: prompt recreating -> g
851 851 getting 8/f
852 852 $ hg mani
853 853 0/f
854 854 1/g
855 855 2/f
856 856 3/f
857 857 4/f
858 858 5/f
859 859 5/g
860 860 6/g
861 861 $ for f in */*; do echo $f:; cat $f; done
862 862 0/f:
863 863 m1
864 864 0/f.base:
865 865 0/f.local:
866 866 m1
867 867 0/f.orig:
868 868 m1
869 869 0/f.other:
870 870 m2
871 871 1/g:
872 872 m1
873 873 1/g.base:
874 874 a
875 875 1/g.local:
876 876 m1
877 877 1/g.orig:
878 878 m1
879 879 1/g.other:
880 880 m2
881 881 2/f:
882 882 m1
883 883 2/f.base:
884 884 a
885 885 2/f.local:
886 886 m1
887 887 2/f.orig:
888 888 m1
889 889 2/f.other:
890 890 m2
891 891 3/f:
892 892 m1
893 893 3/f.base:
894 894 a
895 895 3/f.local:
896 896 m1
897 897 3/f.orig:
898 898 m1
899 899 3/f.other:
900 900 m2
901 901 3/g:
902 902 m1
903 903 3/g.base:
904 904 a
905 905 3/g.local:
906 906 m1
907 907 3/g.orig:
908 908 m1
909 909 3/g.other:
910 910 m2
911 911 4/g:
912 912 m1
913 913 4/g.base:
914 914 a
915 915 4/g.local:
916 916 m1
917 917 4/g.orig:
918 918 m1
919 919 4/g.other:
920 920 m2
921 921 5/f:
922 922 m1
923 923 5/f.base:
924 924 a
925 925 5/f.local:
926 926 m1
927 927 5/f.orig:
928 928 m1
929 929 5/f.other:
930 930 m2
931 931 5/g:
932 932 m1
933 933 5/g.base:
934 934 a
935 935 5/g.local:
936 936 m1
937 937 5/g.orig:
938 938 m1
939 939 5/g.other:
940 940 m2
941 941 6/g:
942 942 m1
943 943 6/g.base:
944 944 a
945 945 6/g.local:
946 946 m1
947 947 6/g.orig:
948 948 m1
949 949 6/g.other:
950 950 m2
951 951 7/f:
952 952 m
953 953 7/f.base:
954 954 7/f.local:
955 955 m
956 956 7/f.orig:
957 957 m
958 958 7/f.other:
959 959 m2
960 960 8/f:
961 961 m2
962 962 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now