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