##// END OF EJS Templates
merge: add stubs for preprocess and conclude steps of merge driver...
Siddharth Agarwal -
r26785:e9a0d5f5 default
parent child Browse files
Show More
@@ -1,1302 +1,1314 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import os
12 12 import shutil
13 13 import struct
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 bin,
18 18 hex,
19 19 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 def driverpreprocess(repo, ms, wctx, labels=None):
520 """run the preprocess step of the merge driver, if any
521
522 This is currently not implemented -- it's an extension point."""
523 return True
524
525 def driverconclude(repo, ms, wctx, labels=None):
526 """run the conclude step of the merge driver, if any
527
528 This is currently not implemented -- it's an extension point."""
529 return True
530
519 531 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
520 532 acceptremote, followcopies):
521 533 """
522 534 Merge p1 and p2 with ancestor pa and generate merge action list
523 535
524 536 branchmerge and force are as passed in to update
525 537 partial = function to filter file lists
526 538 acceptremote = accept the incoming changes without prompting
527 539 """
528 540
529 541 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
530 542
531 543 # manifests fetched in order are going to be faster, so prime the caches
532 544 [x.manifest() for x in
533 545 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
534 546
535 547 if followcopies:
536 548 ret = copies.mergecopies(repo, wctx, p2, pa)
537 549 copy, movewithdir, diverge, renamedelete = ret
538 550
539 551 repo.ui.note(_("resolving manifests\n"))
540 552 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
541 553 % (bool(branchmerge), bool(force), bool(partial)))
542 554 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
543 555
544 556 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
545 557 copied = set(copy.values())
546 558 copied.update(movewithdir.values())
547 559
548 560 if '.hgsubstate' in m1:
549 561 # check whether sub state is modified
550 562 for s in sorted(wctx.substate):
551 563 if wctx.sub(s).dirty():
552 564 m1['.hgsubstate'] += '+'
553 565 break
554 566
555 567 # Compare manifests
556 568 diff = m1.diff(m2)
557 569
558 570 actions = {}
559 571 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
560 572 if partial and not partial(f):
561 573 continue
562 574 if n1 and n2: # file exists on both local and remote side
563 575 if f not in ma:
564 576 fa = copy.get(f, None)
565 577 if fa is not None:
566 578 actions[f] = ('m', (f, f, fa, False, pa.node()),
567 579 "both renamed from " + fa)
568 580 else:
569 581 actions[f] = ('m', (f, f, None, False, pa.node()),
570 582 "both created")
571 583 else:
572 584 a = ma[f]
573 585 fla = ma.flags(f)
574 586 nol = 'l' not in fl1 + fl2 + fla
575 587 if n2 == a and fl2 == fla:
576 588 actions[f] = ('k' , (), "remote unchanged")
577 589 elif n1 == a and fl1 == fla: # local unchanged - use remote
578 590 if n1 == n2: # optimization: keep local content
579 591 actions[f] = ('e', (fl2,), "update permissions")
580 592 else:
581 593 actions[f] = ('g', (fl2,), "remote is newer")
582 594 elif nol and n2 == a: # remote only changed 'x'
583 595 actions[f] = ('e', (fl2,), "update permissions")
584 596 elif nol and n1 == a: # local only changed 'x'
585 597 actions[f] = ('g', (fl1,), "remote is newer")
586 598 else: # both changed something
587 599 actions[f] = ('m', (f, f, f, False, pa.node()),
588 600 "versions differ")
589 601 elif n1: # file exists only on local side
590 602 if f in copied:
591 603 pass # we'll deal with it on m2 side
592 604 elif f in movewithdir: # directory rename, move local
593 605 f2 = movewithdir[f]
594 606 if f2 in m2:
595 607 actions[f2] = ('m', (f, f2, None, True, pa.node()),
596 608 "remote directory rename, both created")
597 609 else:
598 610 actions[f2] = ('dm', (f, fl1),
599 611 "remote directory rename - move from " + f)
600 612 elif f in copy:
601 613 f2 = copy[f]
602 614 actions[f] = ('m', (f, f2, f2, False, pa.node()),
603 615 "local copied/moved from " + f2)
604 616 elif f in ma: # clean, a different, no remote
605 617 if n1 != ma[f]:
606 618 if acceptremote:
607 619 actions[f] = ('r', None, "remote delete")
608 620 else:
609 621 actions[f] = ('cd', None, "prompt changed/deleted")
610 622 elif n1[20:] == 'a':
611 623 # This extra 'a' is added by working copy manifest to mark
612 624 # the file as locally added. We should forget it instead of
613 625 # deleting it.
614 626 actions[f] = ('f', None, "remote deleted")
615 627 else:
616 628 actions[f] = ('r', None, "other deleted")
617 629 elif n2: # file exists only on remote side
618 630 if f in copied:
619 631 pass # we'll deal with it on m1 side
620 632 elif f in movewithdir:
621 633 f2 = movewithdir[f]
622 634 if f2 in m1:
623 635 actions[f2] = ('m', (f2, f, None, False, pa.node()),
624 636 "local directory rename, both created")
625 637 else:
626 638 actions[f2] = ('dg', (f, fl2),
627 639 "local directory rename - get from " + f)
628 640 elif f in copy:
629 641 f2 = copy[f]
630 642 if f2 in m2:
631 643 actions[f] = ('m', (f2, f, f2, False, pa.node()),
632 644 "remote copied from " + f2)
633 645 else:
634 646 actions[f] = ('m', (f2, f, f2, True, pa.node()),
635 647 "remote moved from " + f2)
636 648 elif f not in ma:
637 649 # local unknown, remote created: the logic is described by the
638 650 # following table:
639 651 #
640 652 # force branchmerge different | action
641 653 # n * * | create
642 654 # y n * | create
643 655 # y y n | create
644 656 # y y y | merge
645 657 #
646 658 # Checking whether the files are different is expensive, so we
647 659 # don't do that when we can avoid it.
648 660 if not force:
649 661 actions[f] = ('c', (fl2,), "remote created")
650 662 elif not branchmerge:
651 663 actions[f] = ('c', (fl2,), "remote created")
652 664 else:
653 665 actions[f] = ('cm', (fl2, pa.node()),
654 666 "remote created, get or merge")
655 667 elif n2 != ma[f]:
656 668 if acceptremote:
657 669 actions[f] = ('c', (fl2,), "remote recreating")
658 670 else:
659 671 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
660 672
661 673 return actions, diverge, renamedelete
662 674
663 675 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
664 676 """Resolves false conflicts where the nodeid changed but the content
665 677 remained the same."""
666 678
667 679 for f, (m, args, msg) in actions.items():
668 680 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
669 681 # local did change but ended up with same content
670 682 actions[f] = 'r', None, "prompt same"
671 683 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
672 684 # remote did change but ended up with same content
673 685 del actions[f] # don't get = keep local deleted
674 686
675 687 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
676 688 acceptremote, followcopies):
677 689 "Calculate the actions needed to merge mctx into wctx using ancestors"
678 690
679 691 if len(ancestors) == 1: # default
680 692 actions, diverge, renamedelete = manifestmerge(
681 693 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
682 694 acceptremote, followcopies)
683 695 _checkunknownfiles(repo, wctx, mctx, force, actions)
684 696
685 697 else: # only when merge.preferancestor=* - the default
686 698 repo.ui.note(
687 699 _("note: merging %s and %s using bids from ancestors %s\n") %
688 700 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
689 701
690 702 # Call for bids
691 703 fbids = {} # mapping filename to bids (action method to list af actions)
692 704 diverge, renamedelete = None, None
693 705 for ancestor in ancestors:
694 706 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
695 707 actions, diverge1, renamedelete1 = manifestmerge(
696 708 repo, wctx, mctx, ancestor, branchmerge, force, partial,
697 709 acceptremote, followcopies)
698 710 _checkunknownfiles(repo, wctx, mctx, force, actions)
699 711
700 712 # Track the shortest set of warning on the theory that bid
701 713 # merge will correctly incorporate more information
702 714 if diverge is None or len(diverge1) < len(diverge):
703 715 diverge = diverge1
704 716 if renamedelete is None or len(renamedelete) < len(renamedelete1):
705 717 renamedelete = renamedelete1
706 718
707 719 for f, a in sorted(actions.iteritems()):
708 720 m, args, msg = a
709 721 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
710 722 if f in fbids:
711 723 d = fbids[f]
712 724 if m in d:
713 725 d[m].append(a)
714 726 else:
715 727 d[m] = [a]
716 728 else:
717 729 fbids[f] = {m: [a]}
718 730
719 731 # Pick the best bid for each file
720 732 repo.ui.note(_('\nauction for merging merge bids\n'))
721 733 actions = {}
722 734 for f, bids in sorted(fbids.items()):
723 735 # bids is a mapping from action method to list af actions
724 736 # Consensus?
725 737 if len(bids) == 1: # all bids are the same kind of method
726 738 m, l = bids.items()[0]
727 739 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
728 740 repo.ui.note(" %s: consensus for %s\n" % (f, m))
729 741 actions[f] = l[0]
730 742 continue
731 743 # If keep is an option, just do it.
732 744 if 'k' in bids:
733 745 repo.ui.note(" %s: picking 'keep' action\n" % f)
734 746 actions[f] = bids['k'][0]
735 747 continue
736 748 # If there are gets and they all agree [how could they not?], do it.
737 749 if 'g' in bids:
738 750 ga0 = bids['g'][0]
739 751 if all(a == ga0 for a in bids['g'][1:]):
740 752 repo.ui.note(" %s: picking 'get' action\n" % f)
741 753 actions[f] = ga0
742 754 continue
743 755 # TODO: Consider other simple actions such as mode changes
744 756 # Handle inefficient democrazy.
745 757 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
746 758 for m, l in sorted(bids.items()):
747 759 for _f, args, msg in l:
748 760 repo.ui.note(' %s -> %s\n' % (msg, m))
749 761 # Pick random action. TODO: Instead, prompt user when resolving
750 762 m, l = bids.items()[0]
751 763 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
752 764 (f, m))
753 765 actions[f] = l[0]
754 766 continue
755 767 repo.ui.note(_('end of auction\n\n'))
756 768
757 769 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
758 770
759 771 if wctx.rev() is None:
760 772 fractions = _forgetremoved(wctx, mctx, branchmerge)
761 773 actions.update(fractions)
762 774
763 775 return actions, diverge, renamedelete
764 776
765 777 def batchremove(repo, actions):
766 778 """apply removes to the working directory
767 779
768 780 yields tuples for progress updates
769 781 """
770 782 verbose = repo.ui.verbose
771 783 unlink = util.unlinkpath
772 784 wjoin = repo.wjoin
773 785 audit = repo.wvfs.audit
774 786 i = 0
775 787 for f, args, msg in actions:
776 788 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
777 789 if verbose:
778 790 repo.ui.note(_("removing %s\n") % f)
779 791 audit(f)
780 792 try:
781 793 unlink(wjoin(f), ignoremissing=True)
782 794 except OSError as inst:
783 795 repo.ui.warn(_("update failed to remove %s: %s!\n") %
784 796 (f, inst.strerror))
785 797 if i == 100:
786 798 yield i, f
787 799 i = 0
788 800 i += 1
789 801 if i > 0:
790 802 yield i, f
791 803
792 804 def batchget(repo, mctx, actions):
793 805 """apply gets to the working directory
794 806
795 807 mctx is the context to get from
796 808
797 809 yields tuples for progress updates
798 810 """
799 811 verbose = repo.ui.verbose
800 812 fctx = mctx.filectx
801 813 wwrite = repo.wwrite
802 814 i = 0
803 815 for f, args, msg in actions:
804 816 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
805 817 if verbose:
806 818 repo.ui.note(_("getting %s\n") % f)
807 819 wwrite(f, fctx(f).data(), args[0])
808 820 if i == 100:
809 821 yield i, f
810 822 i = 0
811 823 i += 1
812 824 if i > 0:
813 825 yield i, f
814 826
815 827 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
816 828 """apply the merge action list to the working directory
817 829
818 830 wctx is the working copy context
819 831 mctx is the context to be merged into the working copy
820 832
821 833 Return a tuple of counts (updated, merged, removed, unresolved) that
822 834 describes how many files were affected by the update.
823 835 """
824 836
825 837 updated, merged, removed, unresolved = 0, 0, 0, 0
826 838 ms = mergestate(repo)
827 839 ms.reset(wctx.p1().node(), mctx.node())
828 840 moves = []
829 841 for m, l in actions.items():
830 842 l.sort()
831 843
832 844 # prescan for merges
833 845 for f, args, msg in actions['m']:
834 846 f1, f2, fa, move, anc = args
835 847 if f == '.hgsubstate': # merged internally
836 848 continue
837 849 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
838 850 fcl = wctx[f1]
839 851 fco = mctx[f2]
840 852 actx = repo[anc]
841 853 if fa in actx:
842 854 fca = actx[fa]
843 855 else:
844 856 fca = repo.filectx(f1, fileid=nullrev)
845 857 ms.add(fcl, fco, fca, f)
846 858 if f1 != f and move:
847 859 moves.append(f1)
848 860
849 861 audit = repo.wvfs.audit
850 862 _updating = _('updating')
851 863 _files = _('files')
852 864 progress = repo.ui.progress
853 865
854 866 # remove renamed files after safely stored
855 867 for f in moves:
856 868 if os.path.lexists(repo.wjoin(f)):
857 869 repo.ui.debug("removing %s\n" % f)
858 870 audit(f)
859 871 util.unlinkpath(repo.wjoin(f))
860 872
861 873 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
862 874
863 875 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
864 876 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
865 877
866 878 # remove in parallel (must come first)
867 879 z = 0
868 880 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
869 881 for i, item in prog:
870 882 z += i
871 883 progress(_updating, z, item=item, total=numupdates, unit=_files)
872 884 removed = len(actions['r'])
873 885
874 886 # get in parallel
875 887 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
876 888 for i, item in prog:
877 889 z += i
878 890 progress(_updating, z, item=item, total=numupdates, unit=_files)
879 891 updated = len(actions['g'])
880 892
881 893 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
882 894 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
883 895
884 896 # forget (manifest only, just log it) (must come first)
885 897 for f, args, msg in actions['f']:
886 898 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
887 899 z += 1
888 900 progress(_updating, z, item=f, total=numupdates, unit=_files)
889 901
890 902 # re-add (manifest only, just log it)
891 903 for f, args, msg in actions['a']:
892 904 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
893 905 z += 1
894 906 progress(_updating, z, item=f, total=numupdates, unit=_files)
895 907
896 908 # keep (noop, just log it)
897 909 for f, args, msg in actions['k']:
898 910 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
899 911 # no progress
900 912
901 913 # directory rename, move local
902 914 for f, args, msg in actions['dm']:
903 915 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
904 916 z += 1
905 917 progress(_updating, z, item=f, total=numupdates, unit=_files)
906 918 f0, flags = args
907 919 repo.ui.note(_("moving %s to %s\n") % (f0, f))
908 920 audit(f)
909 921 repo.wwrite(f, wctx.filectx(f0).data(), flags)
910 922 util.unlinkpath(repo.wjoin(f0))
911 923 updated += 1
912 924
913 925 # local directory rename, get
914 926 for f, args, msg in actions['dg']:
915 927 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
916 928 z += 1
917 929 progress(_updating, z, item=f, total=numupdates, unit=_files)
918 930 f0, flags = args
919 931 repo.ui.note(_("getting %s to %s\n") % (f0, f))
920 932 repo.wwrite(f, mctx.filectx(f0).data(), flags)
921 933 updated += 1
922 934
923 935 # exec
924 936 for f, args, msg in actions['e']:
925 937 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
926 938 z += 1
927 939 progress(_updating, z, item=f, total=numupdates, unit=_files)
928 940 flags, = args
929 941 audit(f)
930 942 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
931 943 updated += 1
932 944
933 945 # premerge
934 946 tocomplete = []
935 947 for f, args, msg in actions['m']:
936 948 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
937 949 z += 1
938 950 progress(_updating, z, item=f, total=numupdates, unit=_files)
939 951 if f == '.hgsubstate': # subrepo states need updating
940 952 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
941 953 overwrite)
942 954 continue
943 955 audit(f)
944 956 complete, r = ms.preresolve(f, wctx, labels=labels)
945 957 if complete:
946 958 if r is not None and r > 0:
947 959 unresolved += 1
948 960 else:
949 961 if r is None:
950 962 updated += 1
951 963 else:
952 964 merged += 1
953 965 else:
954 966 numupdates += 1
955 967 tocomplete.append((f, args, msg))
956 968
957 969 # merge
958 970 for f, args, msg in tocomplete:
959 971 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
960 972 z += 1
961 973 progress(_updating, z, item=f, total=numupdates, unit=_files)
962 974 r = ms.resolve(f, wctx, labels=labels)
963 975 if r is not None and r > 0:
964 976 unresolved += 1
965 977 else:
966 978 if r is None:
967 979 updated += 1
968 980 else:
969 981 merged += 1
970 982
971 983 ms.commit()
972 984 progress(_updating, None, total=numupdates, unit=_files)
973 985
974 986 return updated, merged, removed, unresolved
975 987
976 988 def recordupdates(repo, actions, branchmerge):
977 989 "record merge actions to the dirstate"
978 990 # remove (must come first)
979 991 for f, args, msg in actions['r']:
980 992 if branchmerge:
981 993 repo.dirstate.remove(f)
982 994 else:
983 995 repo.dirstate.drop(f)
984 996
985 997 # forget (must come first)
986 998 for f, args, msg in actions['f']:
987 999 repo.dirstate.drop(f)
988 1000
989 1001 # re-add
990 1002 for f, args, msg in actions['a']:
991 1003 if not branchmerge:
992 1004 repo.dirstate.add(f)
993 1005
994 1006 # exec change
995 1007 for f, args, msg in actions['e']:
996 1008 repo.dirstate.normallookup(f)
997 1009
998 1010 # keep
999 1011 for f, args, msg in actions['k']:
1000 1012 pass
1001 1013
1002 1014 # get
1003 1015 for f, args, msg in actions['g']:
1004 1016 if branchmerge:
1005 1017 repo.dirstate.otherparent(f)
1006 1018 else:
1007 1019 repo.dirstate.normal(f)
1008 1020
1009 1021 # merge
1010 1022 for f, args, msg in actions['m']:
1011 1023 f1, f2, fa, move, anc = args
1012 1024 if branchmerge:
1013 1025 # We've done a branch merge, mark this file as merged
1014 1026 # so that we properly record the merger later
1015 1027 repo.dirstate.merge(f)
1016 1028 if f1 != f2: # copy/rename
1017 1029 if move:
1018 1030 repo.dirstate.remove(f1)
1019 1031 if f1 != f:
1020 1032 repo.dirstate.copy(f1, f)
1021 1033 else:
1022 1034 repo.dirstate.copy(f2, f)
1023 1035 else:
1024 1036 # We've update-merged a locally modified file, so
1025 1037 # we set the dirstate to emulate a normal checkout
1026 1038 # of that file some time in the past. Thus our
1027 1039 # merge will appear as a normal local file
1028 1040 # modification.
1029 1041 if f2 == f: # file not locally copied/moved
1030 1042 repo.dirstate.normallookup(f)
1031 1043 if move:
1032 1044 repo.dirstate.drop(f1)
1033 1045
1034 1046 # directory rename, move local
1035 1047 for f, args, msg in actions['dm']:
1036 1048 f0, flag = args
1037 1049 if branchmerge:
1038 1050 repo.dirstate.add(f)
1039 1051 repo.dirstate.remove(f0)
1040 1052 repo.dirstate.copy(f0, f)
1041 1053 else:
1042 1054 repo.dirstate.normal(f)
1043 1055 repo.dirstate.drop(f0)
1044 1056
1045 1057 # directory rename, get
1046 1058 for f, args, msg in actions['dg']:
1047 1059 f0, flag = args
1048 1060 if branchmerge:
1049 1061 repo.dirstate.add(f)
1050 1062 repo.dirstate.copy(f0, f)
1051 1063 else:
1052 1064 repo.dirstate.normal(f)
1053 1065
1054 1066 def update(repo, node, branchmerge, force, partial, ancestor=None,
1055 1067 mergeancestor=False, labels=None):
1056 1068 """
1057 1069 Perform a merge between the working directory and the given node
1058 1070
1059 1071 node = the node to update to, or None if unspecified
1060 1072 branchmerge = whether to merge between branches
1061 1073 force = whether to force branch merging or file overwriting
1062 1074 partial = a function to filter file lists (dirstate not updated)
1063 1075 mergeancestor = whether it is merging with an ancestor. If true,
1064 1076 we should accept the incoming changes for any prompts that occur.
1065 1077 If false, merging with an ancestor (fast-forward) is only allowed
1066 1078 between different named branches. This flag is used by rebase extension
1067 1079 as a temporary fix and should be avoided in general.
1068 1080
1069 1081 The table below shows all the behaviors of the update command
1070 1082 given the -c and -C or no options, whether the working directory
1071 1083 is dirty, whether a revision is specified, and the relationship of
1072 1084 the parent rev to the target rev (linear, on the same named
1073 1085 branch, or on another named branch).
1074 1086
1075 1087 This logic is tested by test-update-branches.t.
1076 1088
1077 1089 -c -C dirty rev | linear same cross
1078 1090 n n n n | ok (1) x
1079 1091 n n n y | ok ok ok
1080 1092 n n y n | merge (2) (2)
1081 1093 n n y y | merge (3) (3)
1082 1094 n y * * | --- discard ---
1083 1095 y n y * | --- (4) ---
1084 1096 y n n * | --- ok ---
1085 1097 y y * * | --- (5) ---
1086 1098
1087 1099 x = can't happen
1088 1100 * = don't-care
1089 1101 1 = abort: not a linear update (merge or update --check to force update)
1090 1102 2 = abort: uncommitted changes (commit and merge, or update --clean to
1091 1103 discard changes)
1092 1104 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1093 1105 4 = abort: uncommitted changes (checked in commands.py)
1094 1106 5 = incompatible options (checked in commands.py)
1095 1107
1096 1108 Return the same tuple as applyupdates().
1097 1109 """
1098 1110
1099 1111 onode = node
1100 1112 wlock = repo.wlock()
1101 1113 try:
1102 1114 wc = repo[None]
1103 1115 pl = wc.parents()
1104 1116 p1 = pl[0]
1105 1117 pas = [None]
1106 1118 if ancestor is not None:
1107 1119 pas = [repo[ancestor]]
1108 1120
1109 1121 if node is None:
1110 1122 if (repo.ui.configbool('devel', 'all-warnings')
1111 1123 or repo.ui.configbool('devel', 'oldapi')):
1112 1124 repo.ui.develwarn('update with no target')
1113 1125 rev, _mark, _act = destutil.destupdate(repo)
1114 1126 node = repo[rev].node()
1115 1127
1116 1128 overwrite = force and not branchmerge
1117 1129
1118 1130 p2 = repo[node]
1119 1131 if pas[0] is None:
1120 1132 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1121 1133 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1122 1134 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1123 1135 else:
1124 1136 pas = [p1.ancestor(p2, warn=branchmerge)]
1125 1137
1126 1138 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1127 1139
1128 1140 ### check phase
1129 1141 if not overwrite and len(pl) > 1:
1130 1142 raise error.Abort(_("outstanding uncommitted merge"))
1131 1143 if branchmerge:
1132 1144 if pas == [p2]:
1133 1145 raise error.Abort(_("merging with a working directory ancestor"
1134 1146 " has no effect"))
1135 1147 elif pas == [p1]:
1136 1148 if not mergeancestor and p1.branch() == p2.branch():
1137 1149 raise error.Abort(_("nothing to merge"),
1138 1150 hint=_("use 'hg update' "
1139 1151 "or check 'hg heads'"))
1140 1152 if not force and (wc.files() or wc.deleted()):
1141 1153 raise error.Abort(_("uncommitted changes"),
1142 1154 hint=_("use 'hg status' to list changes"))
1143 1155 for s in sorted(wc.substate):
1144 1156 wc.sub(s).bailifchanged()
1145 1157
1146 1158 elif not overwrite:
1147 1159 if p1 == p2: # no-op update
1148 1160 # call the hooks and exit early
1149 1161 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1150 1162 repo.hook('update', parent1=xp2, parent2='', error=0)
1151 1163 return 0, 0, 0, 0
1152 1164
1153 1165 if pas not in ([p1], [p2]): # nonlinear
1154 1166 dirty = wc.dirty(missing=True)
1155 1167 if dirty or onode is None:
1156 1168 # Branching is a bit strange to ensure we do the minimal
1157 1169 # amount of call to obsolete.background.
1158 1170 foreground = obsolete.foreground(repo, [p1.node()])
1159 1171 # note: the <node> variable contains a random identifier
1160 1172 if repo[node].node() in foreground:
1161 1173 pas = [p1] # allow updating to successors
1162 1174 elif dirty:
1163 1175 msg = _("uncommitted changes")
1164 1176 if onode is None:
1165 1177 hint = _("commit and merge, or update --clean to"
1166 1178 " discard changes")
1167 1179 else:
1168 1180 hint = _("commit or update --clean to discard"
1169 1181 " changes")
1170 1182 raise error.Abort(msg, hint=hint)
1171 1183 else: # node is none
1172 1184 msg = _("not a linear update")
1173 1185 hint = _("merge or update --check to force update")
1174 1186 raise error.Abort(msg, hint=hint)
1175 1187 else:
1176 1188 # Allow jumping branches if clean and specific rev given
1177 1189 pas = [p1]
1178 1190
1179 1191 # deprecated config: merge.followcopies
1180 1192 followcopies = False
1181 1193 if overwrite:
1182 1194 pas = [wc]
1183 1195 elif pas == [p2]: # backwards
1184 1196 pas = [wc.p1()]
1185 1197 elif not branchmerge and not wc.dirty(missing=True):
1186 1198 pass
1187 1199 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1188 1200 followcopies = True
1189 1201
1190 1202 ### calculate phase
1191 1203 actionbyfile, diverge, renamedelete = calculateupdates(
1192 1204 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1193 1205 followcopies)
1194 1206 # Convert to dictionary-of-lists format
1195 1207 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1196 1208 for f, (m, args, msg) in actionbyfile.iteritems():
1197 1209 if m not in actions:
1198 1210 actions[m] = []
1199 1211 actions[m].append((f, args, msg))
1200 1212
1201 1213 if not util.checkcase(repo.path):
1202 1214 # check collision between files only in p2 for clean update
1203 1215 if (not branchmerge and
1204 1216 (force or not wc.dirty(missing=True, branch=False))):
1205 1217 _checkcollision(repo, p2.manifest(), None)
1206 1218 else:
1207 1219 _checkcollision(repo, wc.manifest(), actions)
1208 1220
1209 1221 # Prompt and create actions. TODO: Move this towards resolve phase.
1210 1222 for f, args, msg in sorted(actions['cd']):
1211 1223 if repo.ui.promptchoice(
1212 1224 _("local changed %s which remote deleted\n"
1213 1225 "use (c)hanged version or (d)elete?"
1214 1226 "$$ &Changed $$ &Delete") % f, 0):
1215 1227 actions['r'].append((f, None, "prompt delete"))
1216 1228 else:
1217 1229 actions['a'].append((f, None, "prompt keep"))
1218 1230 del actions['cd'][:]
1219 1231
1220 1232 for f, args, msg in sorted(actions['dc']):
1221 1233 flags, = args
1222 1234 if repo.ui.promptchoice(
1223 1235 _("remote changed %s which local deleted\n"
1224 1236 "use (c)hanged version or leave (d)eleted?"
1225 1237 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1226 1238 actions['g'].append((f, (flags,), "prompt recreating"))
1227 1239 del actions['dc'][:]
1228 1240
1229 1241 ### apply phase
1230 1242 if not branchmerge: # just jump to the new rev
1231 1243 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1232 1244 if not partial:
1233 1245 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1234 1246 # note that we're in the middle of an update
1235 1247 repo.vfs.write('updatestate', p2.hex())
1236 1248
1237 1249 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1238 1250
1239 1251 # divergent renames
1240 1252 for f, fl in sorted(diverge.iteritems()):
1241 1253 repo.ui.warn(_("note: possible conflict - %s was renamed "
1242 1254 "multiple times to:\n") % f)
1243 1255 for nf in fl:
1244 1256 repo.ui.warn(" %s\n" % nf)
1245 1257
1246 1258 # rename and delete
1247 1259 for f, fl in sorted(renamedelete.iteritems()):
1248 1260 repo.ui.warn(_("note: possible conflict - %s was deleted "
1249 1261 "and renamed to:\n") % f)
1250 1262 for nf in fl:
1251 1263 repo.ui.warn(" %s\n" % nf)
1252 1264
1253 1265 if not partial:
1254 1266 repo.dirstate.beginparentchange()
1255 1267 repo.setparents(fp1, fp2)
1256 1268 recordupdates(repo, actions, branchmerge)
1257 1269 # update completed, clear state
1258 1270 util.unlink(repo.join('updatestate'))
1259 1271
1260 1272 if not branchmerge:
1261 1273 repo.dirstate.setbranch(p2.branch())
1262 1274 repo.dirstate.endparentchange()
1263 1275 finally:
1264 1276 wlock.release()
1265 1277
1266 1278 if not partial:
1267 1279 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1268 1280 return stats
1269 1281
1270 1282 def graft(repo, ctx, pctx, labels):
1271 1283 """Do a graft-like merge.
1272 1284
1273 1285 This is a merge where the merge ancestor is chosen such that one
1274 1286 or more changesets are grafted onto the current changeset. In
1275 1287 addition to the merge, this fixes up the dirstate to include only
1276 1288 a single parent and tries to duplicate any renames/copies
1277 1289 appropriately.
1278 1290
1279 1291 ctx - changeset to rebase
1280 1292 pctx - merge base, usually ctx.p1()
1281 1293 labels - merge labels eg ['local', 'graft']
1282 1294
1283 1295 """
1284 1296 # If we're grafting a descendant onto an ancestor, be sure to pass
1285 1297 # mergeancestor=True to update. This does two things: 1) allows the merge if
1286 1298 # the destination is the same as the parent of the ctx (so we can use graft
1287 1299 # to copy commits), and 2) informs update that the incoming changes are
1288 1300 # newer than the destination so it doesn't prompt about "remote changed foo
1289 1301 # which local deleted".
1290 1302 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1291 1303
1292 1304 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1293 1305 mergeancestor=mergeancestor, labels=labels)
1294 1306
1295 1307 # drop the second merge parent
1296 1308 repo.dirstate.beginparentchange()
1297 1309 repo.setparents(repo['.'].node(), nullid)
1298 1310 repo.dirstate.write(repo.currenttransaction())
1299 1311 # fix up dirstate for copies and renames
1300 1312 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1301 1313 repo.dirstate.endparentchange()
1302 1314 return stats
General Comments 0
You need to be logged in to leave comments. Login now