##// END OF EJS Templates
merge: flush any deferred writes before, and after, running any workers...
Phil Cohen -
r34126:b90e5b2a default
parent child Browse files
Show More
@@ -1,1760 +1,1772 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 hashlib
12 12 import shutil
13 13 import struct
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 addednodeid,
18 18 bin,
19 19 hex,
20 20 modifiednodeid,
21 21 nullhex,
22 22 nullid,
23 23 nullrev,
24 24 )
25 25 from . import (
26 26 copies,
27 27 error,
28 28 filemerge,
29 29 match as matchmod,
30 30 obsutil,
31 31 pycompat,
32 32 scmutil,
33 33 subrepo,
34 34 util,
35 35 worker,
36 36 )
37 37
38 38 _pack = struct.pack
39 39 _unpack = struct.unpack
40 40
41 41 def _droponode(data):
42 42 # used for compatibility for v1
43 43 bits = data.split('\0')
44 44 bits = bits[:-2] + bits[-1:]
45 45 return '\0'.join(bits)
46 46
47 47 class mergestate(object):
48 48 '''track 3-way merge state of individual files
49 49
50 50 The merge state is stored on disk when needed. Two files are used: one with
51 51 an old format (version 1), and one with a new format (version 2). Version 2
52 52 stores a superset of the data in version 1, including new kinds of records
53 53 in the future. For more about the new format, see the documentation for
54 54 `_readrecordsv2`.
55 55
56 56 Each record can contain arbitrary content, and has an associated type. This
57 57 `type` should be a letter. If `type` is uppercase, the record is mandatory:
58 58 versions of Mercurial that don't support it should abort. If `type` is
59 59 lowercase, the record can be safely ignored.
60 60
61 61 Currently known records:
62 62
63 63 L: the node of the "local" part of the merge (hexified version)
64 64 O: the node of the "other" part of the merge (hexified version)
65 65 F: a file to be merged entry
66 66 C: a change/delete or delete/change conflict
67 67 D: a file that the external merge driver will merge internally
68 68 (experimental)
69 69 m: the external merge driver defined for this merge plus its run state
70 70 (experimental)
71 71 f: a (filename, dictionary) tuple of optional values for a given file
72 72 X: unsupported mandatory record type (used in tests)
73 73 x: unsupported advisory record type (used in tests)
74 74 l: the labels for the parts of the merge.
75 75
76 76 Merge driver run states (experimental):
77 77 u: driver-resolved files unmarked -- needs to be run next time we're about
78 78 to resolve or commit
79 79 m: driver-resolved files marked -- only needs to be run before commit
80 80 s: success/skipped -- does not need to be run any more
81 81
82 82 '''
83 83 statepathv1 = 'merge/state'
84 84 statepathv2 = 'merge/state2'
85 85
86 86 @staticmethod
87 87 def clean(repo, node=None, other=None, labels=None):
88 88 """Initialize a brand new merge state, removing any existing state on
89 89 disk."""
90 90 ms = mergestate(repo)
91 91 ms.reset(node, other, labels)
92 92 return ms
93 93
94 94 @staticmethod
95 95 def read(repo):
96 96 """Initialize the merge state, reading it from disk."""
97 97 ms = mergestate(repo)
98 98 ms._read()
99 99 return ms
100 100
101 101 def __init__(self, repo):
102 102 """Initialize the merge state.
103 103
104 104 Do not use this directly! Instead call read() or clean()."""
105 105 self._repo = repo
106 106 self._dirty = False
107 107 self._labels = None
108 108
109 109 def reset(self, node=None, other=None, labels=None):
110 110 self._state = {}
111 111 self._stateextras = {}
112 112 self._local = None
113 113 self._other = None
114 114 self._labels = labels
115 115 for var in ('localctx', 'otherctx'):
116 116 if var in vars(self):
117 117 delattr(self, var)
118 118 if node:
119 119 self._local = node
120 120 self._other = other
121 121 self._readmergedriver = None
122 122 if self.mergedriver:
123 123 self._mdstate = 's'
124 124 else:
125 125 self._mdstate = 'u'
126 126 shutil.rmtree(self._repo.vfs.join('merge'), True)
127 127 self._results = {}
128 128 self._dirty = False
129 129
130 130 def _read(self):
131 131 """Analyse each record content to restore a serialized state from disk
132 132
133 133 This function process "record" entry produced by the de-serialization
134 134 of on disk file.
135 135 """
136 136 self._state = {}
137 137 self._stateextras = {}
138 138 self._local = None
139 139 self._other = None
140 140 for var in ('localctx', 'otherctx'):
141 141 if var in vars(self):
142 142 delattr(self, var)
143 143 self._readmergedriver = None
144 144 self._mdstate = 's'
145 145 unsupported = set()
146 146 records = self._readrecords()
147 147 for rtype, record in records:
148 148 if rtype == 'L':
149 149 self._local = bin(record)
150 150 elif rtype == 'O':
151 151 self._other = bin(record)
152 152 elif rtype == 'm':
153 153 bits = record.split('\0', 1)
154 154 mdstate = bits[1]
155 155 if len(mdstate) != 1 or mdstate not in 'ums':
156 156 # the merge driver should be idempotent, so just rerun it
157 157 mdstate = 'u'
158 158
159 159 self._readmergedriver = bits[0]
160 160 self._mdstate = mdstate
161 161 elif rtype in 'FDC':
162 162 bits = record.split('\0')
163 163 self._state[bits[0]] = bits[1:]
164 164 elif rtype == 'f':
165 165 filename, rawextras = record.split('\0', 1)
166 166 extraparts = rawextras.split('\0')
167 167 extras = {}
168 168 i = 0
169 169 while i < len(extraparts):
170 170 extras[extraparts[i]] = extraparts[i + 1]
171 171 i += 2
172 172
173 173 self._stateextras[filename] = extras
174 174 elif rtype == 'l':
175 175 labels = record.split('\0', 2)
176 176 self._labels = [l for l in labels if len(l) > 0]
177 177 elif not rtype.islower():
178 178 unsupported.add(rtype)
179 179 self._results = {}
180 180 self._dirty = False
181 181
182 182 if unsupported:
183 183 raise error.UnsupportedMergeRecords(unsupported)
184 184
185 185 def _readrecords(self):
186 186 """Read merge state from disk and return a list of record (TYPE, data)
187 187
188 188 We read data from both v1 and v2 files and decide which one to use.
189 189
190 190 V1 has been used by version prior to 2.9.1 and contains less data than
191 191 v2. We read both versions and check if no data in v2 contradicts
192 192 v1. If there is not contradiction we can safely assume that both v1
193 193 and v2 were written at the same time and use the extract data in v2. If
194 194 there is contradiction we ignore v2 content as we assume an old version
195 195 of Mercurial has overwritten the mergestate file and left an old v2
196 196 file around.
197 197
198 198 returns list of record [(TYPE, data), ...]"""
199 199 v1records = self._readrecordsv1()
200 200 v2records = self._readrecordsv2()
201 201 if self._v1v2match(v1records, v2records):
202 202 return v2records
203 203 else:
204 204 # v1 file is newer than v2 file, use it
205 205 # we have to infer the "other" changeset of the merge
206 206 # we cannot do better than that with v1 of the format
207 207 mctx = self._repo[None].parents()[-1]
208 208 v1records.append(('O', mctx.hex()))
209 209 # add place holder "other" file node information
210 210 # nobody is using it yet so we do no need to fetch the data
211 211 # if mctx was wrong `mctx[bits[-2]]` may fails.
212 212 for idx, r in enumerate(v1records):
213 213 if r[0] == 'F':
214 214 bits = r[1].split('\0')
215 215 bits.insert(-2, '')
216 216 v1records[idx] = (r[0], '\0'.join(bits))
217 217 return v1records
218 218
219 219 def _v1v2match(self, v1records, v2records):
220 220 oldv2 = set() # old format version of v2 record
221 221 for rec in v2records:
222 222 if rec[0] == 'L':
223 223 oldv2.add(rec)
224 224 elif rec[0] == 'F':
225 225 # drop the onode data (not contained in v1)
226 226 oldv2.add(('F', _droponode(rec[1])))
227 227 for rec in v1records:
228 228 if rec not in oldv2:
229 229 return False
230 230 else:
231 231 return True
232 232
233 233 def _readrecordsv1(self):
234 234 """read on disk merge state for version 1 file
235 235
236 236 returns list of record [(TYPE, data), ...]
237 237
238 238 Note: the "F" data from this file are one entry short
239 239 (no "other file node" entry)
240 240 """
241 241 records = []
242 242 try:
243 243 f = self._repo.vfs(self.statepathv1)
244 244 for i, l in enumerate(f):
245 245 if i == 0:
246 246 records.append(('L', l[:-1]))
247 247 else:
248 248 records.append(('F', l[:-1]))
249 249 f.close()
250 250 except IOError as err:
251 251 if err.errno != errno.ENOENT:
252 252 raise
253 253 return records
254 254
255 255 def _readrecordsv2(self):
256 256 """read on disk merge state for version 2 file
257 257
258 258 This format is a list of arbitrary records of the form:
259 259
260 260 [type][length][content]
261 261
262 262 `type` is a single character, `length` is a 4 byte integer, and
263 263 `content` is an arbitrary byte sequence of length `length`.
264 264
265 265 Mercurial versions prior to 3.7 have a bug where if there are
266 266 unsupported mandatory merge records, attempting to clear out the merge
267 267 state with hg update --clean or similar aborts. The 't' record type
268 268 works around that by writing out what those versions treat as an
269 269 advisory record, but later versions interpret as special: the first
270 270 character is the 'real' record type and everything onwards is the data.
271 271
272 272 Returns list of records [(TYPE, data), ...]."""
273 273 records = []
274 274 try:
275 275 f = self._repo.vfs(self.statepathv2)
276 276 data = f.read()
277 277 off = 0
278 278 end = len(data)
279 279 while off < end:
280 280 rtype = data[off]
281 281 off += 1
282 282 length = _unpack('>I', data[off:(off + 4)])[0]
283 283 off += 4
284 284 record = data[off:(off + length)]
285 285 off += length
286 286 if rtype == 't':
287 287 rtype, record = record[0], record[1:]
288 288 records.append((rtype, record))
289 289 f.close()
290 290 except IOError as err:
291 291 if err.errno != errno.ENOENT:
292 292 raise
293 293 return records
294 294
295 295 @util.propertycache
296 296 def mergedriver(self):
297 297 # protect against the following:
298 298 # - A configures a malicious merge driver in their hgrc, then
299 299 # pauses the merge
300 300 # - A edits their hgrc to remove references to the merge driver
301 301 # - A gives a copy of their entire repo, including .hg, to B
302 302 # - B inspects .hgrc and finds it to be clean
303 303 # - B then continues the merge and the malicious merge driver
304 304 # gets invoked
305 305 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
306 306 if (self._readmergedriver is not None
307 307 and self._readmergedriver != configmergedriver):
308 308 raise error.ConfigError(
309 309 _("merge driver changed since merge started"),
310 310 hint=_("revert merge driver change or abort merge"))
311 311
312 312 return configmergedriver
313 313
314 314 @util.propertycache
315 315 def localctx(self):
316 316 if self._local is None:
317 317 msg = "localctx accessed but self._local isn't set"
318 318 raise error.ProgrammingError(msg)
319 319 return self._repo[self._local]
320 320
321 321 @util.propertycache
322 322 def otherctx(self):
323 323 if self._other is None:
324 324 msg = "otherctx accessed but self._other isn't set"
325 325 raise error.ProgrammingError(msg)
326 326 return self._repo[self._other]
327 327
328 328 def active(self):
329 329 """Whether mergestate is active.
330 330
331 331 Returns True if there appears to be mergestate. This is a rough proxy
332 332 for "is a merge in progress."
333 333 """
334 334 # Check local variables before looking at filesystem for performance
335 335 # reasons.
336 336 return bool(self._local) or bool(self._state) or \
337 337 self._repo.vfs.exists(self.statepathv1) or \
338 338 self._repo.vfs.exists(self.statepathv2)
339 339
340 340 def commit(self):
341 341 """Write current state on disk (if necessary)"""
342 342 if self._dirty:
343 343 records = self._makerecords()
344 344 self._writerecords(records)
345 345 self._dirty = False
346 346
347 347 def _makerecords(self):
348 348 records = []
349 349 records.append(('L', hex(self._local)))
350 350 records.append(('O', hex(self._other)))
351 351 if self.mergedriver:
352 352 records.append(('m', '\0'.join([
353 353 self.mergedriver, self._mdstate])))
354 354 for d, v in self._state.iteritems():
355 355 if v[0] == 'd':
356 356 records.append(('D', '\0'.join([d] + v)))
357 357 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
358 358 # older versions of Mercurial
359 359 elif v[1] == nullhex or v[6] == nullhex:
360 360 records.append(('C', '\0'.join([d] + v)))
361 361 else:
362 362 records.append(('F', '\0'.join([d] + v)))
363 363 for filename, extras in sorted(self._stateextras.iteritems()):
364 364 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
365 365 extras.iteritems())
366 366 records.append(('f', '%s\0%s' % (filename, rawextras)))
367 367 if self._labels is not None:
368 368 labels = '\0'.join(self._labels)
369 369 records.append(('l', labels))
370 370 return records
371 371
372 372 def _writerecords(self, records):
373 373 """Write current state on disk (both v1 and v2)"""
374 374 self._writerecordsv1(records)
375 375 self._writerecordsv2(records)
376 376
377 377 def _writerecordsv1(self, records):
378 378 """Write current state on disk in a version 1 file"""
379 379 f = self._repo.vfs(self.statepathv1, 'w')
380 380 irecords = iter(records)
381 381 lrecords = next(irecords)
382 382 assert lrecords[0] == 'L'
383 383 f.write(hex(self._local) + '\n')
384 384 for rtype, data in irecords:
385 385 if rtype == 'F':
386 386 f.write('%s\n' % _droponode(data))
387 387 f.close()
388 388
389 389 def _writerecordsv2(self, records):
390 390 """Write current state on disk in a version 2 file
391 391
392 392 See the docstring for _readrecordsv2 for why we use 't'."""
393 393 # these are the records that all version 2 clients can read
394 394 whitelist = 'LOF'
395 395 f = self._repo.vfs(self.statepathv2, 'w')
396 396 for key, data in records:
397 397 assert len(key) == 1
398 398 if key not in whitelist:
399 399 key, data = 't', '%s%s' % (key, data)
400 400 format = '>sI%is' % len(data)
401 401 f.write(_pack(format, key, len(data), data))
402 402 f.close()
403 403
404 404 def add(self, fcl, fco, fca, fd):
405 405 """add a new (potentially?) conflicting file the merge state
406 406 fcl: file context for local,
407 407 fco: file context for remote,
408 408 fca: file context for ancestors,
409 409 fd: file path of the resulting merge.
410 410
411 411 note: also write the local version to the `.hg/merge` directory.
412 412 """
413 413 if fcl.isabsent():
414 414 hash = nullhex
415 415 else:
416 416 hash = hex(hashlib.sha1(fcl.path()).digest())
417 417 self._repo.vfs.write('merge/' + hash, fcl.data())
418 418 self._state[fd] = ['u', hash, fcl.path(),
419 419 fca.path(), hex(fca.filenode()),
420 420 fco.path(), hex(fco.filenode()),
421 421 fcl.flags()]
422 422 self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
423 423 self._dirty = True
424 424
425 425 def __contains__(self, dfile):
426 426 return dfile in self._state
427 427
428 428 def __getitem__(self, dfile):
429 429 return self._state[dfile][0]
430 430
431 431 def __iter__(self):
432 432 return iter(sorted(self._state))
433 433
434 434 def files(self):
435 435 return self._state.keys()
436 436
437 437 def mark(self, dfile, state):
438 438 self._state[dfile][0] = state
439 439 self._dirty = True
440 440
441 441 def mdstate(self):
442 442 return self._mdstate
443 443
444 444 def unresolved(self):
445 445 """Obtain the paths of unresolved files."""
446 446
447 447 for f, entry in self._state.iteritems():
448 448 if entry[0] == 'u':
449 449 yield f
450 450
451 451 def driverresolved(self):
452 452 """Obtain the paths of driver-resolved files."""
453 453
454 454 for f, entry in self._state.items():
455 455 if entry[0] == 'd':
456 456 yield f
457 457
458 458 def extras(self, filename):
459 459 return self._stateextras.setdefault(filename, {})
460 460
461 461 def _resolve(self, preresolve, dfile, wctx):
462 462 """rerun merge process for file path `dfile`"""
463 463 if self[dfile] in 'rd':
464 464 return True, 0
465 465 stateentry = self._state[dfile]
466 466 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
467 467 octx = self._repo[self._other]
468 468 extras = self.extras(dfile)
469 469 anccommitnode = extras.get('ancestorlinknode')
470 470 if anccommitnode:
471 471 actx = self._repo[anccommitnode]
472 472 else:
473 473 actx = None
474 474 fcd = self._filectxorabsent(hash, wctx, dfile)
475 475 fco = self._filectxorabsent(onode, octx, ofile)
476 476 # TODO: move this to filectxorabsent
477 477 fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
478 478 # "premerge" x flags
479 479 flo = fco.flags()
480 480 fla = fca.flags()
481 481 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
482 482 if fca.node() == nullid and flags != flo:
483 483 if preresolve:
484 484 self._repo.ui.warn(
485 485 _('warning: cannot merge flags for %s '
486 486 'without common ancestor - keeping local flags\n')
487 487 % afile)
488 488 elif flags == fla:
489 489 flags = flo
490 490 if preresolve:
491 491 # restore local
492 492 if hash != nullhex:
493 493 f = self._repo.vfs('merge/' + hash)
494 494 wctx[dfile].write(f.read(), flags)
495 495 f.close()
496 496 else:
497 497 wctx[dfile].remove(ignoremissing=True)
498 498 complete, r, deleted = filemerge.premerge(self._repo, wctx,
499 499 self._local, lfile, fcd,
500 500 fco, fca,
501 501 labels=self._labels)
502 502 else:
503 503 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
504 504 self._local, lfile, fcd,
505 505 fco, fca,
506 506 labels=self._labels)
507 507 if r is None:
508 508 # no real conflict
509 509 del self._state[dfile]
510 510 self._stateextras.pop(dfile, None)
511 511 self._dirty = True
512 512 elif not r:
513 513 self.mark(dfile, 'r')
514 514
515 515 if complete:
516 516 action = None
517 517 if deleted:
518 518 if fcd.isabsent():
519 519 # dc: local picked. Need to drop if present, which may
520 520 # happen on re-resolves.
521 521 action = 'f'
522 522 else:
523 523 # cd: remote picked (or otherwise deleted)
524 524 action = 'r'
525 525 else:
526 526 if fcd.isabsent(): # dc: remote picked
527 527 action = 'g'
528 528 elif fco.isabsent(): # cd: local picked
529 529 if dfile in self.localctx:
530 530 action = 'am'
531 531 else:
532 532 action = 'a'
533 533 # else: regular merges (no action necessary)
534 534 self._results[dfile] = r, action
535 535
536 536 return complete, r
537 537
538 538 def _filectxorabsent(self, hexnode, ctx, f):
539 539 if hexnode == nullhex:
540 540 return filemerge.absentfilectx(ctx, f)
541 541 else:
542 542 return ctx[f]
543 543
544 544 def preresolve(self, dfile, wctx):
545 545 """run premerge process for dfile
546 546
547 547 Returns whether the merge is complete, and the exit code."""
548 548 return self._resolve(True, dfile, wctx)
549 549
550 550 def resolve(self, dfile, wctx):
551 551 """run merge process (assuming premerge was run) for dfile
552 552
553 553 Returns the exit code of the merge."""
554 554 return self._resolve(False, dfile, wctx)[1]
555 555
556 556 def counts(self):
557 557 """return counts for updated, merged and removed files in this
558 558 session"""
559 559 updated, merged, removed = 0, 0, 0
560 560 for r, action in self._results.itervalues():
561 561 if r is None:
562 562 updated += 1
563 563 elif r == 0:
564 564 if action == 'r':
565 565 removed += 1
566 566 else:
567 567 merged += 1
568 568 return updated, merged, removed
569 569
570 570 def unresolvedcount(self):
571 571 """get unresolved count for this merge (persistent)"""
572 572 return len(list(self.unresolved()))
573 573
574 574 def actions(self):
575 575 """return lists of actions to perform on the dirstate"""
576 576 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
577 577 for f, (r, action) in self._results.iteritems():
578 578 if action is not None:
579 579 actions[action].append((f, None, "merge result"))
580 580 return actions
581 581
582 582 def recordactions(self):
583 583 """record remove/add/get actions in the dirstate"""
584 584 branchmerge = self._repo.dirstate.p2() != nullid
585 585 recordupdates(self._repo, self.actions(), branchmerge)
586 586
587 587 def queueremove(self, f):
588 588 """queues a file to be removed from the dirstate
589 589
590 590 Meant for use by custom merge drivers."""
591 591 self._results[f] = 0, 'r'
592 592
593 593 def queueadd(self, f):
594 594 """queues a file to be added to the dirstate
595 595
596 596 Meant for use by custom merge drivers."""
597 597 self._results[f] = 0, 'a'
598 598
599 599 def queueget(self, f):
600 600 """queues a file to be marked modified in the dirstate
601 601
602 602 Meant for use by custom merge drivers."""
603 603 self._results[f] = 0, 'g'
604 604
605 605 def _getcheckunknownconfig(repo, section, name):
606 606 config = repo.ui.config(section, name, default='abort')
607 607 valid = ['abort', 'ignore', 'warn']
608 608 if config not in valid:
609 609 validstr = ', '.join(["'" + v + "'" for v in valid])
610 610 raise error.ConfigError(_("%s.%s not valid "
611 611 "('%s' is none of %s)")
612 612 % (section, name, config, validstr))
613 613 return config
614 614
615 615 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
616 616 if f2 is None:
617 617 f2 = f
618 618 return (repo.wvfs.audit.check(f)
619 619 and repo.wvfs.isfileorlink(f)
620 620 and repo.dirstate.normalize(f) not in repo.dirstate
621 621 and mctx[f2].cmp(wctx[f]))
622 622
623 623 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
624 624 """
625 625 Considers any actions that care about the presence of conflicting unknown
626 626 files. For some actions, the result is to abort; for others, it is to
627 627 choose a different action.
628 628 """
629 629 conflicts = set()
630 630 warnconflicts = set()
631 631 abortconflicts = set()
632 632 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
633 633 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
634 634 if not force:
635 635 def collectconflicts(conflicts, config):
636 636 if config == 'abort':
637 637 abortconflicts.update(conflicts)
638 638 elif config == 'warn':
639 639 warnconflicts.update(conflicts)
640 640
641 641 for f, (m, args, msg) in actions.iteritems():
642 642 if m in ('c', 'dc'):
643 643 if _checkunknownfile(repo, wctx, mctx, f):
644 644 conflicts.add(f)
645 645 elif m == 'dg':
646 646 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
647 647 conflicts.add(f)
648 648
649 649 ignoredconflicts = set([c for c in conflicts
650 650 if repo.dirstate._ignore(c)])
651 651 unknownconflicts = conflicts - ignoredconflicts
652 652 collectconflicts(ignoredconflicts, ignoredconfig)
653 653 collectconflicts(unknownconflicts, unknownconfig)
654 654 else:
655 655 for f, (m, args, msg) in actions.iteritems():
656 656 if m == 'cm':
657 657 fl2, anc = args
658 658 different = _checkunknownfile(repo, wctx, mctx, f)
659 659 if repo.dirstate._ignore(f):
660 660 config = ignoredconfig
661 661 else:
662 662 config = unknownconfig
663 663
664 664 # The behavior when force is True is described by this table:
665 665 # config different mergeforce | action backup
666 666 # * n * | get n
667 667 # * y y | merge -
668 668 # abort y n | merge - (1)
669 669 # warn y n | warn + get y
670 670 # ignore y n | get y
671 671 #
672 672 # (1) this is probably the wrong behavior here -- we should
673 673 # probably abort, but some actions like rebases currently
674 674 # don't like an abort happening in the middle of
675 675 # merge.update.
676 676 if not different:
677 677 actions[f] = ('g', (fl2, False), "remote created")
678 678 elif mergeforce or config == 'abort':
679 679 actions[f] = ('m', (f, f, None, False, anc),
680 680 "remote differs from untracked local")
681 681 elif config == 'abort':
682 682 abortconflicts.add(f)
683 683 else:
684 684 if config == 'warn':
685 685 warnconflicts.add(f)
686 686 actions[f] = ('g', (fl2, True), "remote created")
687 687
688 688 for f in sorted(abortconflicts):
689 689 repo.ui.warn(_("%s: untracked file differs\n") % f)
690 690 if abortconflicts:
691 691 raise error.Abort(_("untracked files in working directory "
692 692 "differ from files in requested revision"))
693 693
694 694 for f in sorted(warnconflicts):
695 695 repo.ui.warn(_("%s: replacing untracked file\n") % f)
696 696
697 697 for f, (m, args, msg) in actions.iteritems():
698 698 backup = f in conflicts
699 699 if m == 'c':
700 700 flags, = args
701 701 actions[f] = ('g', (flags, backup), msg)
702 702
703 703 def _forgetremoved(wctx, mctx, branchmerge):
704 704 """
705 705 Forget removed files
706 706
707 707 If we're jumping between revisions (as opposed to merging), and if
708 708 neither the working directory nor the target rev has the file,
709 709 then we need to remove it from the dirstate, to prevent the
710 710 dirstate from listing the file when it is no longer in the
711 711 manifest.
712 712
713 713 If we're merging, and the other revision has removed a file
714 714 that is not present in the working directory, we need to mark it
715 715 as removed.
716 716 """
717 717
718 718 actions = {}
719 719 m = 'f'
720 720 if branchmerge:
721 721 m = 'r'
722 722 for f in wctx.deleted():
723 723 if f not in mctx:
724 724 actions[f] = m, None, "forget deleted"
725 725
726 726 if not branchmerge:
727 727 for f in wctx.removed():
728 728 if f not in mctx:
729 729 actions[f] = 'f', None, "forget removed"
730 730
731 731 return actions
732 732
733 733 def _checkcollision(repo, wmf, actions):
734 734 # build provisional merged manifest up
735 735 pmmf = set(wmf)
736 736
737 737 if actions:
738 738 # k, dr, e and rd are no-op
739 739 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
740 740 for f, args, msg in actions[m]:
741 741 pmmf.add(f)
742 742 for f, args, msg in actions['r']:
743 743 pmmf.discard(f)
744 744 for f, args, msg in actions['dm']:
745 745 f2, flags = args
746 746 pmmf.discard(f2)
747 747 pmmf.add(f)
748 748 for f, args, msg in actions['dg']:
749 749 pmmf.add(f)
750 750 for f, args, msg in actions['m']:
751 751 f1, f2, fa, move, anc = args
752 752 if move:
753 753 pmmf.discard(f1)
754 754 pmmf.add(f)
755 755
756 756 # check case-folding collision in provisional merged manifest
757 757 foldmap = {}
758 758 for f in pmmf:
759 759 fold = util.normcase(f)
760 760 if fold in foldmap:
761 761 raise error.Abort(_("case-folding collision between %s and %s")
762 762 % (f, foldmap[fold]))
763 763 foldmap[fold] = f
764 764
765 765 # check case-folding of directories
766 766 foldprefix = unfoldprefix = lastfull = ''
767 767 for fold, f in sorted(foldmap.items()):
768 768 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
769 769 # the folded prefix matches but actual casing is different
770 770 raise error.Abort(_("case-folding collision between "
771 771 "%s and directory of %s") % (lastfull, f))
772 772 foldprefix = fold + '/'
773 773 unfoldprefix = f + '/'
774 774 lastfull = f
775 775
776 776 def driverpreprocess(repo, ms, wctx, labels=None):
777 777 """run the preprocess step of the merge driver, if any
778 778
779 779 This is currently not implemented -- it's an extension point."""
780 780 return True
781 781
782 782 def driverconclude(repo, ms, wctx, labels=None):
783 783 """run the conclude step of the merge driver, if any
784 784
785 785 This is currently not implemented -- it's an extension point."""
786 786 return True
787 787
788 788 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
789 789 acceptremote, followcopies, forcefulldiff=False):
790 790 """
791 791 Merge wctx and p2 with ancestor pa and generate merge action list
792 792
793 793 branchmerge and force are as passed in to update
794 794 matcher = matcher to filter file lists
795 795 acceptremote = accept the incoming changes without prompting
796 796 """
797 797 if matcher is not None and matcher.always():
798 798 matcher = None
799 799
800 800 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
801 801
802 802 # manifests fetched in order are going to be faster, so prime the caches
803 803 [x.manifest() for x in
804 804 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
805 805
806 806 if followcopies:
807 807 ret = copies.mergecopies(repo, wctx, p2, pa)
808 808 copy, movewithdir, diverge, renamedelete, dirmove = ret
809 809
810 810 boolbm = pycompat.bytestr(bool(branchmerge))
811 811 boolf = pycompat.bytestr(bool(force))
812 812 boolm = pycompat.bytestr(bool(matcher))
813 813 repo.ui.note(_("resolving manifests\n"))
814 814 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
815 815 % (boolbm, boolf, boolm))
816 816 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
817 817
818 818 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
819 819 copied = set(copy.values())
820 820 copied.update(movewithdir.values())
821 821
822 822 if '.hgsubstate' in m1:
823 823 # check whether sub state is modified
824 824 if any(wctx.sub(s).dirty() for s in wctx.substate):
825 825 m1['.hgsubstate'] = modifiednodeid
826 826
827 827 # Don't use m2-vs-ma optimization if:
828 828 # - ma is the same as m1 or m2, which we're just going to diff again later
829 829 # - The caller specifically asks for a full diff, which is useful during bid
830 830 # merge.
831 831 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
832 832 # Identify which files are relevant to the merge, so we can limit the
833 833 # total m1-vs-m2 diff to just those files. This has significant
834 834 # performance benefits in large repositories.
835 835 relevantfiles = set(ma.diff(m2).keys())
836 836
837 837 # For copied and moved files, we need to add the source file too.
838 838 for copykey, copyvalue in copy.iteritems():
839 839 if copyvalue in relevantfiles:
840 840 relevantfiles.add(copykey)
841 841 for movedirkey in movewithdir:
842 842 relevantfiles.add(movedirkey)
843 843 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
844 844 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
845 845
846 846 diff = m1.diff(m2, match=matcher)
847 847
848 848 if matcher is None:
849 849 matcher = matchmod.always('', '')
850 850
851 851 actions = {}
852 852 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
853 853 if n1 and n2: # file exists on both local and remote side
854 854 if f not in ma:
855 855 fa = copy.get(f, None)
856 856 if fa is not None:
857 857 actions[f] = ('m', (f, f, fa, False, pa.node()),
858 858 "both renamed from " + fa)
859 859 else:
860 860 actions[f] = ('m', (f, f, None, False, pa.node()),
861 861 "both created")
862 862 else:
863 863 a = ma[f]
864 864 fla = ma.flags(f)
865 865 nol = 'l' not in fl1 + fl2 + fla
866 866 if n2 == a and fl2 == fla:
867 867 actions[f] = ('k' , (), "remote unchanged")
868 868 elif n1 == a and fl1 == fla: # local unchanged - use remote
869 869 if n1 == n2: # optimization: keep local content
870 870 actions[f] = ('e', (fl2,), "update permissions")
871 871 else:
872 872 actions[f] = ('g', (fl2, False), "remote is newer")
873 873 elif nol and n2 == a: # remote only changed 'x'
874 874 actions[f] = ('e', (fl2,), "update permissions")
875 875 elif nol and n1 == a: # local only changed 'x'
876 876 actions[f] = ('g', (fl1, False), "remote is newer")
877 877 else: # both changed something
878 878 actions[f] = ('m', (f, f, f, False, pa.node()),
879 879 "versions differ")
880 880 elif n1: # file exists only on local side
881 881 if f in copied:
882 882 pass # we'll deal with it on m2 side
883 883 elif f in movewithdir: # directory rename, move local
884 884 f2 = movewithdir[f]
885 885 if f2 in m2:
886 886 actions[f2] = ('m', (f, f2, None, True, pa.node()),
887 887 "remote directory rename, both created")
888 888 else:
889 889 actions[f2] = ('dm', (f, fl1),
890 890 "remote directory rename - move from " + f)
891 891 elif f in copy:
892 892 f2 = copy[f]
893 893 actions[f] = ('m', (f, f2, f2, False, pa.node()),
894 894 "local copied/moved from " + f2)
895 895 elif f in ma: # clean, a different, no remote
896 896 if n1 != ma[f]:
897 897 if acceptremote:
898 898 actions[f] = ('r', None, "remote delete")
899 899 else:
900 900 actions[f] = ('cd', (f, None, f, False, pa.node()),
901 901 "prompt changed/deleted")
902 902 elif n1 == addednodeid:
903 903 # This extra 'a' is added by working copy manifest to mark
904 904 # the file as locally added. We should forget it instead of
905 905 # deleting it.
906 906 actions[f] = ('f', None, "remote deleted")
907 907 else:
908 908 actions[f] = ('r', None, "other deleted")
909 909 elif n2: # file exists only on remote side
910 910 if f in copied:
911 911 pass # we'll deal with it on m1 side
912 912 elif f in movewithdir:
913 913 f2 = movewithdir[f]
914 914 if f2 in m1:
915 915 actions[f2] = ('m', (f2, f, None, False, pa.node()),
916 916 "local directory rename, both created")
917 917 else:
918 918 actions[f2] = ('dg', (f, fl2),
919 919 "local directory rename - get from " + f)
920 920 elif f in copy:
921 921 f2 = copy[f]
922 922 if f2 in m2:
923 923 actions[f] = ('m', (f2, f, f2, False, pa.node()),
924 924 "remote copied from " + f2)
925 925 else:
926 926 actions[f] = ('m', (f2, f, f2, True, pa.node()),
927 927 "remote moved from " + f2)
928 928 elif f not in ma:
929 929 # local unknown, remote created: the logic is described by the
930 930 # following table:
931 931 #
932 932 # force branchmerge different | action
933 933 # n * * | create
934 934 # y n * | create
935 935 # y y n | create
936 936 # y y y | merge
937 937 #
938 938 # Checking whether the files are different is expensive, so we
939 939 # don't do that when we can avoid it.
940 940 if not force:
941 941 actions[f] = ('c', (fl2,), "remote created")
942 942 elif not branchmerge:
943 943 actions[f] = ('c', (fl2,), "remote created")
944 944 else:
945 945 actions[f] = ('cm', (fl2, pa.node()),
946 946 "remote created, get or merge")
947 947 elif n2 != ma[f]:
948 948 df = None
949 949 for d in dirmove:
950 950 if f.startswith(d):
951 951 # new file added in a directory that was moved
952 952 df = dirmove[d] + f[len(d):]
953 953 break
954 954 if df is not None and df in m1:
955 955 actions[df] = ('m', (df, f, f, False, pa.node()),
956 956 "local directory rename - respect move from " + f)
957 957 elif acceptremote:
958 958 actions[f] = ('c', (fl2,), "remote recreating")
959 959 else:
960 960 actions[f] = ('dc', (None, f, f, False, pa.node()),
961 961 "prompt deleted/changed")
962 962
963 963 return actions, diverge, renamedelete
964 964
965 965 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
966 966 """Resolves false conflicts where the nodeid changed but the content
967 967 remained the same."""
968 968
969 969 for f, (m, args, msg) in actions.items():
970 970 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
971 971 # local did change but ended up with same content
972 972 actions[f] = 'r', None, "prompt same"
973 973 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
974 974 # remote did change but ended up with same content
975 975 del actions[f] # don't get = keep local deleted
976 976
977 977 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
978 978 acceptremote, followcopies, matcher=None,
979 979 mergeforce=False):
980 980 """Calculate the actions needed to merge mctx into wctx using ancestors"""
981 981 # Avoid cycle.
982 982 from . import sparse
983 983
984 984 if len(ancestors) == 1: # default
985 985 actions, diverge, renamedelete = manifestmerge(
986 986 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
987 987 acceptremote, followcopies)
988 988 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
989 989
990 990 else: # only when merge.preferancestor=* - the default
991 991 repo.ui.note(
992 992 _("note: merging %s and %s using bids from ancestors %s\n") %
993 993 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
994 994
995 995 # Call for bids
996 996 fbids = {} # mapping filename to bids (action method to list af actions)
997 997 diverge, renamedelete = None, None
998 998 for ancestor in ancestors:
999 999 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1000 1000 actions, diverge1, renamedelete1 = manifestmerge(
1001 1001 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1002 1002 acceptremote, followcopies, forcefulldiff=True)
1003 1003 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1004 1004
1005 1005 # Track the shortest set of warning on the theory that bid
1006 1006 # merge will correctly incorporate more information
1007 1007 if diverge is None or len(diverge1) < len(diverge):
1008 1008 diverge = diverge1
1009 1009 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1010 1010 renamedelete = renamedelete1
1011 1011
1012 1012 for f, a in sorted(actions.iteritems()):
1013 1013 m, args, msg = a
1014 1014 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1015 1015 if f in fbids:
1016 1016 d = fbids[f]
1017 1017 if m in d:
1018 1018 d[m].append(a)
1019 1019 else:
1020 1020 d[m] = [a]
1021 1021 else:
1022 1022 fbids[f] = {m: [a]}
1023 1023
1024 1024 # Pick the best bid for each file
1025 1025 repo.ui.note(_('\nauction for merging merge bids\n'))
1026 1026 actions = {}
1027 1027 dms = [] # filenames that have dm actions
1028 1028 for f, bids in sorted(fbids.items()):
1029 1029 # bids is a mapping from action method to list af actions
1030 1030 # Consensus?
1031 1031 if len(bids) == 1: # all bids are the same kind of method
1032 1032 m, l = bids.items()[0]
1033 1033 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1034 1034 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1035 1035 actions[f] = l[0]
1036 1036 if m == 'dm':
1037 1037 dms.append(f)
1038 1038 continue
1039 1039 # If keep is an option, just do it.
1040 1040 if 'k' in bids:
1041 1041 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1042 1042 actions[f] = bids['k'][0]
1043 1043 continue
1044 1044 # If there are gets and they all agree [how could they not?], do it.
1045 1045 if 'g' in bids:
1046 1046 ga0 = bids['g'][0]
1047 1047 if all(a == ga0 for a in bids['g'][1:]):
1048 1048 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1049 1049 actions[f] = ga0
1050 1050 continue
1051 1051 # TODO: Consider other simple actions such as mode changes
1052 1052 # Handle inefficient democrazy.
1053 1053 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1054 1054 for m, l in sorted(bids.items()):
1055 1055 for _f, args, msg in l:
1056 1056 repo.ui.note(' %s -> %s\n' % (msg, m))
1057 1057 # Pick random action. TODO: Instead, prompt user when resolving
1058 1058 m, l = bids.items()[0]
1059 1059 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1060 1060 (f, m))
1061 1061 actions[f] = l[0]
1062 1062 if m == 'dm':
1063 1063 dms.append(f)
1064 1064 continue
1065 1065 # Work around 'dm' that can cause multiple actions for the same file
1066 1066 for f in dms:
1067 1067 dm, (f0, flags), msg = actions[f]
1068 1068 assert dm == 'dm', dm
1069 1069 if f0 in actions and actions[f0][0] == 'r':
1070 1070 # We have one bid for removing a file and another for moving it.
1071 1071 # These two could be merged as first move and then delete ...
1072 1072 # but instead drop moving and just delete.
1073 1073 del actions[f]
1074 1074 repo.ui.note(_('end of auction\n\n'))
1075 1075
1076 1076 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1077 1077
1078 1078 if wctx.rev() is None:
1079 1079 fractions = _forgetremoved(wctx, mctx, branchmerge)
1080 1080 actions.update(fractions)
1081 1081
1082 1082 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1083 1083 actions)
1084 1084
1085 1085 return prunedactions, diverge, renamedelete
1086 1086
1087 1087 def batchremove(repo, wctx, actions):
1088 1088 """apply removes to the working directory
1089 1089
1090 1090 yields tuples for progress updates
1091 1091 """
1092 1092 verbose = repo.ui.verbose
1093 1093 try:
1094 1094 cwd = pycompat.getcwd()
1095 1095 except OSError as err:
1096 1096 if err.errno != errno.ENOENT:
1097 1097 raise
1098 1098 cwd = None
1099 1099 i = 0
1100 1100 for f, args, msg in actions:
1101 1101 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1102 1102 if verbose:
1103 1103 repo.ui.note(_("removing %s\n") % f)
1104 1104 wctx[f].audit()
1105 1105 try:
1106 1106 wctx[f].remove(ignoremissing=True)
1107 1107 except OSError as inst:
1108 1108 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1109 1109 (f, inst.strerror))
1110 1110 if i == 100:
1111 1111 yield i, f
1112 1112 i = 0
1113 1113 i += 1
1114 1114 if i > 0:
1115 1115 yield i, f
1116 1116 if cwd:
1117 1117 # cwd was present before we started to remove files
1118 1118 # let's check if it is present after we removed them
1119 1119 try:
1120 1120 pycompat.getcwd()
1121 1121 except OSError as err:
1122 1122 if err.errno != errno.ENOENT:
1123 1123 raise
1124 1124 # Print a warning if cwd was deleted
1125 1125 repo.ui.warn(_("current directory was removed\n"
1126 1126 "(consider changing to repo root: %s)\n") %
1127 1127 repo.root)
1128 1128
1129 # It's necessary to flush here in case we're inside a worker fork and will
1130 # quit after this function.
1131 wctx.flushall()
1132
1129 1133 def batchget(repo, mctx, wctx, actions):
1130 1134 """apply gets to the working directory
1131 1135
1132 1136 mctx is the context to get from
1133 1137
1134 1138 yields tuples for progress updates
1135 1139 """
1136 1140 verbose = repo.ui.verbose
1137 1141 fctx = mctx.filectx
1138 1142 ui = repo.ui
1139 1143 i = 0
1140 1144 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1141 1145 for f, (flags, backup), msg in actions:
1142 1146 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1143 1147 if verbose:
1144 1148 repo.ui.note(_("getting %s\n") % f)
1145 1149
1146 1150 if backup:
1147 1151 absf = repo.wjoin(f)
1148 1152 orig = scmutil.origpath(ui, repo, absf)
1149 1153 try:
1150 1154 if repo.wvfs.isfileorlink(f):
1151 1155 util.rename(absf, orig)
1152 1156 except OSError as e:
1153 1157 if e.errno != errno.ENOENT:
1154 1158 raise
1155 1159 wctx[f].clearunknown()
1156 1160 wctx[f].write(fctx(f).data(), flags, backgroundclose=True)
1157 1161 if i == 100:
1158 1162 yield i, f
1159 1163 i = 0
1160 1164 i += 1
1161 1165 if i > 0:
1162 1166 yield i, f
1163 1167
1168 # It's necessary to flush here in case we're inside a worker fork and will
1169 # quit after this function.
1170 wctx.flushall()
1171
1164 1172 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1165 1173 """apply the merge action list to the working directory
1166 1174
1167 1175 wctx is the working copy context
1168 1176 mctx is the context to be merged into the working copy
1169 1177
1170 1178 Return a tuple of counts (updated, merged, removed, unresolved) that
1171 1179 describes how many files were affected by the update.
1172 1180 """
1173 1181
1174 1182 updated, merged, removed = 0, 0, 0
1175 1183 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1176 1184 moves = []
1177 1185 for m, l in actions.items():
1178 1186 l.sort()
1179 1187
1180 1188 # 'cd' and 'dc' actions are treated like other merge conflicts
1181 1189 mergeactions = sorted(actions['cd'])
1182 1190 mergeactions.extend(sorted(actions['dc']))
1183 1191 mergeactions.extend(actions['m'])
1184 1192 for f, args, msg in mergeactions:
1185 1193 f1, f2, fa, move, anc = args
1186 1194 if f == '.hgsubstate': # merged internally
1187 1195 continue
1188 1196 if f1 is None:
1189 1197 fcl = filemerge.absentfilectx(wctx, fa)
1190 1198 else:
1191 1199 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1192 1200 fcl = wctx[f1]
1193 1201 if f2 is None:
1194 1202 fco = filemerge.absentfilectx(mctx, fa)
1195 1203 else:
1196 1204 fco = mctx[f2]
1197 1205 actx = repo[anc]
1198 1206 if fa in actx:
1199 1207 fca = actx[fa]
1200 1208 else:
1201 1209 # TODO: move to absentfilectx
1202 1210 fca = repo.filectx(f1, fileid=nullrev)
1203 1211 ms.add(fcl, fco, fca, f)
1204 1212 if f1 != f and move:
1205 1213 moves.append(f1)
1206 1214
1207 1215 _updating = _('updating')
1208 1216 _files = _('files')
1209 1217 progress = repo.ui.progress
1210 1218
1211 1219 # remove renamed files after safely stored
1212 1220 for f in moves:
1213 1221 if wctx[f].lexists():
1214 1222 repo.ui.debug("removing %s\n" % f)
1215 1223 wctx[f].audit()
1216 1224 wctx[f].remove()
1217 1225
1218 1226 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1219 1227
1220 1228 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1221 1229 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1222 1230
1223 1231 # remove in parallel (must come first)
1224 1232 z = 0
1225 1233 prog = worker.worker(repo.ui, 0.001, batchremove, (repo, wctx),
1226 1234 actions['r'])
1227 1235 for i, item in prog:
1228 1236 z += i
1229 1237 progress(_updating, z, item=item, total=numupdates, unit=_files)
1230 1238 removed = len(actions['r'])
1231 1239
1240 # We should flush before forking into worker processes, since those workers
1241 # flush when they complete, and we don't want to duplicate work.
1242 wctx.flushall()
1243
1232 1244 # get in parallel
1233 1245 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx),
1234 1246 actions['g'])
1235 1247 for i, item in prog:
1236 1248 z += i
1237 1249 progress(_updating, z, item=item, total=numupdates, unit=_files)
1238 1250 updated = len(actions['g'])
1239 1251
1240 1252 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1241 1253 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1242 1254
1243 1255 # forget (manifest only, just log it) (must come first)
1244 1256 for f, args, msg in actions['f']:
1245 1257 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1246 1258 z += 1
1247 1259 progress(_updating, z, item=f, total=numupdates, unit=_files)
1248 1260
1249 1261 # re-add (manifest only, just log it)
1250 1262 for f, args, msg in actions['a']:
1251 1263 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1252 1264 z += 1
1253 1265 progress(_updating, z, item=f, total=numupdates, unit=_files)
1254 1266
1255 1267 # re-add/mark as modified (manifest only, just log it)
1256 1268 for f, args, msg in actions['am']:
1257 1269 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1258 1270 z += 1
1259 1271 progress(_updating, z, item=f, total=numupdates, unit=_files)
1260 1272
1261 1273 # keep (noop, just log it)
1262 1274 for f, args, msg in actions['k']:
1263 1275 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1264 1276 # no progress
1265 1277
1266 1278 # directory rename, move local
1267 1279 for f, args, msg in actions['dm']:
1268 1280 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1269 1281 z += 1
1270 1282 progress(_updating, z, item=f, total=numupdates, unit=_files)
1271 1283 f0, flags = args
1272 1284 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1273 1285 wctx[f].audit()
1274 1286 wctx[f].write(wctx.filectx(f0).data(), flags)
1275 1287 wctx[f0].remove()
1276 1288 updated += 1
1277 1289
1278 1290 # local directory rename, get
1279 1291 for f, args, msg in actions['dg']:
1280 1292 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1281 1293 z += 1
1282 1294 progress(_updating, z, item=f, total=numupdates, unit=_files)
1283 1295 f0, flags = args
1284 1296 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1285 1297 wctx[f].write(mctx.filectx(f0).data(), flags)
1286 1298 updated += 1
1287 1299
1288 1300 # exec
1289 1301 for f, args, msg in actions['e']:
1290 1302 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1291 1303 z += 1
1292 1304 progress(_updating, z, item=f, total=numupdates, unit=_files)
1293 1305 flags, = args
1294 1306 wctx[f].audit()
1295 1307 wctx[f].setflags('l' in flags, 'x' in flags)
1296 1308 updated += 1
1297 1309
1298 1310 # the ordering is important here -- ms.mergedriver will raise if the merge
1299 1311 # driver has changed, and we want to be able to bypass it when overwrite is
1300 1312 # True
1301 1313 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1302 1314
1303 1315 if usemergedriver:
1304 1316 ms.commit()
1305 1317 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1306 1318 # the driver might leave some files unresolved
1307 1319 unresolvedf = set(ms.unresolved())
1308 1320 if not proceed:
1309 1321 # XXX setting unresolved to at least 1 is a hack to make sure we
1310 1322 # error out
1311 1323 return updated, merged, removed, max(len(unresolvedf), 1)
1312 1324 newactions = []
1313 1325 for f, args, msg in mergeactions:
1314 1326 if f in unresolvedf:
1315 1327 newactions.append((f, args, msg))
1316 1328 mergeactions = newactions
1317 1329
1318 1330 # premerge
1319 1331 tocomplete = []
1320 1332 for f, args, msg in mergeactions:
1321 1333 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1322 1334 z += 1
1323 1335 progress(_updating, z, item=f, total=numupdates, unit=_files)
1324 1336 if f == '.hgsubstate': # subrepo states need updating
1325 1337 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1326 1338 overwrite, labels)
1327 1339 continue
1328 1340 wctx[f].audit()
1329 1341 complete, r = ms.preresolve(f, wctx)
1330 1342 if not complete:
1331 1343 numupdates += 1
1332 1344 tocomplete.append((f, args, msg))
1333 1345
1334 1346 # merge
1335 1347 for f, args, msg in tocomplete:
1336 1348 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1337 1349 z += 1
1338 1350 progress(_updating, z, item=f, total=numupdates, unit=_files)
1339 1351 ms.resolve(f, wctx)
1340 1352
1341 1353 ms.commit()
1342 1354
1343 1355 unresolved = ms.unresolvedcount()
1344 1356
1345 1357 if usemergedriver and not unresolved and ms.mdstate() != 's':
1346 1358 if not driverconclude(repo, ms, wctx, labels=labels):
1347 1359 # XXX setting unresolved to at least 1 is a hack to make sure we
1348 1360 # error out
1349 1361 unresolved = max(unresolved, 1)
1350 1362
1351 1363 ms.commit()
1352 1364
1353 1365 msupdated, msmerged, msremoved = ms.counts()
1354 1366 updated += msupdated
1355 1367 merged += msmerged
1356 1368 removed += msremoved
1357 1369
1358 1370 extraactions = ms.actions()
1359 1371 if extraactions:
1360 1372 mfiles = set(a[0] for a in actions['m'])
1361 1373 for k, acts in extraactions.iteritems():
1362 1374 actions[k].extend(acts)
1363 1375 # Remove these files from actions['m'] as well. This is important
1364 1376 # because in recordupdates, files in actions['m'] are processed
1365 1377 # after files in other actions, and the merge driver might add
1366 1378 # files to those actions via extraactions above. This can lead to a
1367 1379 # file being recorded twice, with poor results. This is especially
1368 1380 # problematic for actions['r'] (currently only possible with the
1369 1381 # merge driver in the initial merge process; interrupted merges
1370 1382 # don't go through this flow).
1371 1383 #
1372 1384 # The real fix here is to have indexes by both file and action so
1373 1385 # that when the action for a file is changed it is automatically
1374 1386 # reflected in the other action lists. But that involves a more
1375 1387 # complex data structure, so this will do for now.
1376 1388 #
1377 1389 # We don't need to do the same operation for 'dc' and 'cd' because
1378 1390 # those lists aren't consulted again.
1379 1391 mfiles.difference_update(a[0] for a in acts)
1380 1392
1381 1393 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1382 1394
1383 1395 progress(_updating, None, total=numupdates, unit=_files)
1384 1396
1385 1397 return updated, merged, removed, unresolved
1386 1398
1387 1399 def recordupdates(repo, actions, branchmerge):
1388 1400 "record merge actions to the dirstate"
1389 1401 # remove (must come first)
1390 1402 for f, args, msg in actions.get('r', []):
1391 1403 if branchmerge:
1392 1404 repo.dirstate.remove(f)
1393 1405 else:
1394 1406 repo.dirstate.drop(f)
1395 1407
1396 1408 # forget (must come first)
1397 1409 for f, args, msg in actions.get('f', []):
1398 1410 repo.dirstate.drop(f)
1399 1411
1400 1412 # re-add
1401 1413 for f, args, msg in actions.get('a', []):
1402 1414 repo.dirstate.add(f)
1403 1415
1404 1416 # re-add/mark as modified
1405 1417 for f, args, msg in actions.get('am', []):
1406 1418 if branchmerge:
1407 1419 repo.dirstate.normallookup(f)
1408 1420 else:
1409 1421 repo.dirstate.add(f)
1410 1422
1411 1423 # exec change
1412 1424 for f, args, msg in actions.get('e', []):
1413 1425 repo.dirstate.normallookup(f)
1414 1426
1415 1427 # keep
1416 1428 for f, args, msg in actions.get('k', []):
1417 1429 pass
1418 1430
1419 1431 # get
1420 1432 for f, args, msg in actions.get('g', []):
1421 1433 if branchmerge:
1422 1434 repo.dirstate.otherparent(f)
1423 1435 else:
1424 1436 repo.dirstate.normal(f)
1425 1437
1426 1438 # merge
1427 1439 for f, args, msg in actions.get('m', []):
1428 1440 f1, f2, fa, move, anc = args
1429 1441 if branchmerge:
1430 1442 # We've done a branch merge, mark this file as merged
1431 1443 # so that we properly record the merger later
1432 1444 repo.dirstate.merge(f)
1433 1445 if f1 != f2: # copy/rename
1434 1446 if move:
1435 1447 repo.dirstate.remove(f1)
1436 1448 if f1 != f:
1437 1449 repo.dirstate.copy(f1, f)
1438 1450 else:
1439 1451 repo.dirstate.copy(f2, f)
1440 1452 else:
1441 1453 # We've update-merged a locally modified file, so
1442 1454 # we set the dirstate to emulate a normal checkout
1443 1455 # of that file some time in the past. Thus our
1444 1456 # merge will appear as a normal local file
1445 1457 # modification.
1446 1458 if f2 == f: # file not locally copied/moved
1447 1459 repo.dirstate.normallookup(f)
1448 1460 if move:
1449 1461 repo.dirstate.drop(f1)
1450 1462
1451 1463 # directory rename, move local
1452 1464 for f, args, msg in actions.get('dm', []):
1453 1465 f0, flag = args
1454 1466 if branchmerge:
1455 1467 repo.dirstate.add(f)
1456 1468 repo.dirstate.remove(f0)
1457 1469 repo.dirstate.copy(f0, f)
1458 1470 else:
1459 1471 repo.dirstate.normal(f)
1460 1472 repo.dirstate.drop(f0)
1461 1473
1462 1474 # directory rename, get
1463 1475 for f, args, msg in actions.get('dg', []):
1464 1476 f0, flag = args
1465 1477 if branchmerge:
1466 1478 repo.dirstate.add(f)
1467 1479 repo.dirstate.copy(f0, f)
1468 1480 else:
1469 1481 repo.dirstate.normal(f)
1470 1482
1471 1483 def update(repo, node, branchmerge, force, ancestor=None,
1472 1484 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1473 1485 updatecheck=None):
1474 1486 """
1475 1487 Perform a merge between the working directory and the given node
1476 1488
1477 1489 node = the node to update to
1478 1490 branchmerge = whether to merge between branches
1479 1491 force = whether to force branch merging or file overwriting
1480 1492 matcher = a matcher to filter file lists (dirstate not updated)
1481 1493 mergeancestor = whether it is merging with an ancestor. If true,
1482 1494 we should accept the incoming changes for any prompts that occur.
1483 1495 If false, merging with an ancestor (fast-forward) is only allowed
1484 1496 between different named branches. This flag is used by rebase extension
1485 1497 as a temporary fix and should be avoided in general.
1486 1498 labels = labels to use for base, local and other
1487 1499 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1488 1500 this is True, then 'force' should be True as well.
1489 1501
1490 1502 The table below shows all the behaviors of the update command
1491 1503 given the -c and -C or no options, whether the working directory
1492 1504 is dirty, whether a revision is specified, and the relationship of
1493 1505 the parent rev to the target rev (linear or not). Match from top first. The
1494 1506 -n option doesn't exist on the command line, but represents the
1495 1507 experimental.updatecheck=noconflict option.
1496 1508
1497 1509 This logic is tested by test-update-branches.t.
1498 1510
1499 1511 -c -C -n -m dirty rev linear | result
1500 1512 y y * * * * * | (1)
1501 1513 y * y * * * * | (1)
1502 1514 y * * y * * * | (1)
1503 1515 * y y * * * * | (1)
1504 1516 * y * y * * * | (1)
1505 1517 * * y y * * * | (1)
1506 1518 * * * * * n n | x
1507 1519 * * * * n * * | ok
1508 1520 n n n n y * y | merge
1509 1521 n n n n y y n | (2)
1510 1522 n n n y y * * | merge
1511 1523 n n y n y * * | merge if no conflict
1512 1524 n y n n y * * | discard
1513 1525 y n n n y * * | (3)
1514 1526
1515 1527 x = can't happen
1516 1528 * = don't-care
1517 1529 1 = incompatible options (checked in commands.py)
1518 1530 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1519 1531 3 = abort: uncommitted changes (checked in commands.py)
1520 1532
1521 1533 Return the same tuple as applyupdates().
1522 1534 """
1523 1535 # Avoid cycle.
1524 1536 from . import sparse
1525 1537
1526 1538 # This function used to find the default destination if node was None, but
1527 1539 # that's now in destutil.py.
1528 1540 assert node is not None
1529 1541 if not branchmerge and not force:
1530 1542 # TODO: remove the default once all callers that pass branchmerge=False
1531 1543 # and force=False pass a value for updatecheck. We may want to allow
1532 1544 # updatecheck='abort' to better suppport some of these callers.
1533 1545 if updatecheck is None:
1534 1546 updatecheck = 'linear'
1535 1547 assert updatecheck in ('none', 'linear', 'noconflict')
1536 1548 # If we're doing a partial update, we need to skip updating
1537 1549 # the dirstate, so make a note of any partial-ness to the
1538 1550 # update here.
1539 1551 if matcher is None or matcher.always():
1540 1552 partial = False
1541 1553 else:
1542 1554 partial = True
1543 1555 with repo.wlock():
1544 1556 wc = repo[None]
1545 1557 pl = wc.parents()
1546 1558 p1 = pl[0]
1547 1559 pas = [None]
1548 1560 if ancestor is not None:
1549 1561 pas = [repo[ancestor]]
1550 1562
1551 1563 overwrite = force and not branchmerge
1552 1564
1553 1565 p2 = repo[node]
1554 1566 if pas[0] is None:
1555 1567 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1556 1568 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1557 1569 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1558 1570 else:
1559 1571 pas = [p1.ancestor(p2, warn=branchmerge)]
1560 1572
1561 1573 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1562 1574
1563 1575 ### check phase
1564 1576 if not overwrite:
1565 1577 if len(pl) > 1:
1566 1578 raise error.Abort(_("outstanding uncommitted merge"))
1567 1579 ms = mergestate.read(repo)
1568 1580 if list(ms.unresolved()):
1569 1581 raise error.Abort(_("outstanding merge conflicts"))
1570 1582 if branchmerge:
1571 1583 if pas == [p2]:
1572 1584 raise error.Abort(_("merging with a working directory ancestor"
1573 1585 " has no effect"))
1574 1586 elif pas == [p1]:
1575 1587 if not mergeancestor and wc.branch() == p2.branch():
1576 1588 raise error.Abort(_("nothing to merge"),
1577 1589 hint=_("use 'hg update' "
1578 1590 "or check 'hg heads'"))
1579 1591 if not force and (wc.files() or wc.deleted()):
1580 1592 raise error.Abort(_("uncommitted changes"),
1581 1593 hint=_("use 'hg status' to list changes"))
1582 1594 for s in sorted(wc.substate):
1583 1595 wc.sub(s).bailifchanged()
1584 1596
1585 1597 elif not overwrite:
1586 1598 if p1 == p2: # no-op update
1587 1599 # call the hooks and exit early
1588 1600 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1589 1601 repo.hook('update', parent1=xp2, parent2='', error=0)
1590 1602 return 0, 0, 0, 0
1591 1603
1592 1604 if (updatecheck == 'linear' and
1593 1605 pas not in ([p1], [p2])): # nonlinear
1594 1606 dirty = wc.dirty(missing=True)
1595 1607 if dirty:
1596 1608 # Branching is a bit strange to ensure we do the minimal
1597 1609 # amount of call to obsutil.foreground.
1598 1610 foreground = obsutil.foreground(repo, [p1.node()])
1599 1611 # note: the <node> variable contains a random identifier
1600 1612 if repo[node].node() in foreground:
1601 1613 pass # allow updating to successors
1602 1614 else:
1603 1615 msg = _("uncommitted changes")
1604 1616 hint = _("commit or update --clean to discard changes")
1605 1617 raise error.UpdateAbort(msg, hint=hint)
1606 1618 else:
1607 1619 # Allow jumping branches if clean and specific rev given
1608 1620 pass
1609 1621
1610 1622 if overwrite:
1611 1623 pas = [wc]
1612 1624 elif not branchmerge:
1613 1625 pas = [p1]
1614 1626
1615 1627 # deprecated config: merge.followcopies
1616 1628 followcopies = repo.ui.configbool('merge', 'followcopies')
1617 1629 if overwrite:
1618 1630 followcopies = False
1619 1631 elif not pas[0]:
1620 1632 followcopies = False
1621 1633 if not branchmerge and not wc.dirty(missing=True):
1622 1634 followcopies = False
1623 1635
1624 1636 ### calculate phase
1625 1637 actionbyfile, diverge, renamedelete = calculateupdates(
1626 1638 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1627 1639 followcopies, matcher=matcher, mergeforce=mergeforce)
1628 1640
1629 1641 if updatecheck == 'noconflict':
1630 1642 for f, (m, args, msg) in actionbyfile.iteritems():
1631 1643 if m not in ('g', 'k', 'e', 'r'):
1632 1644 msg = _("conflicting changes")
1633 1645 hint = _("commit or update --clean to discard changes")
1634 1646 raise error.Abort(msg, hint=hint)
1635 1647
1636 1648 # Prompt and create actions. Most of this is in the resolve phase
1637 1649 # already, but we can't handle .hgsubstate in filemerge or
1638 1650 # subrepo.submerge yet so we have to keep prompting for it.
1639 1651 if '.hgsubstate' in actionbyfile:
1640 1652 f = '.hgsubstate'
1641 1653 m, args, msg = actionbyfile[f]
1642 1654 prompts = filemerge.partextras(labels)
1643 1655 prompts['f'] = f
1644 1656 if m == 'cd':
1645 1657 if repo.ui.promptchoice(
1646 1658 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1647 1659 "use (c)hanged version or (d)elete?"
1648 1660 "$$ &Changed $$ &Delete") % prompts, 0):
1649 1661 actionbyfile[f] = ('r', None, "prompt delete")
1650 1662 elif f in p1:
1651 1663 actionbyfile[f] = ('am', None, "prompt keep")
1652 1664 else:
1653 1665 actionbyfile[f] = ('a', None, "prompt keep")
1654 1666 elif m == 'dc':
1655 1667 f1, f2, fa, move, anc = args
1656 1668 flags = p2[f2].flags()
1657 1669 if repo.ui.promptchoice(
1658 1670 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1659 1671 "use (c)hanged version or leave (d)eleted?"
1660 1672 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1661 1673 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1662 1674 else:
1663 1675 del actionbyfile[f]
1664 1676
1665 1677 # Convert to dictionary-of-lists format
1666 1678 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1667 1679 for f, (m, args, msg) in actionbyfile.iteritems():
1668 1680 if m not in actions:
1669 1681 actions[m] = []
1670 1682 actions[m].append((f, args, msg))
1671 1683
1672 1684 if not util.fscasesensitive(repo.path):
1673 1685 # check collision between files only in p2 for clean update
1674 1686 if (not branchmerge and
1675 1687 (force or not wc.dirty(missing=True, branch=False))):
1676 1688 _checkcollision(repo, p2.manifest(), None)
1677 1689 else:
1678 1690 _checkcollision(repo, wc.manifest(), actions)
1679 1691
1680 1692 # divergent renames
1681 1693 for f, fl in sorted(diverge.iteritems()):
1682 1694 repo.ui.warn(_("note: possible conflict - %s was renamed "
1683 1695 "multiple times to:\n") % f)
1684 1696 for nf in fl:
1685 1697 repo.ui.warn(" %s\n" % nf)
1686 1698
1687 1699 # rename and delete
1688 1700 for f, fl in sorted(renamedelete.iteritems()):
1689 1701 repo.ui.warn(_("note: possible conflict - %s was deleted "
1690 1702 "and renamed to:\n") % f)
1691 1703 for nf in fl:
1692 1704 repo.ui.warn(" %s\n" % nf)
1693 1705
1694 1706 ### apply phase
1695 1707 if not branchmerge: # just jump to the new rev
1696 1708 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1697 1709 if not partial:
1698 1710 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1699 1711 # note that we're in the middle of an update
1700 1712 repo.vfs.write('updatestate', p2.hex())
1701 1713
1702 1714 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1703 1715
1704 1716 if not partial:
1705 1717 with repo.dirstate.parentchange():
1706 1718 repo.setparents(fp1, fp2)
1707 1719 recordupdates(repo, actions, branchmerge)
1708 1720 # update completed, clear state
1709 1721 util.unlink(repo.vfs.join('updatestate'))
1710 1722
1711 1723 if not branchmerge:
1712 1724 repo.dirstate.setbranch(p2.branch())
1713 1725
1714 1726 # If we're updating to a location, clean up any stale temporary includes
1715 1727 # (ex: this happens during hg rebase --abort).
1716 1728 if not branchmerge:
1717 1729 sparse.prunetemporaryincludes(repo)
1718 1730
1719 1731 if not partial:
1720 1732 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1721 1733 return stats
1722 1734
1723 1735 def graft(repo, ctx, pctx, labels, keepparent=False):
1724 1736 """Do a graft-like merge.
1725 1737
1726 1738 This is a merge where the merge ancestor is chosen such that one
1727 1739 or more changesets are grafted onto the current changeset. In
1728 1740 addition to the merge, this fixes up the dirstate to include only
1729 1741 a single parent (if keepparent is False) and tries to duplicate any
1730 1742 renames/copies appropriately.
1731 1743
1732 1744 ctx - changeset to rebase
1733 1745 pctx - merge base, usually ctx.p1()
1734 1746 labels - merge labels eg ['local', 'graft']
1735 1747 keepparent - keep second parent if any
1736 1748
1737 1749 """
1738 1750 # If we're grafting a descendant onto an ancestor, be sure to pass
1739 1751 # mergeancestor=True to update. This does two things: 1) allows the merge if
1740 1752 # the destination is the same as the parent of the ctx (so we can use graft
1741 1753 # to copy commits), and 2) informs update that the incoming changes are
1742 1754 # newer than the destination so it doesn't prompt about "remote changed foo
1743 1755 # which local deleted".
1744 1756 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1745 1757
1746 1758 stats = update(repo, ctx.node(), True, True, pctx.node(),
1747 1759 mergeancestor=mergeancestor, labels=labels)
1748 1760
1749 1761 pother = nullid
1750 1762 parents = ctx.parents()
1751 1763 if keepparent and len(parents) == 2 and pctx in parents:
1752 1764 parents.remove(pctx)
1753 1765 pother = parents[0].node()
1754 1766
1755 1767 with repo.dirstate.parentchange():
1756 1768 repo.setparents(repo['.'].node(), pother)
1757 1769 repo.dirstate.write(repo.currenttransaction())
1758 1770 # fix up dirstate for copies and renames
1759 1771 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1760 1772 return stats
General Comments 0
You need to be logged in to leave comments. Login now