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