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