##// END OF EJS Templates
merge: check created file dirs for path conflicts only once (issue5716)...
Mark Thomas -
r35182:a92b9f8e 4.4.2 stable
parent child Browse files
Show More
@@ -1,2060 +1,2065 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 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 666 def __init__(self):
667 667 # A set of paths known to be good. This prevents repeated checking of
668 668 # dirs. It will be updated with any new dirs that are checked and found
669 669 # to be safe.
670 670 self._unknowndircache = set()
671 671
672 672 # A set of paths that are known to be absent. This prevents repeated
673 673 # checking of subdirectories that are known not to exist. It will be
674 674 # updated with any new dirs that are checked and found to be absent.
675 675 self._missingdircache = set()
676 676
677 677 def __call__(self, repo, f):
678 678 # Check for path prefixes that exist as unknown files.
679 679 for p in reversed(list(util.finddirs(f))):
680 680 if p in self._missingdircache:
681 681 return
682 682 if p in self._unknowndircache:
683 683 continue
684 684 if repo.wvfs.audit.check(p):
685 685 if (repo.wvfs.isfileorlink(p)
686 686 and repo.dirstate.normalize(p) not in repo.dirstate):
687 687 return p
688 688 if not repo.wvfs.lexists(p):
689 689 self._missingdircache.add(p)
690 690 return
691 691 self._unknowndircache.add(p)
692 692
693 693 # Check if the file conflicts with a directory containing unknown files.
694 694 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
695 695 # Does the directory contain any files that are not in the dirstate?
696 696 for p, dirs, files in repo.wvfs.walk(f):
697 697 for fn in files:
698 698 relf = repo.dirstate.normalize(repo.wvfs.reljoin(p, fn))
699 699 if relf not in repo.dirstate:
700 700 return f
701 701 return None
702 702
703 703 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
704 704 """
705 705 Considers any actions that care about the presence of conflicting unknown
706 706 files. For some actions, the result is to abort; for others, it is to
707 707 choose a different action.
708 708 """
709 709 fileconflicts = set()
710 710 pathconflicts = set()
711 711 warnconflicts = set()
712 712 abortconflicts = set()
713 713 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
714 714 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
715 715 pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts')
716 716 if not force:
717 717 def collectconflicts(conflicts, config):
718 718 if config == 'abort':
719 719 abortconflicts.update(conflicts)
720 720 elif config == 'warn':
721 721 warnconflicts.update(conflicts)
722 722
723 723 checkunknowndirs = _unknowndirschecker()
724 724 for f, (m, args, msg) in actions.iteritems():
725 725 if m in ('c', 'dc'):
726 726 if _checkunknownfile(repo, wctx, mctx, f):
727 727 fileconflicts.add(f)
728 728 elif pathconfig and f not in wctx:
729 729 path = checkunknowndirs(repo, f)
730 730 if path is not None:
731 731 pathconflicts.add(path)
732 732 elif m == 'dg':
733 733 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
734 734 fileconflicts.add(f)
735 735
736 736 allconflicts = fileconflicts | pathconflicts
737 737 ignoredconflicts = set([c for c in allconflicts
738 738 if repo.dirstate._ignore(c)])
739 739 unknownconflicts = allconflicts - ignoredconflicts
740 740 collectconflicts(ignoredconflicts, ignoredconfig)
741 741 collectconflicts(unknownconflicts, unknownconfig)
742 742 else:
743 743 for f, (m, args, msg) in actions.iteritems():
744 744 if m == 'cm':
745 745 fl2, anc = args
746 746 different = _checkunknownfile(repo, wctx, mctx, f)
747 747 if repo.dirstate._ignore(f):
748 748 config = ignoredconfig
749 749 else:
750 750 config = unknownconfig
751 751
752 752 # The behavior when force is True is described by this table:
753 753 # config different mergeforce | action backup
754 754 # * n * | get n
755 755 # * y y | merge -
756 756 # abort y n | merge - (1)
757 757 # warn y n | warn + get y
758 758 # ignore y n | get y
759 759 #
760 760 # (1) this is probably the wrong behavior here -- we should
761 761 # probably abort, but some actions like rebases currently
762 762 # don't like an abort happening in the middle of
763 763 # merge.update.
764 764 if not different:
765 765 actions[f] = ('g', (fl2, False), "remote created")
766 766 elif mergeforce or config == 'abort':
767 767 actions[f] = ('m', (f, f, None, False, anc),
768 768 "remote differs from untracked local")
769 769 elif config == 'abort':
770 770 abortconflicts.add(f)
771 771 else:
772 772 if config == 'warn':
773 773 warnconflicts.add(f)
774 774 actions[f] = ('g', (fl2, True), "remote created")
775 775
776 776 for f in sorted(abortconflicts):
777 777 warn = repo.ui.warn
778 778 if f in pathconflicts:
779 779 if repo.wvfs.isfileorlink(f):
780 780 warn(_("%s: untracked file conflicts with directory\n") % f)
781 781 else:
782 782 warn(_("%s: untracked directory conflicts with file\n") % f)
783 783 else:
784 784 warn(_("%s: untracked file differs\n") % f)
785 785 if abortconflicts:
786 786 raise error.Abort(_("untracked files in working directory "
787 787 "differ from files in requested revision"))
788 788
789 789 for f in sorted(warnconflicts):
790 790 if repo.wvfs.isfileorlink(f):
791 791 repo.ui.warn(_("%s: replacing untracked file\n") % f)
792 792 else:
793 793 repo.ui.warn(_("%s: replacing untracked files in directory\n") % f)
794 794
795 795 for f, (m, args, msg) in actions.iteritems():
796 796 if m == 'c':
797 797 backup = (f in fileconflicts or f in pathconflicts or
798 798 any(p in pathconflicts for p in util.finddirs(f)))
799 799 flags, = args
800 800 actions[f] = ('g', (flags, backup), msg)
801 801
802 802 def _forgetremoved(wctx, mctx, branchmerge):
803 803 """
804 804 Forget removed files
805 805
806 806 If we're jumping between revisions (as opposed to merging), and if
807 807 neither the working directory nor the target rev has the file,
808 808 then we need to remove it from the dirstate, to prevent the
809 809 dirstate from listing the file when it is no longer in the
810 810 manifest.
811 811
812 812 If we're merging, and the other revision has removed a file
813 813 that is not present in the working directory, we need to mark it
814 814 as removed.
815 815 """
816 816
817 817 actions = {}
818 818 m = 'f'
819 819 if branchmerge:
820 820 m = 'r'
821 821 for f in wctx.deleted():
822 822 if f not in mctx:
823 823 actions[f] = m, None, "forget deleted"
824 824
825 825 if not branchmerge:
826 826 for f in wctx.removed():
827 827 if f not in mctx:
828 828 actions[f] = 'f', None, "forget removed"
829 829
830 830 return actions
831 831
832 832 def _checkcollision(repo, wmf, actions):
833 833 # build provisional merged manifest up
834 834 pmmf = set(wmf)
835 835
836 836 if actions:
837 837 # k, dr, e and rd are no-op
838 838 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
839 839 for f, args, msg in actions[m]:
840 840 pmmf.add(f)
841 841 for f, args, msg in actions['r']:
842 842 pmmf.discard(f)
843 843 for f, args, msg in actions['dm']:
844 844 f2, flags = args
845 845 pmmf.discard(f2)
846 846 pmmf.add(f)
847 847 for f, args, msg in actions['dg']:
848 848 pmmf.add(f)
849 849 for f, args, msg in actions['m']:
850 850 f1, f2, fa, move, anc = args
851 851 if move:
852 852 pmmf.discard(f1)
853 853 pmmf.add(f)
854 854
855 855 # check case-folding collision in provisional merged manifest
856 856 foldmap = {}
857 857 for f in pmmf:
858 858 fold = util.normcase(f)
859 859 if fold in foldmap:
860 860 raise error.Abort(_("case-folding collision between %s and %s")
861 861 % (f, foldmap[fold]))
862 862 foldmap[fold] = f
863 863
864 864 # check case-folding of directories
865 865 foldprefix = unfoldprefix = lastfull = ''
866 866 for fold, f in sorted(foldmap.items()):
867 867 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
868 868 # the folded prefix matches but actual casing is different
869 869 raise error.Abort(_("case-folding collision between "
870 870 "%s and directory of %s") % (lastfull, f))
871 871 foldprefix = fold + '/'
872 872 unfoldprefix = f + '/'
873 873 lastfull = f
874 874
875 875 def driverpreprocess(repo, ms, wctx, labels=None):
876 876 """run the preprocess step of the merge driver, if any
877 877
878 878 This is currently not implemented -- it's an extension point."""
879 879 return True
880 880
881 881 def driverconclude(repo, ms, wctx, labels=None):
882 882 """run the conclude step of the merge driver, if any
883 883
884 884 This is currently not implemented -- it's an extension point."""
885 885 return True
886 886
887 887 def _filesindirs(repo, manifest, dirs):
888 888 """
889 889 Generator that yields pairs of all the files in the manifest that are found
890 890 inside the directories listed in dirs, and which directory they are found
891 891 in.
892 892 """
893 893 for f in manifest:
894 894 for p in util.finddirs(f):
895 895 if p in dirs:
896 896 yield f, p
897 897 break
898 898
899 899 def checkpathconflicts(repo, wctx, mctx, actions):
900 900 """
901 901 Check if any actions introduce path conflicts in the repository, updating
902 902 actions to record or handle the path conflict accordingly.
903 903 """
904 904 mf = wctx.manifest()
905 905
906 906 # The set of local files that conflict with a remote directory.
907 907 localconflicts = set()
908 908
909 909 # The set of directories that conflict with a remote file, and so may cause
910 910 # conflicts if they still contain any files after the merge.
911 911 remoteconflicts = set()
912 912
913 913 # The set of directories that appear as both a file and a directory in the
914 914 # remote manifest. These indicate an invalid remote manifest, which
915 915 # can't be updated to cleanly.
916 916 invalidconflicts = set()
917 917
918 # The set of directories that contain files that are being created.
919 createdfiledirs = set()
920
918 921 # The set of files deleted by all the actions.
919 922 deletedfiles = set()
920 923
921 924 for f, (m, args, msg) in actions.items():
922 925 if m in ('c', 'dc', 'm', 'cm'):
923 926 # This action may create a new local file.
927 createdfiledirs.update(util.finddirs(f))
924 928 if mf.hasdir(f):
925 929 # The file aliases a local directory. This might be ok if all
926 930 # the files in the local directory are being deleted. This
927 931 # will be checked once we know what all the deleted files are.
928 932 remoteconflicts.add(f)
929 for p in util.finddirs(f):
930 if p in mf:
931 if p in mctx:
932 # The file is in a directory which aliases both a local
933 # and a remote file. This is an internal inconsistency
934 # within the remote manifest.
935 invalidconflicts.add(p)
936 else:
937 # The file is in a directory which aliases a local file.
938 # We will need to rename the local file.
939 localconflicts.add(p)
940 if p in actions and actions[p][0] in ('c', 'dc', 'm', 'cm'):
941 # The file is in a directory which aliases a remote file.
942 # This is an internal inconsistency within the remote
943 # manifest.
944 invalidconflicts.add(p)
945
946 933 # Track the names of all deleted files.
947 934 if m == 'r':
948 935 deletedfiles.add(f)
949 936 if m == 'm':
950 937 f1, f2, fa, move, anc = args
951 938 if move:
952 939 deletedfiles.add(f1)
953 940 if m == 'dm':
954 941 f2, flags = args
955 942 deletedfiles.add(f2)
956 943
944 # Check all directories that contain created files for path conflicts.
945 for p in createdfiledirs:
946 if p in mf:
947 if p in mctx:
948 # A file is in a directory which aliases both a local
949 # and a remote file. This is an internal inconsistency
950 # within the remote manifest.
951 invalidconflicts.add(p)
952 else:
953 # A file is in a directory which aliases a local file.
954 # We will need to rename the local file.
955 localconflicts.add(p)
956 if p in actions and actions[p][0] in ('c', 'dc', 'm', 'cm'):
957 # The file is in a directory which aliases a remote file.
958 # This is an internal inconsistency within the remote
959 # manifest.
960 invalidconflicts.add(p)
961
957 962 # Rename all local conflicting files that have not been deleted.
958 963 for p in localconflicts:
959 964 if p not in deletedfiles:
960 965 ctxname = str(wctx).rstrip('+')
961 966 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
962 967 actions[pnew] = ('pr', (p,), "local path conflict")
963 968 actions[p] = ('p', (pnew, 'l'), "path conflict")
964 969
965 970 if remoteconflicts:
966 971 # Check if all files in the conflicting directories have been removed.
967 972 ctxname = str(mctx).rstrip('+')
968 973 for f, p in _filesindirs(repo, mf, remoteconflicts):
969 974 if f not in deletedfiles:
970 975 m, args, msg = actions[p]
971 976 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
972 977 if m in ('dc', 'm'):
973 978 # Action was merge, just update target.
974 979 actions[pnew] = (m, args, msg)
975 980 else:
976 981 # Action was create, change to renamed get action.
977 982 fl = args[0]
978 983 actions[pnew] = ('dg', (p, fl), "remote path conflict")
979 984 actions[p] = ('p', (pnew, 'r'), "path conflict")
980 985 remoteconflicts.remove(p)
981 986 break
982 987
983 988 if invalidconflicts:
984 989 for p in invalidconflicts:
985 990 repo.ui.warn(_("%s: is both a file and a directory\n") % p)
986 991 raise error.Abort(_("destination manifest contains path conflicts"))
987 992
988 993 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
989 994 acceptremote, followcopies, forcefulldiff=False):
990 995 """
991 996 Merge wctx and p2 with ancestor pa and generate merge action list
992 997
993 998 branchmerge and force are as passed in to update
994 999 matcher = matcher to filter file lists
995 1000 acceptremote = accept the incoming changes without prompting
996 1001 """
997 1002 if matcher is not None and matcher.always():
998 1003 matcher = None
999 1004
1000 1005 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
1001 1006
1002 1007 # manifests fetched in order are going to be faster, so prime the caches
1003 1008 [x.manifest() for x in
1004 1009 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
1005 1010
1006 1011 if followcopies:
1007 1012 ret = copies.mergecopies(repo, wctx, p2, pa)
1008 1013 copy, movewithdir, diverge, renamedelete, dirmove = ret
1009 1014
1010 1015 boolbm = pycompat.bytestr(bool(branchmerge))
1011 1016 boolf = pycompat.bytestr(bool(force))
1012 1017 boolm = pycompat.bytestr(bool(matcher))
1013 1018 repo.ui.note(_("resolving manifests\n"))
1014 1019 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
1015 1020 % (boolbm, boolf, boolm))
1016 1021 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
1017 1022
1018 1023 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
1019 1024 copied = set(copy.values())
1020 1025 copied.update(movewithdir.values())
1021 1026
1022 1027 if '.hgsubstate' in m1:
1023 1028 # check whether sub state is modified
1024 1029 if any(wctx.sub(s).dirty() for s in wctx.substate):
1025 1030 m1['.hgsubstate'] = modifiednodeid
1026 1031
1027 1032 # Don't use m2-vs-ma optimization if:
1028 1033 # - ma is the same as m1 or m2, which we're just going to diff again later
1029 1034 # - The caller specifically asks for a full diff, which is useful during bid
1030 1035 # merge.
1031 1036 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
1032 1037 # Identify which files are relevant to the merge, so we can limit the
1033 1038 # total m1-vs-m2 diff to just those files. This has significant
1034 1039 # performance benefits in large repositories.
1035 1040 relevantfiles = set(ma.diff(m2).keys())
1036 1041
1037 1042 # For copied and moved files, we need to add the source file too.
1038 1043 for copykey, copyvalue in copy.iteritems():
1039 1044 if copyvalue in relevantfiles:
1040 1045 relevantfiles.add(copykey)
1041 1046 for movedirkey in movewithdir:
1042 1047 relevantfiles.add(movedirkey)
1043 1048 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
1044 1049 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
1045 1050
1046 1051 diff = m1.diff(m2, match=matcher)
1047 1052
1048 1053 if matcher is None:
1049 1054 matcher = matchmod.always('', '')
1050 1055
1051 1056 actions = {}
1052 1057 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
1053 1058 if n1 and n2: # file exists on both local and remote side
1054 1059 if f not in ma:
1055 1060 fa = copy.get(f, None)
1056 1061 if fa is not None:
1057 1062 actions[f] = ('m', (f, f, fa, False, pa.node()),
1058 1063 "both renamed from " + fa)
1059 1064 else:
1060 1065 actions[f] = ('m', (f, f, None, False, pa.node()),
1061 1066 "both created")
1062 1067 else:
1063 1068 a = ma[f]
1064 1069 fla = ma.flags(f)
1065 1070 nol = 'l' not in fl1 + fl2 + fla
1066 1071 if n2 == a and fl2 == fla:
1067 1072 actions[f] = ('k', (), "remote unchanged")
1068 1073 elif n1 == a and fl1 == fla: # local unchanged - use remote
1069 1074 if n1 == n2: # optimization: keep local content
1070 1075 actions[f] = ('e', (fl2,), "update permissions")
1071 1076 else:
1072 1077 actions[f] = ('g', (fl2, False), "remote is newer")
1073 1078 elif nol and n2 == a: # remote only changed 'x'
1074 1079 actions[f] = ('e', (fl2,), "update permissions")
1075 1080 elif nol and n1 == a: # local only changed 'x'
1076 1081 actions[f] = ('g', (fl1, False), "remote is newer")
1077 1082 else: # both changed something
1078 1083 actions[f] = ('m', (f, f, f, False, pa.node()),
1079 1084 "versions differ")
1080 1085 elif n1: # file exists only on local side
1081 1086 if f in copied:
1082 1087 pass # we'll deal with it on m2 side
1083 1088 elif f in movewithdir: # directory rename, move local
1084 1089 f2 = movewithdir[f]
1085 1090 if f2 in m2:
1086 1091 actions[f2] = ('m', (f, f2, None, True, pa.node()),
1087 1092 "remote directory rename, both created")
1088 1093 else:
1089 1094 actions[f2] = ('dm', (f, fl1),
1090 1095 "remote directory rename - move from " + f)
1091 1096 elif f in copy:
1092 1097 f2 = copy[f]
1093 1098 actions[f] = ('m', (f, f2, f2, False, pa.node()),
1094 1099 "local copied/moved from " + f2)
1095 1100 elif f in ma: # clean, a different, no remote
1096 1101 if n1 != ma[f]:
1097 1102 if acceptremote:
1098 1103 actions[f] = ('r', None, "remote delete")
1099 1104 else:
1100 1105 actions[f] = ('cd', (f, None, f, False, pa.node()),
1101 1106 "prompt changed/deleted")
1102 1107 elif n1 == addednodeid:
1103 1108 # This extra 'a' is added by working copy manifest to mark
1104 1109 # the file as locally added. We should forget it instead of
1105 1110 # deleting it.
1106 1111 actions[f] = ('f', None, "remote deleted")
1107 1112 else:
1108 1113 actions[f] = ('r', None, "other deleted")
1109 1114 elif n2: # file exists only on remote side
1110 1115 if f in copied:
1111 1116 pass # we'll deal with it on m1 side
1112 1117 elif f in movewithdir:
1113 1118 f2 = movewithdir[f]
1114 1119 if f2 in m1:
1115 1120 actions[f2] = ('m', (f2, f, None, False, pa.node()),
1116 1121 "local directory rename, both created")
1117 1122 else:
1118 1123 actions[f2] = ('dg', (f, fl2),
1119 1124 "local directory rename - get from " + f)
1120 1125 elif f in copy:
1121 1126 f2 = copy[f]
1122 1127 if f2 in m2:
1123 1128 actions[f] = ('m', (f2, f, f2, False, pa.node()),
1124 1129 "remote copied from " + f2)
1125 1130 else:
1126 1131 actions[f] = ('m', (f2, f, f2, True, pa.node()),
1127 1132 "remote moved from " + f2)
1128 1133 elif f not in ma:
1129 1134 # local unknown, remote created: the logic is described by the
1130 1135 # following table:
1131 1136 #
1132 1137 # force branchmerge different | action
1133 1138 # n * * | create
1134 1139 # y n * | create
1135 1140 # y y n | create
1136 1141 # y y y | merge
1137 1142 #
1138 1143 # Checking whether the files are different is expensive, so we
1139 1144 # don't do that when we can avoid it.
1140 1145 if not force:
1141 1146 actions[f] = ('c', (fl2,), "remote created")
1142 1147 elif not branchmerge:
1143 1148 actions[f] = ('c', (fl2,), "remote created")
1144 1149 else:
1145 1150 actions[f] = ('cm', (fl2, pa.node()),
1146 1151 "remote created, get or merge")
1147 1152 elif n2 != ma[f]:
1148 1153 df = None
1149 1154 for d in dirmove:
1150 1155 if f.startswith(d):
1151 1156 # new file added in a directory that was moved
1152 1157 df = dirmove[d] + f[len(d):]
1153 1158 break
1154 1159 if df is not None and df in m1:
1155 1160 actions[df] = ('m', (df, f, f, False, pa.node()),
1156 1161 "local directory rename - respect move from " + f)
1157 1162 elif acceptremote:
1158 1163 actions[f] = ('c', (fl2,), "remote recreating")
1159 1164 else:
1160 1165 actions[f] = ('dc', (None, f, f, False, pa.node()),
1161 1166 "prompt deleted/changed")
1162 1167
1163 1168 if repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1164 1169 # If we are merging, look for path conflicts.
1165 1170 checkpathconflicts(repo, wctx, p2, actions)
1166 1171
1167 1172 return actions, diverge, renamedelete
1168 1173
1169 1174 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
1170 1175 """Resolves false conflicts where the nodeid changed but the content
1171 1176 remained the same."""
1172 1177
1173 1178 for f, (m, args, msg) in actions.items():
1174 1179 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
1175 1180 # local did change but ended up with same content
1176 1181 actions[f] = 'r', None, "prompt same"
1177 1182 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
1178 1183 # remote did change but ended up with same content
1179 1184 del actions[f] # don't get = keep local deleted
1180 1185
1181 1186 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
1182 1187 acceptremote, followcopies, matcher=None,
1183 1188 mergeforce=False):
1184 1189 """Calculate the actions needed to merge mctx into wctx using ancestors"""
1185 1190 # Avoid cycle.
1186 1191 from . import sparse
1187 1192
1188 1193 if len(ancestors) == 1: # default
1189 1194 actions, diverge, renamedelete = manifestmerge(
1190 1195 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
1191 1196 acceptremote, followcopies)
1192 1197 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1193 1198
1194 1199 else: # only when merge.preferancestor=* - the default
1195 1200 repo.ui.note(
1196 1201 _("note: merging %s and %s using bids from ancestors %s\n") %
1197 1202 (wctx, mctx, _(' and ').join(pycompat.bytestr(anc)
1198 1203 for anc in ancestors)))
1199 1204
1200 1205 # Call for bids
1201 1206 fbids = {} # mapping filename to bids (action method to list af actions)
1202 1207 diverge, renamedelete = None, None
1203 1208 for ancestor in ancestors:
1204 1209 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1205 1210 actions, diverge1, renamedelete1 = manifestmerge(
1206 1211 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1207 1212 acceptremote, followcopies, forcefulldiff=True)
1208 1213 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1209 1214
1210 1215 # Track the shortest set of warning on the theory that bid
1211 1216 # merge will correctly incorporate more information
1212 1217 if diverge is None or len(diverge1) < len(diverge):
1213 1218 diverge = diverge1
1214 1219 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1215 1220 renamedelete = renamedelete1
1216 1221
1217 1222 for f, a in sorted(actions.iteritems()):
1218 1223 m, args, msg = a
1219 1224 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1220 1225 if f in fbids:
1221 1226 d = fbids[f]
1222 1227 if m in d:
1223 1228 d[m].append(a)
1224 1229 else:
1225 1230 d[m] = [a]
1226 1231 else:
1227 1232 fbids[f] = {m: [a]}
1228 1233
1229 1234 # Pick the best bid for each file
1230 1235 repo.ui.note(_('\nauction for merging merge bids\n'))
1231 1236 actions = {}
1232 1237 dms = [] # filenames that have dm actions
1233 1238 for f, bids in sorted(fbids.items()):
1234 1239 # bids is a mapping from action method to list af actions
1235 1240 # Consensus?
1236 1241 if len(bids) == 1: # all bids are the same kind of method
1237 1242 m, l = list(bids.items())[0]
1238 1243 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1239 1244 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1240 1245 actions[f] = l[0]
1241 1246 if m == 'dm':
1242 1247 dms.append(f)
1243 1248 continue
1244 1249 # If keep is an option, just do it.
1245 1250 if 'k' in bids:
1246 1251 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1247 1252 actions[f] = bids['k'][0]
1248 1253 continue
1249 1254 # If there are gets and they all agree [how could they not?], do it.
1250 1255 if 'g' in bids:
1251 1256 ga0 = bids['g'][0]
1252 1257 if all(a == ga0 for a in bids['g'][1:]):
1253 1258 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1254 1259 actions[f] = ga0
1255 1260 continue
1256 1261 # TODO: Consider other simple actions such as mode changes
1257 1262 # Handle inefficient democrazy.
1258 1263 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1259 1264 for m, l in sorted(bids.items()):
1260 1265 for _f, args, msg in l:
1261 1266 repo.ui.note(' %s -> %s\n' % (msg, m))
1262 1267 # Pick random action. TODO: Instead, prompt user when resolving
1263 1268 m, l = list(bids.items())[0]
1264 1269 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1265 1270 (f, m))
1266 1271 actions[f] = l[0]
1267 1272 if m == 'dm':
1268 1273 dms.append(f)
1269 1274 continue
1270 1275 # Work around 'dm' that can cause multiple actions for the same file
1271 1276 for f in dms:
1272 1277 dm, (f0, flags), msg = actions[f]
1273 1278 assert dm == 'dm', dm
1274 1279 if f0 in actions and actions[f0][0] == 'r':
1275 1280 # We have one bid for removing a file and another for moving it.
1276 1281 # These two could be merged as first move and then delete ...
1277 1282 # but instead drop moving and just delete.
1278 1283 del actions[f]
1279 1284 repo.ui.note(_('end of auction\n\n'))
1280 1285
1281 1286 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1282 1287
1283 1288 if wctx.rev() is None:
1284 1289 fractions = _forgetremoved(wctx, mctx, branchmerge)
1285 1290 actions.update(fractions)
1286 1291
1287 1292 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1288 1293 actions)
1289 1294
1290 1295 return prunedactions, diverge, renamedelete
1291 1296
1292 1297 def _getcwd():
1293 1298 try:
1294 1299 return pycompat.getcwd()
1295 1300 except OSError as err:
1296 1301 if err.errno == errno.ENOENT:
1297 1302 return None
1298 1303 raise
1299 1304
1300 1305 def batchremove(repo, wctx, actions):
1301 1306 """apply removes to the working directory
1302 1307
1303 1308 yields tuples for progress updates
1304 1309 """
1305 1310 verbose = repo.ui.verbose
1306 1311 cwd = _getcwd()
1307 1312 i = 0
1308 1313 for f, args, msg in actions:
1309 1314 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1310 1315 if verbose:
1311 1316 repo.ui.note(_("removing %s\n") % f)
1312 1317 wctx[f].audit()
1313 1318 try:
1314 1319 wctx[f].remove(ignoremissing=True)
1315 1320 except OSError as inst:
1316 1321 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1317 1322 (f, inst.strerror))
1318 1323 if i == 100:
1319 1324 yield i, f
1320 1325 i = 0
1321 1326 i += 1
1322 1327 if i > 0:
1323 1328 yield i, f
1324 1329
1325 1330 if cwd and not _getcwd():
1326 1331 # cwd was removed in the course of removing files; print a helpful
1327 1332 # warning.
1328 1333 repo.ui.warn(_("current directory was removed\n"
1329 1334 "(consider changing to repo root: %s)\n") % repo.root)
1330 1335
1331 1336 # It's necessary to flush here in case we're inside a worker fork and will
1332 1337 # quit after this function.
1333 1338 wctx.flushall()
1334 1339
1335 1340 def batchget(repo, mctx, wctx, actions):
1336 1341 """apply gets to the working directory
1337 1342
1338 1343 mctx is the context to get from
1339 1344
1340 1345 yields tuples for progress updates
1341 1346 """
1342 1347 verbose = repo.ui.verbose
1343 1348 fctx = mctx.filectx
1344 1349 ui = repo.ui
1345 1350 i = 0
1346 1351 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1347 1352 for f, (flags, backup), msg in actions:
1348 1353 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1349 1354 if verbose:
1350 1355 repo.ui.note(_("getting %s\n") % f)
1351 1356
1352 1357 if backup:
1353 1358 # If a file or directory exists with the same name, back that
1354 1359 # up. Otherwise, look to see if there is a file that conflicts
1355 1360 # with a directory this file is in, and if so, back that up.
1356 1361 absf = repo.wjoin(f)
1357 1362 if not repo.wvfs.lexists(f):
1358 1363 for p in util.finddirs(f):
1359 1364 if repo.wvfs.isfileorlink(p):
1360 1365 absf = repo.wjoin(p)
1361 1366 break
1362 1367 orig = scmutil.origpath(ui, repo, absf)
1363 1368 if repo.wvfs.lexists(absf):
1364 1369 util.rename(absf, orig)
1365 1370 wctx[f].clearunknown()
1366 1371 wctx[f].write(fctx(f).data(), flags, backgroundclose=True)
1367 1372 if i == 100:
1368 1373 yield i, f
1369 1374 i = 0
1370 1375 i += 1
1371 1376 if i > 0:
1372 1377 yield i, f
1373 1378
1374 1379 # It's necessary to flush here in case we're inside a worker fork and will
1375 1380 # quit after this function.
1376 1381 wctx.flushall()
1377 1382
1378 1383 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1379 1384 """apply the merge action list to the working directory
1380 1385
1381 1386 wctx is the working copy context
1382 1387 mctx is the context to be merged into the working copy
1383 1388
1384 1389 Return a tuple of counts (updated, merged, removed, unresolved) that
1385 1390 describes how many files were affected by the update.
1386 1391 """
1387 1392
1388 1393 updated, merged, removed = 0, 0, 0
1389 1394 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1390 1395 moves = []
1391 1396 for m, l in actions.items():
1392 1397 l.sort()
1393 1398
1394 1399 # 'cd' and 'dc' actions are treated like other merge conflicts
1395 1400 mergeactions = sorted(actions['cd'])
1396 1401 mergeactions.extend(sorted(actions['dc']))
1397 1402 mergeactions.extend(actions['m'])
1398 1403 for f, args, msg in mergeactions:
1399 1404 f1, f2, fa, move, anc = args
1400 1405 if f == '.hgsubstate': # merged internally
1401 1406 continue
1402 1407 if f1 is None:
1403 1408 fcl = filemerge.absentfilectx(wctx, fa)
1404 1409 else:
1405 1410 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1406 1411 fcl = wctx[f1]
1407 1412 if f2 is None:
1408 1413 fco = filemerge.absentfilectx(mctx, fa)
1409 1414 else:
1410 1415 fco = mctx[f2]
1411 1416 actx = repo[anc]
1412 1417 if fa in actx:
1413 1418 fca = actx[fa]
1414 1419 else:
1415 1420 # TODO: move to absentfilectx
1416 1421 fca = repo.filectx(f1, fileid=nullrev)
1417 1422 ms.add(fcl, fco, fca, f)
1418 1423 if f1 != f and move:
1419 1424 moves.append(f1)
1420 1425
1421 1426 _updating = _('updating')
1422 1427 _files = _('files')
1423 1428 progress = repo.ui.progress
1424 1429
1425 1430 # remove renamed files after safely stored
1426 1431 for f in moves:
1427 1432 if wctx[f].lexists():
1428 1433 repo.ui.debug("removing %s\n" % f)
1429 1434 wctx[f].audit()
1430 1435 wctx[f].remove()
1431 1436
1432 1437 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1433 1438 z = 0
1434 1439
1435 1440 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1436 1441 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1437 1442
1438 1443 # record path conflicts
1439 1444 for f, args, msg in actions['p']:
1440 1445 f1, fo = args
1441 1446 s = repo.ui.status
1442 1447 s(_("%s: path conflict - a file or link has the same name as a "
1443 1448 "directory\n") % f)
1444 1449 if fo == 'l':
1445 1450 s(_("the local file has been renamed to %s\n") % f1)
1446 1451 else:
1447 1452 s(_("the remote file has been renamed to %s\n") % f1)
1448 1453 s(_("resolve manually then use 'hg resolve --mark %s'\n") % f)
1449 1454 ms.addpath(f, f1, fo)
1450 1455 z += 1
1451 1456 progress(_updating, z, item=f, total=numupdates, unit=_files)
1452 1457
1453 1458 # When merging in-memory, we can't support worker processes, so set the
1454 1459 # per-item cost at 0 in that case.
1455 1460 cost = 0 if wctx.isinmemory() else 0.001
1456 1461
1457 1462 # remove in parallel (must come before resolving path conflicts and getting)
1458 1463 prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx),
1459 1464 actions['r'])
1460 1465 for i, item in prog:
1461 1466 z += i
1462 1467 progress(_updating, z, item=item, total=numupdates, unit=_files)
1463 1468 removed = len(actions['r'])
1464 1469
1465 1470 # resolve path conflicts (must come before getting)
1466 1471 for f, args, msg in actions['pr']:
1467 1472 repo.ui.debug(" %s: %s -> pr\n" % (f, msg))
1468 1473 f0, = args
1469 1474 if wctx[f0].lexists():
1470 1475 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1471 1476 wctx[f].audit()
1472 1477 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1473 1478 wctx[f0].remove()
1474 1479 z += 1
1475 1480 progress(_updating, z, item=f, total=numupdates, unit=_files)
1476 1481
1477 1482 # We should flush before forking into worker processes, since those workers
1478 1483 # flush when they complete, and we don't want to duplicate work.
1479 1484 wctx.flushall()
1480 1485
1481 1486 # get in parallel
1482 1487 prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx),
1483 1488 actions['g'])
1484 1489 for i, item in prog:
1485 1490 z += i
1486 1491 progress(_updating, z, item=item, total=numupdates, unit=_files)
1487 1492 updated = len(actions['g'])
1488 1493
1489 1494 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1490 1495 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1491 1496
1492 1497 # forget (manifest only, just log it) (must come first)
1493 1498 for f, args, msg in actions['f']:
1494 1499 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1495 1500 z += 1
1496 1501 progress(_updating, z, item=f, total=numupdates, unit=_files)
1497 1502
1498 1503 # re-add (manifest only, just log it)
1499 1504 for f, args, msg in actions['a']:
1500 1505 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1501 1506 z += 1
1502 1507 progress(_updating, z, item=f, total=numupdates, unit=_files)
1503 1508
1504 1509 # re-add/mark as modified (manifest only, just log it)
1505 1510 for f, args, msg in actions['am']:
1506 1511 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1507 1512 z += 1
1508 1513 progress(_updating, z, item=f, total=numupdates, unit=_files)
1509 1514
1510 1515 # keep (noop, just log it)
1511 1516 for f, args, msg in actions['k']:
1512 1517 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1513 1518 # no progress
1514 1519
1515 1520 # directory rename, move local
1516 1521 for f, args, msg in actions['dm']:
1517 1522 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1518 1523 z += 1
1519 1524 progress(_updating, z, item=f, total=numupdates, unit=_files)
1520 1525 f0, flags = args
1521 1526 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1522 1527 wctx[f].audit()
1523 1528 wctx[f].write(wctx.filectx(f0).data(), flags)
1524 1529 wctx[f0].remove()
1525 1530 updated += 1
1526 1531
1527 1532 # local directory rename, get
1528 1533 for f, args, msg in actions['dg']:
1529 1534 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1530 1535 z += 1
1531 1536 progress(_updating, z, item=f, total=numupdates, unit=_files)
1532 1537 f0, flags = args
1533 1538 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1534 1539 wctx[f].write(mctx.filectx(f0).data(), flags)
1535 1540 updated += 1
1536 1541
1537 1542 # exec
1538 1543 for f, args, msg in actions['e']:
1539 1544 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1540 1545 z += 1
1541 1546 progress(_updating, z, item=f, total=numupdates, unit=_files)
1542 1547 flags, = args
1543 1548 wctx[f].audit()
1544 1549 wctx[f].setflags('l' in flags, 'x' in flags)
1545 1550 updated += 1
1546 1551
1547 1552 # the ordering is important here -- ms.mergedriver will raise if the merge
1548 1553 # driver has changed, and we want to be able to bypass it when overwrite is
1549 1554 # True
1550 1555 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1551 1556
1552 1557 if usemergedriver:
1553 1558 ms.commit()
1554 1559 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1555 1560 # the driver might leave some files unresolved
1556 1561 unresolvedf = set(ms.unresolved())
1557 1562 if not proceed:
1558 1563 # XXX setting unresolved to at least 1 is a hack to make sure we
1559 1564 # error out
1560 1565 return updated, merged, removed, max(len(unresolvedf), 1)
1561 1566 newactions = []
1562 1567 for f, args, msg in mergeactions:
1563 1568 if f in unresolvedf:
1564 1569 newactions.append((f, args, msg))
1565 1570 mergeactions = newactions
1566 1571
1567 1572 try:
1568 1573 # premerge
1569 1574 tocomplete = []
1570 1575 for f, args, msg in mergeactions:
1571 1576 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1572 1577 z += 1
1573 1578 progress(_updating, z, item=f, total=numupdates, unit=_files)
1574 1579 if f == '.hgsubstate': # subrepo states need updating
1575 1580 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1576 1581 overwrite, labels)
1577 1582 continue
1578 1583 wctx[f].audit()
1579 1584 complete, r = ms.preresolve(f, wctx)
1580 1585 if not complete:
1581 1586 numupdates += 1
1582 1587 tocomplete.append((f, args, msg))
1583 1588
1584 1589 # merge
1585 1590 for f, args, msg in tocomplete:
1586 1591 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1587 1592 z += 1
1588 1593 progress(_updating, z, item=f, total=numupdates, unit=_files)
1589 1594 ms.resolve(f, wctx)
1590 1595
1591 1596 finally:
1592 1597 ms.commit()
1593 1598
1594 1599 unresolved = ms.unresolvedcount()
1595 1600
1596 1601 if usemergedriver and not unresolved and ms.mdstate() != 's':
1597 1602 if not driverconclude(repo, ms, wctx, labels=labels):
1598 1603 # XXX setting unresolved to at least 1 is a hack to make sure we
1599 1604 # error out
1600 1605 unresolved = max(unresolved, 1)
1601 1606
1602 1607 ms.commit()
1603 1608
1604 1609 msupdated, msmerged, msremoved = ms.counts()
1605 1610 updated += msupdated
1606 1611 merged += msmerged
1607 1612 removed += msremoved
1608 1613
1609 1614 extraactions = ms.actions()
1610 1615 if extraactions:
1611 1616 mfiles = set(a[0] for a in actions['m'])
1612 1617 for k, acts in extraactions.iteritems():
1613 1618 actions[k].extend(acts)
1614 1619 # Remove these files from actions['m'] as well. This is important
1615 1620 # because in recordupdates, files in actions['m'] are processed
1616 1621 # after files in other actions, and the merge driver might add
1617 1622 # files to those actions via extraactions above. This can lead to a
1618 1623 # file being recorded twice, with poor results. This is especially
1619 1624 # problematic for actions['r'] (currently only possible with the
1620 1625 # merge driver in the initial merge process; interrupted merges
1621 1626 # don't go through this flow).
1622 1627 #
1623 1628 # The real fix here is to have indexes by both file and action so
1624 1629 # that when the action for a file is changed it is automatically
1625 1630 # reflected in the other action lists. But that involves a more
1626 1631 # complex data structure, so this will do for now.
1627 1632 #
1628 1633 # We don't need to do the same operation for 'dc' and 'cd' because
1629 1634 # those lists aren't consulted again.
1630 1635 mfiles.difference_update(a[0] for a in acts)
1631 1636
1632 1637 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1633 1638
1634 1639 progress(_updating, None, total=numupdates, unit=_files)
1635 1640
1636 1641 return updated, merged, removed, unresolved
1637 1642
1638 1643 def recordupdates(repo, actions, branchmerge):
1639 1644 "record merge actions to the dirstate"
1640 1645 # remove (must come first)
1641 1646 for f, args, msg in actions.get('r', []):
1642 1647 if branchmerge:
1643 1648 repo.dirstate.remove(f)
1644 1649 else:
1645 1650 repo.dirstate.drop(f)
1646 1651
1647 1652 # forget (must come first)
1648 1653 for f, args, msg in actions.get('f', []):
1649 1654 repo.dirstate.drop(f)
1650 1655
1651 1656 # resolve path conflicts
1652 1657 for f, args, msg in actions.get('pr', []):
1653 1658 f0, = args
1654 1659 origf0 = repo.dirstate.copied(f0) or f0
1655 1660 repo.dirstate.add(f)
1656 1661 repo.dirstate.copy(origf0, f)
1657 1662 if f0 == origf0:
1658 1663 repo.dirstate.remove(f0)
1659 1664 else:
1660 1665 repo.dirstate.drop(f0)
1661 1666
1662 1667 # re-add
1663 1668 for f, args, msg in actions.get('a', []):
1664 1669 repo.dirstate.add(f)
1665 1670
1666 1671 # re-add/mark as modified
1667 1672 for f, args, msg in actions.get('am', []):
1668 1673 if branchmerge:
1669 1674 repo.dirstate.normallookup(f)
1670 1675 else:
1671 1676 repo.dirstate.add(f)
1672 1677
1673 1678 # exec change
1674 1679 for f, args, msg in actions.get('e', []):
1675 1680 repo.dirstate.normallookup(f)
1676 1681
1677 1682 # keep
1678 1683 for f, args, msg in actions.get('k', []):
1679 1684 pass
1680 1685
1681 1686 # get
1682 1687 for f, args, msg in actions.get('g', []):
1683 1688 if branchmerge:
1684 1689 repo.dirstate.otherparent(f)
1685 1690 else:
1686 1691 repo.dirstate.normal(f)
1687 1692
1688 1693 # merge
1689 1694 for f, args, msg in actions.get('m', []):
1690 1695 f1, f2, fa, move, anc = args
1691 1696 if branchmerge:
1692 1697 # We've done a branch merge, mark this file as merged
1693 1698 # so that we properly record the merger later
1694 1699 repo.dirstate.merge(f)
1695 1700 if f1 != f2: # copy/rename
1696 1701 if move:
1697 1702 repo.dirstate.remove(f1)
1698 1703 if f1 != f:
1699 1704 repo.dirstate.copy(f1, f)
1700 1705 else:
1701 1706 repo.dirstate.copy(f2, f)
1702 1707 else:
1703 1708 # We've update-merged a locally modified file, so
1704 1709 # we set the dirstate to emulate a normal checkout
1705 1710 # of that file some time in the past. Thus our
1706 1711 # merge will appear as a normal local file
1707 1712 # modification.
1708 1713 if f2 == f: # file not locally copied/moved
1709 1714 repo.dirstate.normallookup(f)
1710 1715 if move:
1711 1716 repo.dirstate.drop(f1)
1712 1717
1713 1718 # directory rename, move local
1714 1719 for f, args, msg in actions.get('dm', []):
1715 1720 f0, flag = args
1716 1721 if branchmerge:
1717 1722 repo.dirstate.add(f)
1718 1723 repo.dirstate.remove(f0)
1719 1724 repo.dirstate.copy(f0, f)
1720 1725 else:
1721 1726 repo.dirstate.normal(f)
1722 1727 repo.dirstate.drop(f0)
1723 1728
1724 1729 # directory rename, get
1725 1730 for f, args, msg in actions.get('dg', []):
1726 1731 f0, flag = args
1727 1732 if branchmerge:
1728 1733 repo.dirstate.add(f)
1729 1734 repo.dirstate.copy(f0, f)
1730 1735 else:
1731 1736 repo.dirstate.normal(f)
1732 1737
1733 1738 def update(repo, node, branchmerge, force, ancestor=None,
1734 1739 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1735 1740 updatecheck=None, wc=None):
1736 1741 """
1737 1742 Perform a merge between the working directory and the given node
1738 1743
1739 1744 node = the node to update to
1740 1745 branchmerge = whether to merge between branches
1741 1746 force = whether to force branch merging or file overwriting
1742 1747 matcher = a matcher to filter file lists (dirstate not updated)
1743 1748 mergeancestor = whether it is merging with an ancestor. If true,
1744 1749 we should accept the incoming changes for any prompts that occur.
1745 1750 If false, merging with an ancestor (fast-forward) is only allowed
1746 1751 between different named branches. This flag is used by rebase extension
1747 1752 as a temporary fix and should be avoided in general.
1748 1753 labels = labels to use for base, local and other
1749 1754 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1750 1755 this is True, then 'force' should be True as well.
1751 1756
1752 1757 The table below shows all the behaviors of the update command given the
1753 1758 -c/--check and -C/--clean or no options, whether the working directory is
1754 1759 dirty, whether a revision is specified, and the relationship of the parent
1755 1760 rev to the target rev (linear or not). Match from top first. The -n
1756 1761 option doesn't exist on the command line, but represents the
1757 1762 experimental.updatecheck=noconflict option.
1758 1763
1759 1764 This logic is tested by test-update-branches.t.
1760 1765
1761 1766 -c -C -n -m dirty rev linear | result
1762 1767 y y * * * * * | (1)
1763 1768 y * y * * * * | (1)
1764 1769 y * * y * * * | (1)
1765 1770 * y y * * * * | (1)
1766 1771 * y * y * * * | (1)
1767 1772 * * y y * * * | (1)
1768 1773 * * * * * n n | x
1769 1774 * * * * n * * | ok
1770 1775 n n n n y * y | merge
1771 1776 n n n n y y n | (2)
1772 1777 n n n y y * * | merge
1773 1778 n n y n y * * | merge if no conflict
1774 1779 n y n n y * * | discard
1775 1780 y n n n y * * | (3)
1776 1781
1777 1782 x = can't happen
1778 1783 * = don't-care
1779 1784 1 = incompatible options (checked in commands.py)
1780 1785 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1781 1786 3 = abort: uncommitted changes (checked in commands.py)
1782 1787
1783 1788 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1784 1789 to repo[None] if None is passed.
1785 1790
1786 1791 Return the same tuple as applyupdates().
1787 1792 """
1788 1793 # Avoid cycle.
1789 1794 from . import sparse
1790 1795
1791 1796 # This function used to find the default destination if node was None, but
1792 1797 # that's now in destutil.py.
1793 1798 assert node is not None
1794 1799 if not branchmerge and not force:
1795 1800 # TODO: remove the default once all callers that pass branchmerge=False
1796 1801 # and force=False pass a value for updatecheck. We may want to allow
1797 1802 # updatecheck='abort' to better suppport some of these callers.
1798 1803 if updatecheck is None:
1799 1804 updatecheck = 'linear'
1800 1805 assert updatecheck in ('none', 'linear', 'noconflict')
1801 1806 # If we're doing a partial update, we need to skip updating
1802 1807 # the dirstate, so make a note of any partial-ness to the
1803 1808 # update here.
1804 1809 if matcher is None or matcher.always():
1805 1810 partial = False
1806 1811 else:
1807 1812 partial = True
1808 1813 with repo.wlock():
1809 1814 if wc is None:
1810 1815 wc = repo[None]
1811 1816 pl = wc.parents()
1812 1817 p1 = pl[0]
1813 1818 pas = [None]
1814 1819 if ancestor is not None:
1815 1820 pas = [repo[ancestor]]
1816 1821
1817 1822 overwrite = force and not branchmerge
1818 1823
1819 1824 p2 = repo[node]
1820 1825 if pas[0] is None:
1821 1826 if repo.ui.configlist('merge', 'preferancestor') == ['*']:
1822 1827 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1823 1828 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1824 1829 else:
1825 1830 pas = [p1.ancestor(p2, warn=branchmerge)]
1826 1831
1827 1832 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1828 1833
1829 1834 ### check phase
1830 1835 if not overwrite:
1831 1836 if len(pl) > 1:
1832 1837 raise error.Abort(_("outstanding uncommitted merge"))
1833 1838 ms = mergestate.read(repo)
1834 1839 if list(ms.unresolved()):
1835 1840 raise error.Abort(_("outstanding merge conflicts"))
1836 1841 if branchmerge:
1837 1842 if pas == [p2]:
1838 1843 raise error.Abort(_("merging with a working directory ancestor"
1839 1844 " has no effect"))
1840 1845 elif pas == [p1]:
1841 1846 if not mergeancestor and wc.branch() == p2.branch():
1842 1847 raise error.Abort(_("nothing to merge"),
1843 1848 hint=_("use 'hg update' "
1844 1849 "or check 'hg heads'"))
1845 1850 if not force and (wc.files() or wc.deleted()):
1846 1851 raise error.Abort(_("uncommitted changes"),
1847 1852 hint=_("use 'hg status' to list changes"))
1848 1853 for s in sorted(wc.substate):
1849 1854 wc.sub(s).bailifchanged()
1850 1855
1851 1856 elif not overwrite:
1852 1857 if p1 == p2: # no-op update
1853 1858 # call the hooks and exit early
1854 1859 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1855 1860 repo.hook('update', parent1=xp2, parent2='', error=0)
1856 1861 return 0, 0, 0, 0
1857 1862
1858 1863 if (updatecheck == 'linear' and
1859 1864 pas not in ([p1], [p2])): # nonlinear
1860 1865 dirty = wc.dirty(missing=True)
1861 1866 if dirty:
1862 1867 # Branching is a bit strange to ensure we do the minimal
1863 1868 # amount of call to obsutil.foreground.
1864 1869 foreground = obsutil.foreground(repo, [p1.node()])
1865 1870 # note: the <node> variable contains a random identifier
1866 1871 if repo[node].node() in foreground:
1867 1872 pass # allow updating to successors
1868 1873 else:
1869 1874 msg = _("uncommitted changes")
1870 1875 hint = _("commit or update --clean to discard changes")
1871 1876 raise error.UpdateAbort(msg, hint=hint)
1872 1877 else:
1873 1878 # Allow jumping branches if clean and specific rev given
1874 1879 pass
1875 1880
1876 1881 if overwrite:
1877 1882 pas = [wc]
1878 1883 elif not branchmerge:
1879 1884 pas = [p1]
1880 1885
1881 1886 # deprecated config: merge.followcopies
1882 1887 followcopies = repo.ui.configbool('merge', 'followcopies')
1883 1888 if overwrite:
1884 1889 followcopies = False
1885 1890 elif not pas[0]:
1886 1891 followcopies = False
1887 1892 if not branchmerge and not wc.dirty(missing=True):
1888 1893 followcopies = False
1889 1894
1890 1895 ### calculate phase
1891 1896 actionbyfile, diverge, renamedelete = calculateupdates(
1892 1897 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1893 1898 followcopies, matcher=matcher, mergeforce=mergeforce)
1894 1899
1895 1900 if updatecheck == 'noconflict':
1896 1901 for f, (m, args, msg) in actionbyfile.iteritems():
1897 1902 if m not in ('g', 'k', 'e', 'r', 'pr'):
1898 1903 msg = _("conflicting changes")
1899 1904 hint = _("commit or update --clean to discard changes")
1900 1905 raise error.Abort(msg, hint=hint)
1901 1906
1902 1907 # Prompt and create actions. Most of this is in the resolve phase
1903 1908 # already, but we can't handle .hgsubstate in filemerge or
1904 1909 # subrepo.submerge yet so we have to keep prompting for it.
1905 1910 if '.hgsubstate' in actionbyfile:
1906 1911 f = '.hgsubstate'
1907 1912 m, args, msg = actionbyfile[f]
1908 1913 prompts = filemerge.partextras(labels)
1909 1914 prompts['f'] = f
1910 1915 if m == 'cd':
1911 1916 if repo.ui.promptchoice(
1912 1917 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1913 1918 "use (c)hanged version or (d)elete?"
1914 1919 "$$ &Changed $$ &Delete") % prompts, 0):
1915 1920 actionbyfile[f] = ('r', None, "prompt delete")
1916 1921 elif f in p1:
1917 1922 actionbyfile[f] = ('am', None, "prompt keep")
1918 1923 else:
1919 1924 actionbyfile[f] = ('a', None, "prompt keep")
1920 1925 elif m == 'dc':
1921 1926 f1, f2, fa, move, anc = args
1922 1927 flags = p2[f2].flags()
1923 1928 if repo.ui.promptchoice(
1924 1929 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1925 1930 "use (c)hanged version or leave (d)eleted?"
1926 1931 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1927 1932 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1928 1933 else:
1929 1934 del actionbyfile[f]
1930 1935
1931 1936 # Convert to dictionary-of-lists format
1932 1937 actions = dict((m, [])
1933 1938 for m in 'a am f g cd dc r dm dg m e k p pr'.split())
1934 1939 for f, (m, args, msg) in actionbyfile.iteritems():
1935 1940 if m not in actions:
1936 1941 actions[m] = []
1937 1942 actions[m].append((f, args, msg))
1938 1943
1939 1944 if not util.fscasesensitive(repo.path):
1940 1945 # check collision between files only in p2 for clean update
1941 1946 if (not branchmerge and
1942 1947 (force or not wc.dirty(missing=True, branch=False))):
1943 1948 _checkcollision(repo, p2.manifest(), None)
1944 1949 else:
1945 1950 _checkcollision(repo, wc.manifest(), actions)
1946 1951
1947 1952 # divergent renames
1948 1953 for f, fl in sorted(diverge.iteritems()):
1949 1954 repo.ui.warn(_("note: possible conflict - %s was renamed "
1950 1955 "multiple times to:\n") % f)
1951 1956 for nf in fl:
1952 1957 repo.ui.warn(" %s\n" % nf)
1953 1958
1954 1959 # rename and delete
1955 1960 for f, fl in sorted(renamedelete.iteritems()):
1956 1961 repo.ui.warn(_("note: possible conflict - %s was deleted "
1957 1962 "and renamed to:\n") % f)
1958 1963 for nf in fl:
1959 1964 repo.ui.warn(" %s\n" % nf)
1960 1965
1961 1966 ### apply phase
1962 1967 if not branchmerge: # just jump to the new rev
1963 1968 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1964 1969 if not partial:
1965 1970 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1966 1971 # note that we're in the middle of an update
1967 1972 repo.vfs.write('updatestate', p2.hex())
1968 1973
1969 1974 # Advertise fsmonitor when its presence could be useful.
1970 1975 #
1971 1976 # We only advertise when performing an update from an empty working
1972 1977 # directory. This typically only occurs during initial clone.
1973 1978 #
1974 1979 # We give users a mechanism to disable the warning in case it is
1975 1980 # annoying.
1976 1981 #
1977 1982 # We only allow on Linux and MacOS because that's where fsmonitor is
1978 1983 # considered stable.
1979 1984 fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused')
1980 1985 fsmonitorthreshold = repo.ui.configint('fsmonitor',
1981 1986 'warn_update_file_count')
1982 1987 try:
1983 1988 extensions.find('fsmonitor')
1984 1989 fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off'
1985 1990 # We intentionally don't look at whether fsmonitor has disabled
1986 1991 # itself because a) fsmonitor may have already printed a warning
1987 1992 # b) we only care about the config state here.
1988 1993 except KeyError:
1989 1994 fsmonitorenabled = False
1990 1995
1991 1996 if (fsmonitorwarning
1992 1997 and not fsmonitorenabled
1993 1998 and p1.node() == nullid
1994 1999 and len(actions['g']) >= fsmonitorthreshold
1995 2000 and pycompat.sysplatform.startswith(('linux', 'darwin'))):
1996 2001 repo.ui.warn(
1997 2002 _('(warning: large working directory being used without '
1998 2003 'fsmonitor enabled; enable fsmonitor to improve performance; '
1999 2004 'see "hg help -e fsmonitor")\n'))
2000 2005
2001 2006 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
2002 2007 wc.flushall()
2003 2008
2004 2009 if not partial:
2005 2010 with repo.dirstate.parentchange():
2006 2011 repo.setparents(fp1, fp2)
2007 2012 recordupdates(repo, actions, branchmerge)
2008 2013 # update completed, clear state
2009 2014 util.unlink(repo.vfs.join('updatestate'))
2010 2015
2011 2016 if not branchmerge:
2012 2017 repo.dirstate.setbranch(p2.branch())
2013 2018
2014 2019 # If we're updating to a location, clean up any stale temporary includes
2015 2020 # (ex: this happens during hg rebase --abort).
2016 2021 if not branchmerge:
2017 2022 sparse.prunetemporaryincludes(repo)
2018 2023
2019 2024 if not partial:
2020 2025 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
2021 2026 return stats
2022 2027
2023 2028 def graft(repo, ctx, pctx, labels, keepparent=False):
2024 2029 """Do a graft-like merge.
2025 2030
2026 2031 This is a merge where the merge ancestor is chosen such that one
2027 2032 or more changesets are grafted onto the current changeset. In
2028 2033 addition to the merge, this fixes up the dirstate to include only
2029 2034 a single parent (if keepparent is False) and tries to duplicate any
2030 2035 renames/copies appropriately.
2031 2036
2032 2037 ctx - changeset to rebase
2033 2038 pctx - merge base, usually ctx.p1()
2034 2039 labels - merge labels eg ['local', 'graft']
2035 2040 keepparent - keep second parent if any
2036 2041
2037 2042 """
2038 2043 # If we're grafting a descendant onto an ancestor, be sure to pass
2039 2044 # mergeancestor=True to update. This does two things: 1) allows the merge if
2040 2045 # the destination is the same as the parent of the ctx (so we can use graft
2041 2046 # to copy commits), and 2) informs update that the incoming changes are
2042 2047 # newer than the destination so it doesn't prompt about "remote changed foo
2043 2048 # which local deleted".
2044 2049 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
2045 2050
2046 2051 stats = update(repo, ctx.node(), True, True, pctx.node(),
2047 2052 mergeancestor=mergeancestor, labels=labels)
2048 2053
2049 2054 pother = nullid
2050 2055 parents = ctx.parents()
2051 2056 if keepparent and len(parents) == 2 and pctx in parents:
2052 2057 parents.remove(pctx)
2053 2058 pother = parents[0].node()
2054 2059
2055 2060 with repo.dirstate.parentchange():
2056 2061 repo.setparents(repo['.'].node(), pother)
2057 2062 repo.dirstate.write(repo.currenttransaction())
2058 2063 # fix up dirstate for copies and renames
2059 2064 copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
2060 2065 return stats
General Comments 0
You need to be logged in to leave comments. Login now