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