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