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