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