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