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