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