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