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