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