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