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