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