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