##// END OF EJS Templates
mergestate: add a way to record pending dirstate actions...
Siddharth Agarwal -
r27088:e2b79f57 default
parent child Browse files
Show More
@@ -1,1456 +1,1461 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 def recordactions(self):
521 """record remove/add/get actions in the dirstate"""
522 branchmerge = self._repo.dirstate.p2() != nullid
523 recordupdates(self._repo, self.actions(), branchmerge)
524
520 525 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
521 526 if f2 is None:
522 527 f2 = f
523 528 return (os.path.isfile(repo.wjoin(f))
524 529 and repo.wvfs.audit.check(f)
525 530 and repo.dirstate.normalize(f) not in repo.dirstate
526 531 and mctx[f2].cmp(wctx[f]))
527 532
528 533 def _checkunknownfiles(repo, wctx, mctx, force, actions):
529 534 """
530 535 Considers any actions that care about the presence of conflicting unknown
531 536 files. For some actions, the result is to abort; for others, it is to
532 537 choose a different action.
533 538 """
534 539 aborts = []
535 540 if not force:
536 541 for f, (m, args, msg) in actions.iteritems():
537 542 if m in ('c', 'dc'):
538 543 if _checkunknownfile(repo, wctx, mctx, f):
539 544 aborts.append(f)
540 545 elif m == 'dg':
541 546 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
542 547 aborts.append(f)
543 548
544 549 for f in sorted(aborts):
545 550 repo.ui.warn(_("%s: untracked file differs\n") % f)
546 551 if aborts:
547 552 raise error.Abort(_("untracked files in working directory differ "
548 553 "from files in requested revision"))
549 554
550 555 for f, (m, args, msg) in actions.iteritems():
551 556 if m == 'c':
552 557 actions[f] = ('g', args, msg)
553 558 elif m == 'cm':
554 559 fl2, anc = args
555 560 different = _checkunknownfile(repo, wctx, mctx, f)
556 561 if different:
557 562 actions[f] = ('m', (f, f, None, False, anc),
558 563 "remote differs from untracked local")
559 564 else:
560 565 actions[f] = ('g', (fl2,), "remote created")
561 566
562 567 def _forgetremoved(wctx, mctx, branchmerge):
563 568 """
564 569 Forget removed files
565 570
566 571 If we're jumping between revisions (as opposed to merging), and if
567 572 neither the working directory nor the target rev has the file,
568 573 then we need to remove it from the dirstate, to prevent the
569 574 dirstate from listing the file when it is no longer in the
570 575 manifest.
571 576
572 577 If we're merging, and the other revision has removed a file
573 578 that is not present in the working directory, we need to mark it
574 579 as removed.
575 580 """
576 581
577 582 actions = {}
578 583 m = 'f'
579 584 if branchmerge:
580 585 m = 'r'
581 586 for f in wctx.deleted():
582 587 if f not in mctx:
583 588 actions[f] = m, None, "forget deleted"
584 589
585 590 if not branchmerge:
586 591 for f in wctx.removed():
587 592 if f not in mctx:
588 593 actions[f] = 'f', None, "forget removed"
589 594
590 595 return actions
591 596
592 597 def _checkcollision(repo, wmf, actions):
593 598 # build provisional merged manifest up
594 599 pmmf = set(wmf)
595 600
596 601 if actions:
597 602 # k, dr, e and rd are no-op
598 603 for m in 'a', 'f', 'g', 'cd', 'dc':
599 604 for f, args, msg in actions[m]:
600 605 pmmf.add(f)
601 606 for f, args, msg in actions['r']:
602 607 pmmf.discard(f)
603 608 for f, args, msg in actions['dm']:
604 609 f2, flags = args
605 610 pmmf.discard(f2)
606 611 pmmf.add(f)
607 612 for f, args, msg in actions['dg']:
608 613 pmmf.add(f)
609 614 for f, args, msg in actions['m']:
610 615 f1, f2, fa, move, anc = args
611 616 if move:
612 617 pmmf.discard(f1)
613 618 pmmf.add(f)
614 619
615 620 # check case-folding collision in provisional merged manifest
616 621 foldmap = {}
617 622 for f in sorted(pmmf):
618 623 fold = util.normcase(f)
619 624 if fold in foldmap:
620 625 raise error.Abort(_("case-folding collision between %s and %s")
621 626 % (f, foldmap[fold]))
622 627 foldmap[fold] = f
623 628
624 629 # check case-folding of directories
625 630 foldprefix = unfoldprefix = lastfull = ''
626 631 for fold, f in sorted(foldmap.items()):
627 632 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
628 633 # the folded prefix matches but actual casing is different
629 634 raise error.Abort(_("case-folding collision between "
630 635 "%s and directory of %s") % (lastfull, f))
631 636 foldprefix = fold + '/'
632 637 unfoldprefix = f + '/'
633 638 lastfull = f
634 639
635 640 def driverpreprocess(repo, ms, wctx, labels=None):
636 641 """run the preprocess step of the merge driver, if any
637 642
638 643 This is currently not implemented -- it's an extension point."""
639 644 return True
640 645
641 646 def driverconclude(repo, ms, wctx, labels=None):
642 647 """run the conclude step of the merge driver, if any
643 648
644 649 This is currently not implemented -- it's an extension point."""
645 650 return True
646 651
647 652 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
648 653 acceptremote, followcopies):
649 654 """
650 655 Merge p1 and p2 with ancestor pa and generate merge action list
651 656
652 657 branchmerge and force are as passed in to update
653 658 partial = function to filter file lists
654 659 acceptremote = accept the incoming changes without prompting
655 660 """
656 661
657 662 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
658 663
659 664 # manifests fetched in order are going to be faster, so prime the caches
660 665 [x.manifest() for x in
661 666 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
662 667
663 668 if followcopies:
664 669 ret = copies.mergecopies(repo, wctx, p2, pa)
665 670 copy, movewithdir, diverge, renamedelete = ret
666 671
667 672 repo.ui.note(_("resolving manifests\n"))
668 673 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
669 674 % (bool(branchmerge), bool(force), bool(partial)))
670 675 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
671 676
672 677 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
673 678 copied = set(copy.values())
674 679 copied.update(movewithdir.values())
675 680
676 681 if '.hgsubstate' in m1:
677 682 # check whether sub state is modified
678 683 for s in sorted(wctx.substate):
679 684 if wctx.sub(s).dirty():
680 685 m1['.hgsubstate'] += '+'
681 686 break
682 687
683 688 # Compare manifests
684 689 diff = m1.diff(m2)
685 690
686 691 actions = {}
687 692 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
688 693 if partial and not partial(f):
689 694 continue
690 695 if n1 and n2: # file exists on both local and remote side
691 696 if f not in ma:
692 697 fa = copy.get(f, None)
693 698 if fa is not None:
694 699 actions[f] = ('m', (f, f, fa, False, pa.node()),
695 700 "both renamed from " + fa)
696 701 else:
697 702 actions[f] = ('m', (f, f, None, False, pa.node()),
698 703 "both created")
699 704 else:
700 705 a = ma[f]
701 706 fla = ma.flags(f)
702 707 nol = 'l' not in fl1 + fl2 + fla
703 708 if n2 == a and fl2 == fla:
704 709 actions[f] = ('k' , (), "remote unchanged")
705 710 elif n1 == a and fl1 == fla: # local unchanged - use remote
706 711 if n1 == n2: # optimization: keep local content
707 712 actions[f] = ('e', (fl2,), "update permissions")
708 713 else:
709 714 actions[f] = ('g', (fl2,), "remote is newer")
710 715 elif nol and n2 == a: # remote only changed 'x'
711 716 actions[f] = ('e', (fl2,), "update permissions")
712 717 elif nol and n1 == a: # local only changed 'x'
713 718 actions[f] = ('g', (fl1,), "remote is newer")
714 719 else: # both changed something
715 720 actions[f] = ('m', (f, f, f, False, pa.node()),
716 721 "versions differ")
717 722 elif n1: # file exists only on local side
718 723 if f in copied:
719 724 pass # we'll deal with it on m2 side
720 725 elif f in movewithdir: # directory rename, move local
721 726 f2 = movewithdir[f]
722 727 if f2 in m2:
723 728 actions[f2] = ('m', (f, f2, None, True, pa.node()),
724 729 "remote directory rename, both created")
725 730 else:
726 731 actions[f2] = ('dm', (f, fl1),
727 732 "remote directory rename - move from " + f)
728 733 elif f in copy:
729 734 f2 = copy[f]
730 735 actions[f] = ('m', (f, f2, f2, False, pa.node()),
731 736 "local copied/moved from " + f2)
732 737 elif f in ma: # clean, a different, no remote
733 738 if n1 != ma[f]:
734 739 if acceptremote:
735 740 actions[f] = ('r', None, "remote delete")
736 741 else:
737 742 actions[f] = ('cd', (f, None, f, False, pa.node()),
738 743 "prompt changed/deleted")
739 744 elif n1[20:] == 'a':
740 745 # This extra 'a' is added by working copy manifest to mark
741 746 # the file as locally added. We should forget it instead of
742 747 # deleting it.
743 748 actions[f] = ('f', None, "remote deleted")
744 749 else:
745 750 actions[f] = ('r', None, "other deleted")
746 751 elif n2: # file exists only on remote side
747 752 if f in copied:
748 753 pass # we'll deal with it on m1 side
749 754 elif f in movewithdir:
750 755 f2 = movewithdir[f]
751 756 if f2 in m1:
752 757 actions[f2] = ('m', (f2, f, None, False, pa.node()),
753 758 "local directory rename, both created")
754 759 else:
755 760 actions[f2] = ('dg', (f, fl2),
756 761 "local directory rename - get from " + f)
757 762 elif f in copy:
758 763 f2 = copy[f]
759 764 if f2 in m2:
760 765 actions[f] = ('m', (f2, f, f2, False, pa.node()),
761 766 "remote copied from " + f2)
762 767 else:
763 768 actions[f] = ('m', (f2, f, f2, True, pa.node()),
764 769 "remote moved from " + f2)
765 770 elif f not in ma:
766 771 # local unknown, remote created: the logic is described by the
767 772 # following table:
768 773 #
769 774 # force branchmerge different | action
770 775 # n * * | create
771 776 # y n * | create
772 777 # y y n | create
773 778 # y y y | merge
774 779 #
775 780 # Checking whether the files are different is expensive, so we
776 781 # don't do that when we can avoid it.
777 782 if not force:
778 783 actions[f] = ('c', (fl2,), "remote created")
779 784 elif not branchmerge:
780 785 actions[f] = ('c', (fl2,), "remote created")
781 786 else:
782 787 actions[f] = ('cm', (fl2, pa.node()),
783 788 "remote created, get or merge")
784 789 elif n2 != ma[f]:
785 790 if acceptremote:
786 791 actions[f] = ('c', (fl2,), "remote recreating")
787 792 else:
788 793 actions[f] = ('dc', (None, f, f, False, pa.node()),
789 794 "prompt deleted/changed")
790 795
791 796 return actions, diverge, renamedelete
792 797
793 798 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
794 799 """Resolves false conflicts where the nodeid changed but the content
795 800 remained the same."""
796 801
797 802 for f, (m, args, msg) in actions.items():
798 803 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
799 804 # local did change but ended up with same content
800 805 actions[f] = 'r', None, "prompt same"
801 806 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
802 807 # remote did change but ended up with same content
803 808 del actions[f] # don't get = keep local deleted
804 809
805 810 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
806 811 acceptremote, followcopies):
807 812 "Calculate the actions needed to merge mctx into wctx using ancestors"
808 813
809 814 if len(ancestors) == 1: # default
810 815 actions, diverge, renamedelete = manifestmerge(
811 816 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
812 817 acceptremote, followcopies)
813 818 _checkunknownfiles(repo, wctx, mctx, force, actions)
814 819
815 820 else: # only when merge.preferancestor=* - the default
816 821 repo.ui.note(
817 822 _("note: merging %s and %s using bids from ancestors %s\n") %
818 823 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
819 824
820 825 # Call for bids
821 826 fbids = {} # mapping filename to bids (action method to list af actions)
822 827 diverge, renamedelete = None, None
823 828 for ancestor in ancestors:
824 829 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
825 830 actions, diverge1, renamedelete1 = manifestmerge(
826 831 repo, wctx, mctx, ancestor, branchmerge, force, partial,
827 832 acceptremote, followcopies)
828 833 _checkunknownfiles(repo, wctx, mctx, force, actions)
829 834
830 835 # Track the shortest set of warning on the theory that bid
831 836 # merge will correctly incorporate more information
832 837 if diverge is None or len(diverge1) < len(diverge):
833 838 diverge = diverge1
834 839 if renamedelete is None or len(renamedelete) < len(renamedelete1):
835 840 renamedelete = renamedelete1
836 841
837 842 for f, a in sorted(actions.iteritems()):
838 843 m, args, msg = a
839 844 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
840 845 if f in fbids:
841 846 d = fbids[f]
842 847 if m in d:
843 848 d[m].append(a)
844 849 else:
845 850 d[m] = [a]
846 851 else:
847 852 fbids[f] = {m: [a]}
848 853
849 854 # Pick the best bid for each file
850 855 repo.ui.note(_('\nauction for merging merge bids\n'))
851 856 actions = {}
852 857 for f, bids in sorted(fbids.items()):
853 858 # bids is a mapping from action method to list af actions
854 859 # Consensus?
855 860 if len(bids) == 1: # all bids are the same kind of method
856 861 m, l = bids.items()[0]
857 862 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
858 863 repo.ui.note(" %s: consensus for %s\n" % (f, m))
859 864 actions[f] = l[0]
860 865 continue
861 866 # If keep is an option, just do it.
862 867 if 'k' in bids:
863 868 repo.ui.note(" %s: picking 'keep' action\n" % f)
864 869 actions[f] = bids['k'][0]
865 870 continue
866 871 # If there are gets and they all agree [how could they not?], do it.
867 872 if 'g' in bids:
868 873 ga0 = bids['g'][0]
869 874 if all(a == ga0 for a in bids['g'][1:]):
870 875 repo.ui.note(" %s: picking 'get' action\n" % f)
871 876 actions[f] = ga0
872 877 continue
873 878 # TODO: Consider other simple actions such as mode changes
874 879 # Handle inefficient democrazy.
875 880 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
876 881 for m, l in sorted(bids.items()):
877 882 for _f, args, msg in l:
878 883 repo.ui.note(' %s -> %s\n' % (msg, m))
879 884 # Pick random action. TODO: Instead, prompt user when resolving
880 885 m, l = bids.items()[0]
881 886 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
882 887 (f, m))
883 888 actions[f] = l[0]
884 889 continue
885 890 repo.ui.note(_('end of auction\n\n'))
886 891
887 892 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
888 893
889 894 if wctx.rev() is None:
890 895 fractions = _forgetremoved(wctx, mctx, branchmerge)
891 896 actions.update(fractions)
892 897
893 898 return actions, diverge, renamedelete
894 899
895 900 def batchremove(repo, actions):
896 901 """apply removes to the working directory
897 902
898 903 yields tuples for progress updates
899 904 """
900 905 verbose = repo.ui.verbose
901 906 unlink = util.unlinkpath
902 907 wjoin = repo.wjoin
903 908 audit = repo.wvfs.audit
904 909 i = 0
905 910 for f, args, msg in actions:
906 911 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
907 912 if verbose:
908 913 repo.ui.note(_("removing %s\n") % f)
909 914 audit(f)
910 915 try:
911 916 unlink(wjoin(f), ignoremissing=True)
912 917 except OSError as inst:
913 918 repo.ui.warn(_("update failed to remove %s: %s!\n") %
914 919 (f, inst.strerror))
915 920 if i == 100:
916 921 yield i, f
917 922 i = 0
918 923 i += 1
919 924 if i > 0:
920 925 yield i, f
921 926
922 927 def batchget(repo, mctx, actions):
923 928 """apply gets to the working directory
924 929
925 930 mctx is the context to get from
926 931
927 932 yields tuples for progress updates
928 933 """
929 934 verbose = repo.ui.verbose
930 935 fctx = mctx.filectx
931 936 wwrite = repo.wwrite
932 937 i = 0
933 938 for f, args, msg in actions:
934 939 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
935 940 if verbose:
936 941 repo.ui.note(_("getting %s\n") % f)
937 942 wwrite(f, fctx(f).data(), args[0])
938 943 if i == 100:
939 944 yield i, f
940 945 i = 0
941 946 i += 1
942 947 if i > 0:
943 948 yield i, f
944 949
945 950 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
946 951 """apply the merge action list to the working directory
947 952
948 953 wctx is the working copy context
949 954 mctx is the context to be merged into the working copy
950 955
951 956 Return a tuple of counts (updated, merged, removed, unresolved) that
952 957 describes how many files were affected by the update.
953 958 """
954 959
955 960 updated, merged, removed = 0, 0, 0
956 961 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
957 962 moves = []
958 963 for m, l in actions.items():
959 964 l.sort()
960 965
961 966 # prescan for merges
962 967 for f, args, msg in actions['m']:
963 968 f1, f2, fa, move, anc = args
964 969 if f == '.hgsubstate': # merged internally
965 970 continue
966 971 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
967 972 fcl = wctx[f1]
968 973 fco = mctx[f2]
969 974 actx = repo[anc]
970 975 if fa in actx:
971 976 fca = actx[fa]
972 977 else:
973 978 fca = repo.filectx(f1, fileid=nullrev)
974 979 ms.add(fcl, fco, fca, f)
975 980 if f1 != f and move:
976 981 moves.append(f1)
977 982
978 983 audit = repo.wvfs.audit
979 984 _updating = _('updating')
980 985 _files = _('files')
981 986 progress = repo.ui.progress
982 987
983 988 # remove renamed files after safely stored
984 989 for f in moves:
985 990 if os.path.lexists(repo.wjoin(f)):
986 991 repo.ui.debug("removing %s\n" % f)
987 992 audit(f)
988 993 util.unlinkpath(repo.wjoin(f))
989 994
990 995 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
991 996
992 997 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
993 998 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
994 999
995 1000 # remove in parallel (must come first)
996 1001 z = 0
997 1002 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
998 1003 for i, item in prog:
999 1004 z += i
1000 1005 progress(_updating, z, item=item, total=numupdates, unit=_files)
1001 1006 removed = len(actions['r'])
1002 1007
1003 1008 # get in parallel
1004 1009 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1005 1010 for i, item in prog:
1006 1011 z += i
1007 1012 progress(_updating, z, item=item, total=numupdates, unit=_files)
1008 1013 updated = len(actions['g'])
1009 1014
1010 1015 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1011 1016 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1012 1017
1013 1018 # forget (manifest only, just log it) (must come first)
1014 1019 for f, args, msg in actions['f']:
1015 1020 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1016 1021 z += 1
1017 1022 progress(_updating, z, item=f, total=numupdates, unit=_files)
1018 1023
1019 1024 # re-add (manifest only, just log it)
1020 1025 for f, args, msg in actions['a']:
1021 1026 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1022 1027 z += 1
1023 1028 progress(_updating, z, item=f, total=numupdates, unit=_files)
1024 1029
1025 1030 # keep (noop, just log it)
1026 1031 for f, args, msg in actions['k']:
1027 1032 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1028 1033 # no progress
1029 1034
1030 1035 # directory rename, move local
1031 1036 for f, args, msg in actions['dm']:
1032 1037 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1033 1038 z += 1
1034 1039 progress(_updating, z, item=f, total=numupdates, unit=_files)
1035 1040 f0, flags = args
1036 1041 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1037 1042 audit(f)
1038 1043 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1039 1044 util.unlinkpath(repo.wjoin(f0))
1040 1045 updated += 1
1041 1046
1042 1047 # local directory rename, get
1043 1048 for f, args, msg in actions['dg']:
1044 1049 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1045 1050 z += 1
1046 1051 progress(_updating, z, item=f, total=numupdates, unit=_files)
1047 1052 f0, flags = args
1048 1053 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1049 1054 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1050 1055 updated += 1
1051 1056
1052 1057 # exec
1053 1058 for f, args, msg in actions['e']:
1054 1059 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1055 1060 z += 1
1056 1061 progress(_updating, z, item=f, total=numupdates, unit=_files)
1057 1062 flags, = args
1058 1063 audit(f)
1059 1064 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1060 1065 updated += 1
1061 1066
1062 1067 mergeactions = actions['m']
1063 1068 # the ordering is important here -- ms.mergedriver will raise if the merge
1064 1069 # driver has changed, and we want to be able to bypass it when overwrite is
1065 1070 # True
1066 1071 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1067 1072
1068 1073 if usemergedriver:
1069 1074 ms.commit()
1070 1075 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1071 1076 # the driver might leave some files unresolved
1072 1077 unresolvedf = set(ms.unresolved())
1073 1078 if not proceed:
1074 1079 # XXX setting unresolved to at least 1 is a hack to make sure we
1075 1080 # error out
1076 1081 return updated, merged, removed, max(len(unresolvedf), 1)
1077 1082 newactions = []
1078 1083 for f, args, msg in mergeactions:
1079 1084 if f in unresolvedf:
1080 1085 newactions.append((f, args, msg))
1081 1086 mergeactions = newactions
1082 1087
1083 1088 # premerge
1084 1089 tocomplete = []
1085 1090 for f, args, msg in mergeactions:
1086 1091 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1087 1092 z += 1
1088 1093 progress(_updating, z, item=f, total=numupdates, unit=_files)
1089 1094 if f == '.hgsubstate': # subrepo states need updating
1090 1095 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1091 1096 overwrite)
1092 1097 continue
1093 1098 audit(f)
1094 1099 complete, r = ms.preresolve(f, wctx, labels=labels)
1095 1100 if not complete:
1096 1101 numupdates += 1
1097 1102 tocomplete.append((f, args, msg))
1098 1103
1099 1104 # merge
1100 1105 for f, args, msg in tocomplete:
1101 1106 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1102 1107 z += 1
1103 1108 progress(_updating, z, item=f, total=numupdates, unit=_files)
1104 1109 ms.resolve(f, wctx, labels=labels)
1105 1110
1106 1111 ms.commit()
1107 1112
1108 1113 unresolved = ms.unresolvedcount()
1109 1114
1110 1115 if usemergedriver and not unresolved and ms.mdstate() != 's':
1111 1116 if not driverconclude(repo, ms, wctx, labels=labels):
1112 1117 # XXX setting unresolved to at least 1 is a hack to make sure we
1113 1118 # error out
1114 1119 unresolved = max(unresolved, 1)
1115 1120
1116 1121 ms.commit()
1117 1122
1118 1123 msupdated, msmerged, msremoved = ms.counts()
1119 1124 updated += msupdated
1120 1125 merged += msmerged
1121 1126 removed += msremoved
1122 1127
1123 1128 extraactions = ms.actions()
1124 1129 for a in 'rag':
1125 1130 actions[a].extend(extraactions[a])
1126 1131
1127 1132 progress(_updating, None, total=numupdates, unit=_files)
1128 1133
1129 1134 return updated, merged, removed, unresolved
1130 1135
1131 1136 def recordupdates(repo, actions, branchmerge):
1132 1137 "record merge actions to the dirstate"
1133 1138 # remove (must come first)
1134 1139 for f, args, msg in actions.get('r', []):
1135 1140 if branchmerge:
1136 1141 repo.dirstate.remove(f)
1137 1142 else:
1138 1143 repo.dirstate.drop(f)
1139 1144
1140 1145 # forget (must come first)
1141 1146 for f, args, msg in actions.get('f', []):
1142 1147 repo.dirstate.drop(f)
1143 1148
1144 1149 # re-add
1145 1150 for f, args, msg in actions.get('a', []):
1146 1151 if not branchmerge:
1147 1152 repo.dirstate.add(f)
1148 1153
1149 1154 # exec change
1150 1155 for f, args, msg in actions.get('e', []):
1151 1156 repo.dirstate.normallookup(f)
1152 1157
1153 1158 # keep
1154 1159 for f, args, msg in actions.get('k', []):
1155 1160 pass
1156 1161
1157 1162 # get
1158 1163 for f, args, msg in actions.get('g', []):
1159 1164 if branchmerge:
1160 1165 repo.dirstate.otherparent(f)
1161 1166 else:
1162 1167 repo.dirstate.normal(f)
1163 1168
1164 1169 # merge
1165 1170 for f, args, msg in actions.get('m', []):
1166 1171 f1, f2, fa, move, anc = args
1167 1172 if branchmerge:
1168 1173 # We've done a branch merge, mark this file as merged
1169 1174 # so that we properly record the merger later
1170 1175 repo.dirstate.merge(f)
1171 1176 if f1 != f2: # copy/rename
1172 1177 if move:
1173 1178 repo.dirstate.remove(f1)
1174 1179 if f1 != f:
1175 1180 repo.dirstate.copy(f1, f)
1176 1181 else:
1177 1182 repo.dirstate.copy(f2, f)
1178 1183 else:
1179 1184 # We've update-merged a locally modified file, so
1180 1185 # we set the dirstate to emulate a normal checkout
1181 1186 # of that file some time in the past. Thus our
1182 1187 # merge will appear as a normal local file
1183 1188 # modification.
1184 1189 if f2 == f: # file not locally copied/moved
1185 1190 repo.dirstate.normallookup(f)
1186 1191 if move:
1187 1192 repo.dirstate.drop(f1)
1188 1193
1189 1194 # directory rename, move local
1190 1195 for f, args, msg in actions.get('dm', []):
1191 1196 f0, flag = args
1192 1197 if branchmerge:
1193 1198 repo.dirstate.add(f)
1194 1199 repo.dirstate.remove(f0)
1195 1200 repo.dirstate.copy(f0, f)
1196 1201 else:
1197 1202 repo.dirstate.normal(f)
1198 1203 repo.dirstate.drop(f0)
1199 1204
1200 1205 # directory rename, get
1201 1206 for f, args, msg in actions.get('dg', []):
1202 1207 f0, flag = args
1203 1208 if branchmerge:
1204 1209 repo.dirstate.add(f)
1205 1210 repo.dirstate.copy(f0, f)
1206 1211 else:
1207 1212 repo.dirstate.normal(f)
1208 1213
1209 1214 def update(repo, node, branchmerge, force, partial, ancestor=None,
1210 1215 mergeancestor=False, labels=None):
1211 1216 """
1212 1217 Perform a merge between the working directory and the given node
1213 1218
1214 1219 node = the node to update to, or None if unspecified
1215 1220 branchmerge = whether to merge between branches
1216 1221 force = whether to force branch merging or file overwriting
1217 1222 partial = a function to filter file lists (dirstate not updated)
1218 1223 mergeancestor = whether it is merging with an ancestor. If true,
1219 1224 we should accept the incoming changes for any prompts that occur.
1220 1225 If false, merging with an ancestor (fast-forward) is only allowed
1221 1226 between different named branches. This flag is used by rebase extension
1222 1227 as a temporary fix and should be avoided in general.
1223 1228
1224 1229 The table below shows all the behaviors of the update command
1225 1230 given the -c and -C or no options, whether the working directory
1226 1231 is dirty, whether a revision is specified, and the relationship of
1227 1232 the parent rev to the target rev (linear, on the same named
1228 1233 branch, or on another named branch).
1229 1234
1230 1235 This logic is tested by test-update-branches.t.
1231 1236
1232 1237 -c -C dirty rev | linear same cross
1233 1238 n n n n | ok (1) x
1234 1239 n n n y | ok ok ok
1235 1240 n n y n | merge (2) (2)
1236 1241 n n y y | merge (3) (3)
1237 1242 n y * * | discard discard discard
1238 1243 y n y * | (4) (4) (4)
1239 1244 y n n * | ok ok ok
1240 1245 y y * * | (5) (5) (5)
1241 1246
1242 1247 x = can't happen
1243 1248 * = don't-care
1244 1249 1 = abort: not a linear update (merge or update --check to force update)
1245 1250 2 = abort: uncommitted changes (commit and merge, or update --clean to
1246 1251 discard changes)
1247 1252 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1248 1253 4 = abort: uncommitted changes (checked in commands.py)
1249 1254 5 = incompatible options (checked in commands.py)
1250 1255
1251 1256 Return the same tuple as applyupdates().
1252 1257 """
1253 1258
1254 1259 onode = node
1255 1260 wlock = repo.wlock()
1256 1261 try:
1257 1262 wc = repo[None]
1258 1263 pl = wc.parents()
1259 1264 p1 = pl[0]
1260 1265 pas = [None]
1261 1266 if ancestor is not None:
1262 1267 pas = [repo[ancestor]]
1263 1268
1264 1269 if node is None:
1265 1270 if (repo.ui.configbool('devel', 'all-warnings')
1266 1271 or repo.ui.configbool('devel', 'oldapi')):
1267 1272 repo.ui.develwarn('update with no target')
1268 1273 rev, _mark, _act = destutil.destupdate(repo)
1269 1274 node = repo[rev].node()
1270 1275
1271 1276 overwrite = force and not branchmerge
1272 1277
1273 1278 p2 = repo[node]
1274 1279 if pas[0] is None:
1275 1280 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1276 1281 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1277 1282 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1278 1283 else:
1279 1284 pas = [p1.ancestor(p2, warn=branchmerge)]
1280 1285
1281 1286 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1282 1287
1283 1288 ### check phase
1284 1289 if not overwrite and len(pl) > 1:
1285 1290 raise error.Abort(_("outstanding uncommitted merge"))
1286 1291 if branchmerge:
1287 1292 if pas == [p2]:
1288 1293 raise error.Abort(_("merging with a working directory ancestor"
1289 1294 " has no effect"))
1290 1295 elif pas == [p1]:
1291 1296 if not mergeancestor and p1.branch() == p2.branch():
1292 1297 raise error.Abort(_("nothing to merge"),
1293 1298 hint=_("use 'hg update' "
1294 1299 "or check 'hg heads'"))
1295 1300 if not force and (wc.files() or wc.deleted()):
1296 1301 raise error.Abort(_("uncommitted changes"),
1297 1302 hint=_("use 'hg status' to list changes"))
1298 1303 for s in sorted(wc.substate):
1299 1304 wc.sub(s).bailifchanged()
1300 1305
1301 1306 elif not overwrite:
1302 1307 if p1 == p2: # no-op update
1303 1308 # call the hooks and exit early
1304 1309 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1305 1310 repo.hook('update', parent1=xp2, parent2='', error=0)
1306 1311 return 0, 0, 0, 0
1307 1312
1308 1313 if pas not in ([p1], [p2]): # nonlinear
1309 1314 dirty = wc.dirty(missing=True)
1310 1315 if dirty or onode is None:
1311 1316 # Branching is a bit strange to ensure we do the minimal
1312 1317 # amount of call to obsolete.background.
1313 1318 foreground = obsolete.foreground(repo, [p1.node()])
1314 1319 # note: the <node> variable contains a random identifier
1315 1320 if repo[node].node() in foreground:
1316 1321 pas = [p1] # allow updating to successors
1317 1322 elif dirty:
1318 1323 msg = _("uncommitted changes")
1319 1324 if onode is None:
1320 1325 hint = _("commit and merge, or update --clean to"
1321 1326 " discard changes")
1322 1327 else:
1323 1328 hint = _("commit or update --clean to discard"
1324 1329 " changes")
1325 1330 raise error.Abort(msg, hint=hint)
1326 1331 else: # node is none
1327 1332 msg = _("not a linear update")
1328 1333 hint = _("merge or update --check to force update")
1329 1334 raise error.Abort(msg, hint=hint)
1330 1335 else:
1331 1336 # Allow jumping branches if clean and specific rev given
1332 1337 pas = [p1]
1333 1338
1334 1339 # deprecated config: merge.followcopies
1335 1340 followcopies = False
1336 1341 if overwrite:
1337 1342 pas = [wc]
1338 1343 elif pas == [p2]: # backwards
1339 1344 pas = [wc.p1()]
1340 1345 elif not branchmerge and not wc.dirty(missing=True):
1341 1346 pass
1342 1347 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1343 1348 followcopies = True
1344 1349
1345 1350 ### calculate phase
1346 1351 actionbyfile, diverge, renamedelete = calculateupdates(
1347 1352 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1348 1353 followcopies)
1349 1354 # Convert to dictionary-of-lists format
1350 1355 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1351 1356 for f, (m, args, msg) in actionbyfile.iteritems():
1352 1357 if m not in actions:
1353 1358 actions[m] = []
1354 1359 actions[m].append((f, args, msg))
1355 1360
1356 1361 if not util.checkcase(repo.path):
1357 1362 # check collision between files only in p2 for clean update
1358 1363 if (not branchmerge and
1359 1364 (force or not wc.dirty(missing=True, branch=False))):
1360 1365 _checkcollision(repo, p2.manifest(), None)
1361 1366 else:
1362 1367 _checkcollision(repo, wc.manifest(), actions)
1363 1368
1364 1369 # Prompt and create actions. TODO: Move this towards resolve phase.
1365 1370 for f, args, msg in sorted(actions['cd']):
1366 1371 if repo.ui.promptchoice(
1367 1372 _("local changed %s which remote deleted\n"
1368 1373 "use (c)hanged version or (d)elete?"
1369 1374 "$$ &Changed $$ &Delete") % f, 0):
1370 1375 actions['r'].append((f, None, "prompt delete"))
1371 1376 else:
1372 1377 actions['a'].append((f, None, "prompt keep"))
1373 1378
1374 1379 for f, args, msg in sorted(actions['dc']):
1375 1380 f1, f2, fa, move, anc = args
1376 1381 flags = p2[f2].flags()
1377 1382 if repo.ui.promptchoice(
1378 1383 _("remote changed %s which local deleted\n"
1379 1384 "use (c)hanged version or leave (d)eleted?"
1380 1385 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1381 1386 actions['g'].append((f, (flags,), "prompt recreating"))
1382 1387
1383 1388 # divergent renames
1384 1389 for f, fl in sorted(diverge.iteritems()):
1385 1390 repo.ui.warn(_("note: possible conflict - %s was renamed "
1386 1391 "multiple times to:\n") % f)
1387 1392 for nf in fl:
1388 1393 repo.ui.warn(" %s\n" % nf)
1389 1394
1390 1395 # rename and delete
1391 1396 for f, fl in sorted(renamedelete.iteritems()):
1392 1397 repo.ui.warn(_("note: possible conflict - %s was deleted "
1393 1398 "and renamed to:\n") % f)
1394 1399 for nf in fl:
1395 1400 repo.ui.warn(" %s\n" % nf)
1396 1401
1397 1402 ### apply phase
1398 1403 if not branchmerge: # just jump to the new rev
1399 1404 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1400 1405 if not partial:
1401 1406 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1402 1407 # note that we're in the middle of an update
1403 1408 repo.vfs.write('updatestate', p2.hex())
1404 1409
1405 1410 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1406 1411
1407 1412 if not partial:
1408 1413 repo.dirstate.beginparentchange()
1409 1414 repo.setparents(fp1, fp2)
1410 1415 recordupdates(repo, actions, branchmerge)
1411 1416 # update completed, clear state
1412 1417 util.unlink(repo.join('updatestate'))
1413 1418
1414 1419 if not branchmerge:
1415 1420 repo.dirstate.setbranch(p2.branch())
1416 1421 repo.dirstate.endparentchange()
1417 1422 finally:
1418 1423 wlock.release()
1419 1424
1420 1425 if not partial:
1421 1426 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1422 1427 return stats
1423 1428
1424 1429 def graft(repo, ctx, pctx, labels):
1425 1430 """Do a graft-like merge.
1426 1431
1427 1432 This is a merge where the merge ancestor is chosen such that one
1428 1433 or more changesets are grafted onto the current changeset. In
1429 1434 addition to the merge, this fixes up the dirstate to include only
1430 1435 a single parent and tries to duplicate any renames/copies
1431 1436 appropriately.
1432 1437
1433 1438 ctx - changeset to rebase
1434 1439 pctx - merge base, usually ctx.p1()
1435 1440 labels - merge labels eg ['local', 'graft']
1436 1441
1437 1442 """
1438 1443 # If we're grafting a descendant onto an ancestor, be sure to pass
1439 1444 # mergeancestor=True to update. This does two things: 1) allows the merge if
1440 1445 # the destination is the same as the parent of the ctx (so we can use graft
1441 1446 # to copy commits), and 2) informs update that the incoming changes are
1442 1447 # newer than the destination so it doesn't prompt about "remote changed foo
1443 1448 # which local deleted".
1444 1449 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1445 1450
1446 1451 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1447 1452 mergeancestor=mergeancestor, labels=labels)
1448 1453
1449 1454 # drop the second merge parent
1450 1455 repo.dirstate.beginparentchange()
1451 1456 repo.setparents(repo['.'].node(), nullid)
1452 1457 repo.dirstate.write(repo.currenttransaction())
1453 1458 # fix up dirstate for copies and renames
1454 1459 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1455 1460 repo.dirstate.endparentchange()
1456 1461 return stats
General Comments 0
You need to be logged in to leave comments. Login now