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