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