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