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