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