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