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