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