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