##// END OF EJS Templates
merge: factor out code to get checkunknown config...
Siddharth Agarwal -
r27740:da5634e1 default
parent child Browse files
Show More
@@ -1,1573 +1,1576 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 def _getcheckunknownconfig(repo, section, name):
561 config = repo.ui.config(section, name, default='abort')
562 valid = ['abort', 'ignore', 'warn']
563 if config not in valid:
564 validstr = ', '.join(["'" + v + "'" for v in valid])
565 raise error.ConfigError(_("%s.%s not valid "
566 "('%s' is none of %s)")
567 % (section, name, config, validstr))
568 return config
569
560 570 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
561 571 if f2 is None:
562 572 f2 = f
563 573 return (repo.wvfs.isfileorlink(f)
564 574 and repo.wvfs.audit.check(f)
565 575 and repo.dirstate.normalize(f) not in repo.dirstate
566 576 and mctx[f2].cmp(wctx[f]))
567 577
568 578 def _checkunknownfiles(repo, wctx, mctx, force, actions):
569 579 """
570 580 Considers any actions that care about the presence of conflicting unknown
571 581 files. For some actions, the result is to abort; for others, it is to
572 582 choose a different action.
573 583 """
574 584 conflicts = set()
575 585 if not force:
576 config = repo.ui.config('merge', 'checkunknown', default='abort')
577 valid = ['abort', 'ignore', 'warn']
578 if config not in valid:
579 validstr = ', '.join(["'" + v + "'" for v in valid])
580 raise error.ConfigError(_("merge.checkunknown not valid "
581 "('%s' is none of %s)")
582 % (config, validstr))
583
586 config = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
584 587 for f, (m, args, msg) in actions.iteritems():
585 588 if m in ('c', 'dc'):
586 589 if _checkunknownfile(repo, wctx, mctx, f):
587 590 conflicts.add(f)
588 591 elif m == 'dg':
589 592 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
590 593 conflicts.add(f)
591 594
592 595 if config == 'abort':
593 596 for f in sorted(conflicts):
594 597 repo.ui.warn(_("%s: untracked file differs\n") % f)
595 598 if conflicts:
596 599 raise error.Abort(_("untracked files in working directory "
597 600 "differ from files in requested revision"))
598 601 elif config == 'warn':
599 602 for f in sorted(conflicts):
600 603 repo.ui.warn(_("%s: replacing untracked file\n") % f)
601 604
602 605 for f, (m, args, msg) in actions.iteritems():
603 606 backup = f in conflicts
604 607 if m == 'c':
605 608 flags, = args
606 609 actions[f] = ('g', (flags, backup), msg)
607 610 elif m == 'cm':
608 611 fl2, anc = args
609 612 different = _checkunknownfile(repo, wctx, mctx, f)
610 613 if different:
611 614 actions[f] = ('m', (f, f, None, False, anc),
612 615 "remote differs from untracked local")
613 616 else:
614 617 actions[f] = ('g', (fl2, backup), "remote created")
615 618
616 619 def _forgetremoved(wctx, mctx, branchmerge):
617 620 """
618 621 Forget removed files
619 622
620 623 If we're jumping between revisions (as opposed to merging), and if
621 624 neither the working directory nor the target rev has the file,
622 625 then we need to remove it from the dirstate, to prevent the
623 626 dirstate from listing the file when it is no longer in the
624 627 manifest.
625 628
626 629 If we're merging, and the other revision has removed a file
627 630 that is not present in the working directory, we need to mark it
628 631 as removed.
629 632 """
630 633
631 634 actions = {}
632 635 m = 'f'
633 636 if branchmerge:
634 637 m = 'r'
635 638 for f in wctx.deleted():
636 639 if f not in mctx:
637 640 actions[f] = m, None, "forget deleted"
638 641
639 642 if not branchmerge:
640 643 for f in wctx.removed():
641 644 if f not in mctx:
642 645 actions[f] = 'f', None, "forget removed"
643 646
644 647 return actions
645 648
646 649 def _checkcollision(repo, wmf, actions):
647 650 # build provisional merged manifest up
648 651 pmmf = set(wmf)
649 652
650 653 if actions:
651 654 # k, dr, e and rd are no-op
652 655 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
653 656 for f, args, msg in actions[m]:
654 657 pmmf.add(f)
655 658 for f, args, msg in actions['r']:
656 659 pmmf.discard(f)
657 660 for f, args, msg in actions['dm']:
658 661 f2, flags = args
659 662 pmmf.discard(f2)
660 663 pmmf.add(f)
661 664 for f, args, msg in actions['dg']:
662 665 pmmf.add(f)
663 666 for f, args, msg in actions['m']:
664 667 f1, f2, fa, move, anc = args
665 668 if move:
666 669 pmmf.discard(f1)
667 670 pmmf.add(f)
668 671
669 672 # check case-folding collision in provisional merged manifest
670 673 foldmap = {}
671 674 for f in sorted(pmmf):
672 675 fold = util.normcase(f)
673 676 if fold in foldmap:
674 677 raise error.Abort(_("case-folding collision between %s and %s")
675 678 % (f, foldmap[fold]))
676 679 foldmap[fold] = f
677 680
678 681 # check case-folding of directories
679 682 foldprefix = unfoldprefix = lastfull = ''
680 683 for fold, f in sorted(foldmap.items()):
681 684 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
682 685 # the folded prefix matches but actual casing is different
683 686 raise error.Abort(_("case-folding collision between "
684 687 "%s and directory of %s") % (lastfull, f))
685 688 foldprefix = fold + '/'
686 689 unfoldprefix = f + '/'
687 690 lastfull = f
688 691
689 692 def driverpreprocess(repo, ms, wctx, labels=None):
690 693 """run the preprocess step of the merge driver, if any
691 694
692 695 This is currently not implemented -- it's an extension point."""
693 696 return True
694 697
695 698 def driverconclude(repo, ms, wctx, labels=None):
696 699 """run the conclude step of the merge driver, if any
697 700
698 701 This is currently not implemented -- it's an extension point."""
699 702 return True
700 703
701 704 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
702 705 acceptremote, followcopies):
703 706 """
704 707 Merge p1 and p2 with ancestor pa and generate merge action list
705 708
706 709 branchmerge and force are as passed in to update
707 710 matcher = matcher to filter file lists
708 711 acceptremote = accept the incoming changes without prompting
709 712 """
710 713 if matcher is not None and matcher.always():
711 714 matcher = None
712 715
713 716 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
714 717
715 718 # manifests fetched in order are going to be faster, so prime the caches
716 719 [x.manifest() for x in
717 720 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
718 721
719 722 if followcopies:
720 723 ret = copies.mergecopies(repo, wctx, p2, pa)
721 724 copy, movewithdir, diverge, renamedelete = ret
722 725
723 726 repo.ui.note(_("resolving manifests\n"))
724 727 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
725 728 % (bool(branchmerge), bool(force), bool(matcher)))
726 729 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
727 730
728 731 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
729 732 copied = set(copy.values())
730 733 copied.update(movewithdir.values())
731 734
732 735 if '.hgsubstate' in m1:
733 736 # check whether sub state is modified
734 737 for s in sorted(wctx.substate):
735 738 if wctx.sub(s).dirty():
736 739 m1['.hgsubstate'] += '+'
737 740 break
738 741
739 742 # Compare manifests
740 743 if matcher is not None:
741 744 m1 = m1.matches(matcher)
742 745 m2 = m2.matches(matcher)
743 746 diff = m1.diff(m2)
744 747
745 748 actions = {}
746 749 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
747 750 if n1 and n2: # file exists on both local and remote side
748 751 if f not in ma:
749 752 fa = copy.get(f, None)
750 753 if fa is not None:
751 754 actions[f] = ('m', (f, f, fa, False, pa.node()),
752 755 "both renamed from " + fa)
753 756 else:
754 757 actions[f] = ('m', (f, f, None, False, pa.node()),
755 758 "both created")
756 759 else:
757 760 a = ma[f]
758 761 fla = ma.flags(f)
759 762 nol = 'l' not in fl1 + fl2 + fla
760 763 if n2 == a and fl2 == fla:
761 764 actions[f] = ('k' , (), "remote unchanged")
762 765 elif n1 == a and fl1 == fla: # local unchanged - use remote
763 766 if n1 == n2: # optimization: keep local content
764 767 actions[f] = ('e', (fl2,), "update permissions")
765 768 else:
766 769 actions[f] = ('g', (fl2, False), "remote is newer")
767 770 elif nol and n2 == a: # remote only changed 'x'
768 771 actions[f] = ('e', (fl2,), "update permissions")
769 772 elif nol and n1 == a: # local only changed 'x'
770 773 actions[f] = ('g', (fl1, False), "remote is newer")
771 774 else: # both changed something
772 775 actions[f] = ('m', (f, f, f, False, pa.node()),
773 776 "versions differ")
774 777 elif n1: # file exists only on local side
775 778 if f in copied:
776 779 pass # we'll deal with it on m2 side
777 780 elif f in movewithdir: # directory rename, move local
778 781 f2 = movewithdir[f]
779 782 if f2 in m2:
780 783 actions[f2] = ('m', (f, f2, None, True, pa.node()),
781 784 "remote directory rename, both created")
782 785 else:
783 786 actions[f2] = ('dm', (f, fl1),
784 787 "remote directory rename - move from " + f)
785 788 elif f in copy:
786 789 f2 = copy[f]
787 790 actions[f] = ('m', (f, f2, f2, False, pa.node()),
788 791 "local copied/moved from " + f2)
789 792 elif f in ma: # clean, a different, no remote
790 793 if n1 != ma[f]:
791 794 if acceptremote:
792 795 actions[f] = ('r', None, "remote delete")
793 796 else:
794 797 actions[f] = ('cd', (f, None, f, False, pa.node()),
795 798 "prompt changed/deleted")
796 799 elif n1[20:] == 'a':
797 800 # This extra 'a' is added by working copy manifest to mark
798 801 # the file as locally added. We should forget it instead of
799 802 # deleting it.
800 803 actions[f] = ('f', None, "remote deleted")
801 804 else:
802 805 actions[f] = ('r', None, "other deleted")
803 806 elif n2: # file exists only on remote side
804 807 if f in copied:
805 808 pass # we'll deal with it on m1 side
806 809 elif f in movewithdir:
807 810 f2 = movewithdir[f]
808 811 if f2 in m1:
809 812 actions[f2] = ('m', (f2, f, None, False, pa.node()),
810 813 "local directory rename, both created")
811 814 else:
812 815 actions[f2] = ('dg', (f, fl2),
813 816 "local directory rename - get from " + f)
814 817 elif f in copy:
815 818 f2 = copy[f]
816 819 if f2 in m2:
817 820 actions[f] = ('m', (f2, f, f2, False, pa.node()),
818 821 "remote copied from " + f2)
819 822 else:
820 823 actions[f] = ('m', (f2, f, f2, True, pa.node()),
821 824 "remote moved from " + f2)
822 825 elif f not in ma:
823 826 # local unknown, remote created: the logic is described by the
824 827 # following table:
825 828 #
826 829 # force branchmerge different | action
827 830 # n * * | create
828 831 # y n * | create
829 832 # y y n | create
830 833 # y y y | merge
831 834 #
832 835 # Checking whether the files are different is expensive, so we
833 836 # don't do that when we can avoid it.
834 837 if not force:
835 838 actions[f] = ('c', (fl2,), "remote created")
836 839 elif not branchmerge:
837 840 actions[f] = ('c', (fl2,), "remote created")
838 841 else:
839 842 actions[f] = ('cm', (fl2, pa.node()),
840 843 "remote created, get or merge")
841 844 elif n2 != ma[f]:
842 845 if acceptremote:
843 846 actions[f] = ('c', (fl2,), "remote recreating")
844 847 else:
845 848 actions[f] = ('dc', (None, f, f, False, pa.node()),
846 849 "prompt deleted/changed")
847 850
848 851 return actions, diverge, renamedelete
849 852
850 853 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
851 854 """Resolves false conflicts where the nodeid changed but the content
852 855 remained the same."""
853 856
854 857 for f, (m, args, msg) in actions.items():
855 858 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
856 859 # local did change but ended up with same content
857 860 actions[f] = 'r', None, "prompt same"
858 861 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
859 862 # remote did change but ended up with same content
860 863 del actions[f] # don't get = keep local deleted
861 864
862 865 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
863 866 acceptremote, followcopies, matcher=None):
864 867 "Calculate the actions needed to merge mctx into wctx using ancestors"
865 868 if len(ancestors) == 1: # default
866 869 actions, diverge, renamedelete = manifestmerge(
867 870 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
868 871 acceptremote, followcopies)
869 872 _checkunknownfiles(repo, wctx, mctx, force, actions)
870 873
871 874 else: # only when merge.preferancestor=* - the default
872 875 repo.ui.note(
873 876 _("note: merging %s and %s using bids from ancestors %s\n") %
874 877 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
875 878
876 879 # Call for bids
877 880 fbids = {} # mapping filename to bids (action method to list af actions)
878 881 diverge, renamedelete = None, None
879 882 for ancestor in ancestors:
880 883 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
881 884 actions, diverge1, renamedelete1 = manifestmerge(
882 885 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
883 886 acceptremote, followcopies)
884 887 _checkunknownfiles(repo, wctx, mctx, force, actions)
885 888
886 889 # Track the shortest set of warning on the theory that bid
887 890 # merge will correctly incorporate more information
888 891 if diverge is None or len(diverge1) < len(diverge):
889 892 diverge = diverge1
890 893 if renamedelete is None or len(renamedelete) < len(renamedelete1):
891 894 renamedelete = renamedelete1
892 895
893 896 for f, a in sorted(actions.iteritems()):
894 897 m, args, msg = a
895 898 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
896 899 if f in fbids:
897 900 d = fbids[f]
898 901 if m in d:
899 902 d[m].append(a)
900 903 else:
901 904 d[m] = [a]
902 905 else:
903 906 fbids[f] = {m: [a]}
904 907
905 908 # Pick the best bid for each file
906 909 repo.ui.note(_('\nauction for merging merge bids\n'))
907 910 actions = {}
908 911 for f, bids in sorted(fbids.items()):
909 912 # bids is a mapping from action method to list af actions
910 913 # Consensus?
911 914 if len(bids) == 1: # all bids are the same kind of method
912 915 m, l = bids.items()[0]
913 916 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
914 917 repo.ui.note(" %s: consensus for %s\n" % (f, m))
915 918 actions[f] = l[0]
916 919 continue
917 920 # If keep is an option, just do it.
918 921 if 'k' in bids:
919 922 repo.ui.note(" %s: picking 'keep' action\n" % f)
920 923 actions[f] = bids['k'][0]
921 924 continue
922 925 # If there are gets and they all agree [how could they not?], do it.
923 926 if 'g' in bids:
924 927 ga0 = bids['g'][0]
925 928 if all(a == ga0 for a in bids['g'][1:]):
926 929 repo.ui.note(" %s: picking 'get' action\n" % f)
927 930 actions[f] = ga0
928 931 continue
929 932 # TODO: Consider other simple actions such as mode changes
930 933 # Handle inefficient democrazy.
931 934 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
932 935 for m, l in sorted(bids.items()):
933 936 for _f, args, msg in l:
934 937 repo.ui.note(' %s -> %s\n' % (msg, m))
935 938 # Pick random action. TODO: Instead, prompt user when resolving
936 939 m, l = bids.items()[0]
937 940 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
938 941 (f, m))
939 942 actions[f] = l[0]
940 943 continue
941 944 repo.ui.note(_('end of auction\n\n'))
942 945
943 946 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
944 947
945 948 if wctx.rev() is None:
946 949 fractions = _forgetremoved(wctx, mctx, branchmerge)
947 950 actions.update(fractions)
948 951
949 952 return actions, diverge, renamedelete
950 953
951 954 def batchremove(repo, actions):
952 955 """apply removes to the working directory
953 956
954 957 yields tuples for progress updates
955 958 """
956 959 verbose = repo.ui.verbose
957 960 unlink = util.unlinkpath
958 961 wjoin = repo.wjoin
959 962 audit = repo.wvfs.audit
960 963 i = 0
961 964 for f, args, msg in actions:
962 965 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
963 966 if verbose:
964 967 repo.ui.note(_("removing %s\n") % f)
965 968 audit(f)
966 969 try:
967 970 unlink(wjoin(f), ignoremissing=True)
968 971 except OSError as inst:
969 972 repo.ui.warn(_("update failed to remove %s: %s!\n") %
970 973 (f, inst.strerror))
971 974 if i == 100:
972 975 yield i, f
973 976 i = 0
974 977 i += 1
975 978 if i > 0:
976 979 yield i, f
977 980
978 981 def batchget(repo, mctx, actions):
979 982 """apply gets to the working directory
980 983
981 984 mctx is the context to get from
982 985
983 986 yields tuples for progress updates
984 987 """
985 988 verbose = repo.ui.verbose
986 989 fctx = mctx.filectx
987 990 wwrite = repo.wwrite
988 991 ui = repo.ui
989 992 i = 0
990 993 for f, (flags, backup), msg in actions:
991 994 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
992 995 if verbose:
993 996 repo.ui.note(_("getting %s\n") % f)
994 997
995 998 if backup:
996 999 absf = repo.wjoin(f)
997 1000 orig = scmutil.origpath(ui, repo, absf)
998 1001 try:
999 1002 # TODO Mercurial has always aborted if an untracked directory
1000 1003 # is replaced by a tracked file, or generally with
1001 1004 # file/directory merges. This needs to be sorted out.
1002 1005 if repo.wvfs.isfileorlink(f):
1003 1006 util.rename(absf, orig)
1004 1007 except OSError as e:
1005 1008 if e.errno != errno.ENOENT:
1006 1009 raise
1007 1010
1008 1011 wwrite(f, fctx(f).data(), flags)
1009 1012 if i == 100:
1010 1013 yield i, f
1011 1014 i = 0
1012 1015 i += 1
1013 1016 if i > 0:
1014 1017 yield i, f
1015 1018
1016 1019 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1017 1020 """apply the merge action list to the working directory
1018 1021
1019 1022 wctx is the working copy context
1020 1023 mctx is the context to be merged into the working copy
1021 1024
1022 1025 Return a tuple of counts (updated, merged, removed, unresolved) that
1023 1026 describes how many files were affected by the update.
1024 1027 """
1025 1028
1026 1029 updated, merged, removed = 0, 0, 0
1027 1030 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
1028 1031 moves = []
1029 1032 for m, l in actions.items():
1030 1033 l.sort()
1031 1034
1032 1035 # 'cd' and 'dc' actions are treated like other merge conflicts
1033 1036 mergeactions = sorted(actions['cd'])
1034 1037 mergeactions.extend(sorted(actions['dc']))
1035 1038 mergeactions.extend(actions['m'])
1036 1039 for f, args, msg in mergeactions:
1037 1040 f1, f2, fa, move, anc = args
1038 1041 if f == '.hgsubstate': # merged internally
1039 1042 continue
1040 1043 if f1 is None:
1041 1044 fcl = filemerge.absentfilectx(wctx, fa)
1042 1045 else:
1043 1046 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1044 1047 fcl = wctx[f1]
1045 1048 if f2 is None:
1046 1049 fco = filemerge.absentfilectx(mctx, fa)
1047 1050 else:
1048 1051 fco = mctx[f2]
1049 1052 actx = repo[anc]
1050 1053 if fa in actx:
1051 1054 fca = actx[fa]
1052 1055 else:
1053 1056 # TODO: move to absentfilectx
1054 1057 fca = repo.filectx(f1, fileid=nullrev)
1055 1058 ms.add(fcl, fco, fca, f)
1056 1059 if f1 != f and move:
1057 1060 moves.append(f1)
1058 1061
1059 1062 audit = repo.wvfs.audit
1060 1063 _updating = _('updating')
1061 1064 _files = _('files')
1062 1065 progress = repo.ui.progress
1063 1066
1064 1067 # remove renamed files after safely stored
1065 1068 for f in moves:
1066 1069 if os.path.lexists(repo.wjoin(f)):
1067 1070 repo.ui.debug("removing %s\n" % f)
1068 1071 audit(f)
1069 1072 util.unlinkpath(repo.wjoin(f))
1070 1073
1071 1074 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1072 1075
1073 1076 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1074 1077 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1075 1078
1076 1079 # remove in parallel (must come first)
1077 1080 z = 0
1078 1081 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1079 1082 for i, item in prog:
1080 1083 z += i
1081 1084 progress(_updating, z, item=item, total=numupdates, unit=_files)
1082 1085 removed = len(actions['r'])
1083 1086
1084 1087 # get in parallel
1085 1088 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1086 1089 for i, item in prog:
1087 1090 z += i
1088 1091 progress(_updating, z, item=item, total=numupdates, unit=_files)
1089 1092 updated = len(actions['g'])
1090 1093
1091 1094 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1092 1095 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1093 1096
1094 1097 # forget (manifest only, just log it) (must come first)
1095 1098 for f, args, msg in actions['f']:
1096 1099 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1097 1100 z += 1
1098 1101 progress(_updating, z, item=f, total=numupdates, unit=_files)
1099 1102
1100 1103 # re-add (manifest only, just log it)
1101 1104 for f, args, msg in actions['a']:
1102 1105 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1103 1106 z += 1
1104 1107 progress(_updating, z, item=f, total=numupdates, unit=_files)
1105 1108
1106 1109 # re-add/mark as modified (manifest only, just log it)
1107 1110 for f, args, msg in actions['am']:
1108 1111 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1109 1112 z += 1
1110 1113 progress(_updating, z, item=f, total=numupdates, unit=_files)
1111 1114
1112 1115 # keep (noop, just log it)
1113 1116 for f, args, msg in actions['k']:
1114 1117 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1115 1118 # no progress
1116 1119
1117 1120 # directory rename, move local
1118 1121 for f, args, msg in actions['dm']:
1119 1122 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1120 1123 z += 1
1121 1124 progress(_updating, z, item=f, total=numupdates, unit=_files)
1122 1125 f0, flags = args
1123 1126 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1124 1127 audit(f)
1125 1128 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1126 1129 util.unlinkpath(repo.wjoin(f0))
1127 1130 updated += 1
1128 1131
1129 1132 # local directory rename, get
1130 1133 for f, args, msg in actions['dg']:
1131 1134 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1132 1135 z += 1
1133 1136 progress(_updating, z, item=f, total=numupdates, unit=_files)
1134 1137 f0, flags = args
1135 1138 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1136 1139 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1137 1140 updated += 1
1138 1141
1139 1142 # exec
1140 1143 for f, args, msg in actions['e']:
1141 1144 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1142 1145 z += 1
1143 1146 progress(_updating, z, item=f, total=numupdates, unit=_files)
1144 1147 flags, = args
1145 1148 audit(f)
1146 1149 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1147 1150 updated += 1
1148 1151
1149 1152 # the ordering is important here -- ms.mergedriver will raise if the merge
1150 1153 # driver has changed, and we want to be able to bypass it when overwrite is
1151 1154 # True
1152 1155 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1153 1156
1154 1157 if usemergedriver:
1155 1158 ms.commit()
1156 1159 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1157 1160 # the driver might leave some files unresolved
1158 1161 unresolvedf = set(ms.unresolved())
1159 1162 if not proceed:
1160 1163 # XXX setting unresolved to at least 1 is a hack to make sure we
1161 1164 # error out
1162 1165 return updated, merged, removed, max(len(unresolvedf), 1)
1163 1166 newactions = []
1164 1167 for f, args, msg in mergeactions:
1165 1168 if f in unresolvedf:
1166 1169 newactions.append((f, args, msg))
1167 1170 mergeactions = newactions
1168 1171
1169 1172 # premerge
1170 1173 tocomplete = []
1171 1174 for f, args, msg in mergeactions:
1172 1175 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1173 1176 z += 1
1174 1177 progress(_updating, z, item=f, total=numupdates, unit=_files)
1175 1178 if f == '.hgsubstate': # subrepo states need updating
1176 1179 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1177 1180 overwrite)
1178 1181 continue
1179 1182 audit(f)
1180 1183 complete, r = ms.preresolve(f, wctx, labels=labels)
1181 1184 if not complete:
1182 1185 numupdates += 1
1183 1186 tocomplete.append((f, args, msg))
1184 1187
1185 1188 # merge
1186 1189 for f, args, msg in tocomplete:
1187 1190 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1188 1191 z += 1
1189 1192 progress(_updating, z, item=f, total=numupdates, unit=_files)
1190 1193 ms.resolve(f, wctx, labels=labels)
1191 1194
1192 1195 ms.commit()
1193 1196
1194 1197 unresolved = ms.unresolvedcount()
1195 1198
1196 1199 if usemergedriver and not unresolved and ms.mdstate() != 's':
1197 1200 if not driverconclude(repo, ms, wctx, labels=labels):
1198 1201 # XXX setting unresolved to at least 1 is a hack to make sure we
1199 1202 # error out
1200 1203 unresolved = max(unresolved, 1)
1201 1204
1202 1205 ms.commit()
1203 1206
1204 1207 msupdated, msmerged, msremoved = ms.counts()
1205 1208 updated += msupdated
1206 1209 merged += msmerged
1207 1210 removed += msremoved
1208 1211
1209 1212 extraactions = ms.actions()
1210 1213 for k, acts in extraactions.iteritems():
1211 1214 actions[k].extend(acts)
1212 1215
1213 1216 progress(_updating, None, total=numupdates, unit=_files)
1214 1217
1215 1218 return updated, merged, removed, unresolved
1216 1219
1217 1220 def recordupdates(repo, actions, branchmerge):
1218 1221 "record merge actions to the dirstate"
1219 1222 # remove (must come first)
1220 1223 for f, args, msg in actions.get('r', []):
1221 1224 if branchmerge:
1222 1225 repo.dirstate.remove(f)
1223 1226 else:
1224 1227 repo.dirstate.drop(f)
1225 1228
1226 1229 # forget (must come first)
1227 1230 for f, args, msg in actions.get('f', []):
1228 1231 repo.dirstate.drop(f)
1229 1232
1230 1233 # re-add
1231 1234 for f, args, msg in actions.get('a', []):
1232 1235 repo.dirstate.add(f)
1233 1236
1234 1237 # re-add/mark as modified
1235 1238 for f, args, msg in actions.get('am', []):
1236 1239 if branchmerge:
1237 1240 repo.dirstate.normallookup(f)
1238 1241 else:
1239 1242 repo.dirstate.add(f)
1240 1243
1241 1244 # exec change
1242 1245 for f, args, msg in actions.get('e', []):
1243 1246 repo.dirstate.normallookup(f)
1244 1247
1245 1248 # keep
1246 1249 for f, args, msg in actions.get('k', []):
1247 1250 pass
1248 1251
1249 1252 # get
1250 1253 for f, args, msg in actions.get('g', []):
1251 1254 if branchmerge:
1252 1255 repo.dirstate.otherparent(f)
1253 1256 else:
1254 1257 repo.dirstate.normal(f)
1255 1258
1256 1259 # merge
1257 1260 for f, args, msg in actions.get('m', []):
1258 1261 f1, f2, fa, move, anc = args
1259 1262 if branchmerge:
1260 1263 # We've done a branch merge, mark this file as merged
1261 1264 # so that we properly record the merger later
1262 1265 repo.dirstate.merge(f)
1263 1266 if f1 != f2: # copy/rename
1264 1267 if move:
1265 1268 repo.dirstate.remove(f1)
1266 1269 if f1 != f:
1267 1270 repo.dirstate.copy(f1, f)
1268 1271 else:
1269 1272 repo.dirstate.copy(f2, f)
1270 1273 else:
1271 1274 # We've update-merged a locally modified file, so
1272 1275 # we set the dirstate to emulate a normal checkout
1273 1276 # of that file some time in the past. Thus our
1274 1277 # merge will appear as a normal local file
1275 1278 # modification.
1276 1279 if f2 == f: # file not locally copied/moved
1277 1280 repo.dirstate.normallookup(f)
1278 1281 if move:
1279 1282 repo.dirstate.drop(f1)
1280 1283
1281 1284 # directory rename, move local
1282 1285 for f, args, msg in actions.get('dm', []):
1283 1286 f0, flag = args
1284 1287 if branchmerge:
1285 1288 repo.dirstate.add(f)
1286 1289 repo.dirstate.remove(f0)
1287 1290 repo.dirstate.copy(f0, f)
1288 1291 else:
1289 1292 repo.dirstate.normal(f)
1290 1293 repo.dirstate.drop(f0)
1291 1294
1292 1295 # directory rename, get
1293 1296 for f, args, msg in actions.get('dg', []):
1294 1297 f0, flag = args
1295 1298 if branchmerge:
1296 1299 repo.dirstate.add(f)
1297 1300 repo.dirstate.copy(f0, f)
1298 1301 else:
1299 1302 repo.dirstate.normal(f)
1300 1303
1301 1304 def update(repo, node, branchmerge, force, ancestor=None,
1302 1305 mergeancestor=False, labels=None, matcher=None):
1303 1306 """
1304 1307 Perform a merge between the working directory and the given node
1305 1308
1306 1309 node = the node to update to, or None if unspecified
1307 1310 branchmerge = whether to merge between branches
1308 1311 force = whether to force branch merging or file overwriting
1309 1312 matcher = a matcher to filter file lists (dirstate not updated)
1310 1313 mergeancestor = whether it is merging with an ancestor. If true,
1311 1314 we should accept the incoming changes for any prompts that occur.
1312 1315 If false, merging with an ancestor (fast-forward) is only allowed
1313 1316 between different named branches. This flag is used by rebase extension
1314 1317 as a temporary fix and should be avoided in general.
1315 1318
1316 1319 The table below shows all the behaviors of the update command
1317 1320 given the -c and -C or no options, whether the working directory
1318 1321 is dirty, whether a revision is specified, and the relationship of
1319 1322 the parent rev to the target rev (linear, on the same named
1320 1323 branch, or on another named branch).
1321 1324
1322 1325 This logic is tested by test-update-branches.t.
1323 1326
1324 1327 -c -C dirty rev | linear same cross
1325 1328 n n n n | ok (1) x
1326 1329 n n n y | ok ok ok
1327 1330 n n y n | merge (2) (2)
1328 1331 n n y y | merge (3) (3)
1329 1332 n y * * | discard discard discard
1330 1333 y n y * | (4) (4) (4)
1331 1334 y n n * | ok ok ok
1332 1335 y y * * | (5) (5) (5)
1333 1336
1334 1337 x = can't happen
1335 1338 * = don't-care
1336 1339 1 = abort: not a linear update (merge or update --check to force update)
1337 1340 2 = abort: uncommitted changes (commit and merge, or update --clean to
1338 1341 discard changes)
1339 1342 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1340 1343 4 = abort: uncommitted changes (checked in commands.py)
1341 1344 5 = incompatible options (checked in commands.py)
1342 1345
1343 1346 Return the same tuple as applyupdates().
1344 1347 """
1345 1348
1346 1349 onode = node
1347 1350 wlock = repo.wlock()
1348 1351 # If we're doing a partial update, we need to skip updating
1349 1352 # the dirstate, so make a note of any partial-ness to the
1350 1353 # update here.
1351 1354 if matcher is None or matcher.always():
1352 1355 partial = False
1353 1356 else:
1354 1357 partial = True
1355 1358 try:
1356 1359 wc = repo[None]
1357 1360 pl = wc.parents()
1358 1361 p1 = pl[0]
1359 1362 pas = [None]
1360 1363 if ancestor is not None:
1361 1364 pas = [repo[ancestor]]
1362 1365
1363 1366 if node is None:
1364 1367 if (repo.ui.configbool('devel', 'all-warnings')
1365 1368 or repo.ui.configbool('devel', 'oldapi')):
1366 1369 repo.ui.develwarn('update with no target')
1367 1370 rev, _mark, _act = destutil.destupdate(repo)
1368 1371 node = repo[rev].node()
1369 1372
1370 1373 overwrite = force and not branchmerge
1371 1374
1372 1375 p2 = repo[node]
1373 1376 if pas[0] is None:
1374 1377 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1375 1378 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1376 1379 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1377 1380 else:
1378 1381 pas = [p1.ancestor(p2, warn=branchmerge)]
1379 1382
1380 1383 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1381 1384
1382 1385 ### check phase
1383 1386 if not overwrite:
1384 1387 if len(pl) > 1:
1385 1388 raise error.Abort(_("outstanding uncommitted merge"))
1386 1389 ms = mergestate.read(repo)
1387 1390 if list(ms.unresolved()):
1388 1391 raise error.Abort(_("outstanding merge conflicts"))
1389 1392 if branchmerge:
1390 1393 if pas == [p2]:
1391 1394 raise error.Abort(_("merging with a working directory ancestor"
1392 1395 " has no effect"))
1393 1396 elif pas == [p1]:
1394 1397 if not mergeancestor and p1.branch() == p2.branch():
1395 1398 raise error.Abort(_("nothing to merge"),
1396 1399 hint=_("use 'hg update' "
1397 1400 "or check 'hg heads'"))
1398 1401 if not force and (wc.files() or wc.deleted()):
1399 1402 raise error.Abort(_("uncommitted changes"),
1400 1403 hint=_("use 'hg status' to list changes"))
1401 1404 for s in sorted(wc.substate):
1402 1405 wc.sub(s).bailifchanged()
1403 1406
1404 1407 elif not overwrite:
1405 1408 if p1 == p2: # no-op update
1406 1409 # call the hooks and exit early
1407 1410 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1408 1411 repo.hook('update', parent1=xp2, parent2='', error=0)
1409 1412 return 0, 0, 0, 0
1410 1413
1411 1414 if pas not in ([p1], [p2]): # nonlinear
1412 1415 dirty = wc.dirty(missing=True)
1413 1416 if dirty or onode is None:
1414 1417 # Branching is a bit strange to ensure we do the minimal
1415 1418 # amount of call to obsolete.background.
1416 1419 foreground = obsolete.foreground(repo, [p1.node()])
1417 1420 # note: the <node> variable contains a random identifier
1418 1421 if repo[node].node() in foreground:
1419 1422 pas = [p1] # allow updating to successors
1420 1423 elif dirty:
1421 1424 msg = _("uncommitted changes")
1422 1425 if onode is None:
1423 1426 hint = _("commit and merge, or update --clean to"
1424 1427 " discard changes")
1425 1428 else:
1426 1429 hint = _("commit or update --clean to discard"
1427 1430 " changes")
1428 1431 raise error.Abort(msg, hint=hint)
1429 1432 else: # node is none
1430 1433 msg = _("not a linear update")
1431 1434 hint = _("merge or update --check to force update")
1432 1435 raise error.Abort(msg, hint=hint)
1433 1436 else:
1434 1437 # Allow jumping branches if clean and specific rev given
1435 1438 pas = [p1]
1436 1439
1437 1440 # deprecated config: merge.followcopies
1438 1441 followcopies = False
1439 1442 if overwrite:
1440 1443 pas = [wc]
1441 1444 elif pas == [p2]: # backwards
1442 1445 pas = [wc.p1()]
1443 1446 elif not branchmerge and not wc.dirty(missing=True):
1444 1447 pass
1445 1448 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1446 1449 followcopies = True
1447 1450
1448 1451 ### calculate phase
1449 1452 actionbyfile, diverge, renamedelete = calculateupdates(
1450 1453 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1451 1454 followcopies, matcher=matcher)
1452 1455 # Convert to dictionary-of-lists format
1453 1456 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1454 1457 for f, (m, args, msg) in actionbyfile.iteritems():
1455 1458 if m not in actions:
1456 1459 actions[m] = []
1457 1460 actions[m].append((f, args, msg))
1458 1461
1459 1462 if not util.checkcase(repo.path):
1460 1463 # check collision between files only in p2 for clean update
1461 1464 if (not branchmerge and
1462 1465 (force or not wc.dirty(missing=True, branch=False))):
1463 1466 _checkcollision(repo, p2.manifest(), None)
1464 1467 else:
1465 1468 _checkcollision(repo, wc.manifest(), actions)
1466 1469
1467 1470 # Prompt and create actions. Most of this is in the resolve phase
1468 1471 # already, but we can't handle .hgsubstate in filemerge or
1469 1472 # subrepo.submerge yet so we have to keep prompting for it.
1470 1473 for f, args, msg in sorted(actions['cd']):
1471 1474 if f != '.hgsubstate':
1472 1475 continue
1473 1476 if repo.ui.promptchoice(
1474 1477 _("local changed %s which remote deleted\n"
1475 1478 "use (c)hanged version or (d)elete?"
1476 1479 "$$ &Changed $$ &Delete") % f, 0):
1477 1480 actions['r'].append((f, None, "prompt delete"))
1478 1481 elif f in p1:
1479 1482 actions['am'].append((f, None, "prompt keep"))
1480 1483 else:
1481 1484 actions['a'].append((f, None, "prompt keep"))
1482 1485
1483 1486 for f, args, msg in sorted(actions['dc']):
1484 1487 if f != '.hgsubstate':
1485 1488 continue
1486 1489 f1, f2, fa, move, anc = args
1487 1490 flags = p2[f2].flags()
1488 1491 if repo.ui.promptchoice(
1489 1492 _("remote changed %s which local deleted\n"
1490 1493 "use (c)hanged version or leave (d)eleted?"
1491 1494 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1492 1495 actions['g'].append((f, (flags, False), "prompt recreating"))
1493 1496
1494 1497 # divergent renames
1495 1498 for f, fl in sorted(diverge.iteritems()):
1496 1499 repo.ui.warn(_("note: possible conflict - %s was renamed "
1497 1500 "multiple times to:\n") % f)
1498 1501 for nf in fl:
1499 1502 repo.ui.warn(" %s\n" % nf)
1500 1503
1501 1504 # rename and delete
1502 1505 for f, fl in sorted(renamedelete.iteritems()):
1503 1506 repo.ui.warn(_("note: possible conflict - %s was deleted "
1504 1507 "and renamed to:\n") % f)
1505 1508 for nf in fl:
1506 1509 repo.ui.warn(" %s\n" % nf)
1507 1510
1508 1511 ### apply phase
1509 1512 if not branchmerge: # just jump to the new rev
1510 1513 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1511 1514 if not partial:
1512 1515 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1513 1516 # note that we're in the middle of an update
1514 1517 repo.vfs.write('updatestate', p2.hex())
1515 1518
1516 1519 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1517 1520
1518 1521 if not partial:
1519 1522 repo.dirstate.beginparentchange()
1520 1523 repo.setparents(fp1, fp2)
1521 1524 recordupdates(repo, actions, branchmerge)
1522 1525 # update completed, clear state
1523 1526 util.unlink(repo.join('updatestate'))
1524 1527
1525 1528 if not branchmerge:
1526 1529 repo.dirstate.setbranch(p2.branch())
1527 1530 repo.dirstate.endparentchange()
1528 1531 finally:
1529 1532 wlock.release()
1530 1533
1531 1534 if not partial:
1532 1535 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1533 1536 return stats
1534 1537
1535 1538 def graft(repo, ctx, pctx, labels, keepparent=False):
1536 1539 """Do a graft-like merge.
1537 1540
1538 1541 This is a merge where the merge ancestor is chosen such that one
1539 1542 or more changesets are grafted onto the current changeset. In
1540 1543 addition to the merge, this fixes up the dirstate to include only
1541 1544 a single parent (if keepparent is False) and tries to duplicate any
1542 1545 renames/copies appropriately.
1543 1546
1544 1547 ctx - changeset to rebase
1545 1548 pctx - merge base, usually ctx.p1()
1546 1549 labels - merge labels eg ['local', 'graft']
1547 1550 keepparent - keep second parent if any
1548 1551
1549 1552 """
1550 1553 # If we're grafting a descendant onto an ancestor, be sure to pass
1551 1554 # mergeancestor=True to update. This does two things: 1) allows the merge if
1552 1555 # the destination is the same as the parent of the ctx (so we can use graft
1553 1556 # to copy commits), and 2) informs update that the incoming changes are
1554 1557 # newer than the destination so it doesn't prompt about "remote changed foo
1555 1558 # which local deleted".
1556 1559 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1557 1560
1558 1561 stats = update(repo, ctx.node(), True, True, pctx.node(),
1559 1562 mergeancestor=mergeancestor, labels=labels)
1560 1563
1561 1564 pother = nullid
1562 1565 parents = ctx.parents()
1563 1566 if keepparent and len(parents) == 2 and pctx in parents:
1564 1567 parents.remove(pctx)
1565 1568 pother = parents[0].node()
1566 1569
1567 1570 repo.dirstate.beginparentchange()
1568 1571 repo.setparents(repo['.'].node(), pother)
1569 1572 repo.dirstate.write(repo.currenttransaction())
1570 1573 # fix up dirstate for copies and renames
1571 1574 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1572 1575 repo.dirstate.endparentchange()
1573 1576 return stats
General Comments 0
You need to be logged in to leave comments. Login now