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