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