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