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