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