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