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