##// END OF EJS Templates
merge.mergestate._resolve: also return completed status...
Siddharth Agarwal -
r26616:2f1fce0d default
parent child Browse files
Show More
@@ -1,1196 +1,1196 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 nullid,
20 20 nullrev,
21 21 )
22 22 from . import (
23 23 copies,
24 24 destutil,
25 25 error,
26 26 filemerge,
27 27 obsolete,
28 28 subrepo,
29 29 util,
30 30 worker,
31 31 )
32 32
33 33 _pack = struct.pack
34 34 _unpack = struct.unpack
35 35
36 36 def _droponode(data):
37 37 # used for compatibility for v1
38 38 bits = data.split('\0')
39 39 bits = bits[:-2] + bits[-1:]
40 40 return '\0'.join(bits)
41 41
42 42 class mergestate(object):
43 43 '''track 3-way merge state of individual files
44 44
45 45 it is stored on disk when needed. Two file are used, one with an old
46 46 format, one with a new format. Both contains similar data, but the new
47 47 format can store new kinds of field.
48 48
49 49 Current new format is a list of arbitrary record of the form:
50 50
51 51 [type][length][content]
52 52
53 53 Type is a single character, length is a 4 bytes integer, content is an
54 54 arbitrary suites of bytes of length `length`.
55 55
56 56 Type should be a letter. Capital letter are mandatory record, Mercurial
57 57 should abort if they are unknown. lower case record can be safely ignored.
58 58
59 59 Currently known record:
60 60
61 61 L: the node of the "local" part of the merge (hexified version)
62 62 O: the node of the "other" part of the merge (hexified version)
63 63 F: a file to be merged entry
64 64 '''
65 65 statepathv1 = 'merge/state'
66 66 statepathv2 = 'merge/state2'
67 67
68 68 def __init__(self, repo):
69 69 self._repo = repo
70 70 self._dirty = False
71 71 self._read()
72 72
73 73 def reset(self, node=None, other=None):
74 74 self._state = {}
75 75 self._local = None
76 76 self._other = None
77 77 if node:
78 78 self._local = node
79 79 self._other = other
80 80 shutil.rmtree(self._repo.join('merge'), True)
81 81 self._dirty = False
82 82
83 83 def _read(self):
84 84 """Analyse each record content to restore a serialized state from disk
85 85
86 86 This function process "record" entry produced by the de-serialization
87 87 of on disk file.
88 88 """
89 89 self._state = {}
90 90 self._local = None
91 91 self._other = None
92 92 records = self._readrecords()
93 93 for rtype, record in records:
94 94 if rtype == 'L':
95 95 self._local = bin(record)
96 96 elif rtype == 'O':
97 97 self._other = bin(record)
98 98 elif rtype == 'F':
99 99 bits = record.split('\0')
100 100 self._state[bits[0]] = bits[1:]
101 101 elif not rtype.islower():
102 102 raise error.Abort(_('unsupported merge state record: %s')
103 103 % rtype)
104 104 self._dirty = False
105 105
106 106 def _readrecords(self):
107 107 """Read merge state from disk and return a list of record (TYPE, data)
108 108
109 109 We read data from both v1 and v2 files and decide which one to use.
110 110
111 111 V1 has been used by version prior to 2.9.1 and contains less data than
112 112 v2. We read both versions and check if no data in v2 contradicts
113 113 v1. If there is not contradiction we can safely assume that both v1
114 114 and v2 were written at the same time and use the extract data in v2. If
115 115 there is contradiction we ignore v2 content as we assume an old version
116 116 of Mercurial has overwritten the mergestate file and left an old v2
117 117 file around.
118 118
119 119 returns list of record [(TYPE, data), ...]"""
120 120 v1records = self._readrecordsv1()
121 121 v2records = self._readrecordsv2()
122 122 if self._v1v2match(v1records, v2records):
123 123 return v2records
124 124 else:
125 125 # v1 file is newer than v2 file, use it
126 126 # we have to infer the "other" changeset of the merge
127 127 # we cannot do better than that with v1 of the format
128 128 mctx = self._repo[None].parents()[-1]
129 129 v1records.append(('O', mctx.hex()))
130 130 # add place holder "other" file node information
131 131 # nobody is using it yet so we do no need to fetch the data
132 132 # if mctx was wrong `mctx[bits[-2]]` may fails.
133 133 for idx, r in enumerate(v1records):
134 134 if r[0] == 'F':
135 135 bits = r[1].split('\0')
136 136 bits.insert(-2, '')
137 137 v1records[idx] = (r[0], '\0'.join(bits))
138 138 return v1records
139 139
140 140 def _v1v2match(self, v1records, v2records):
141 141 oldv2 = set() # old format version of v2 record
142 142 for rec in v2records:
143 143 if rec[0] == 'L':
144 144 oldv2.add(rec)
145 145 elif rec[0] == 'F':
146 146 # drop the onode data (not contained in v1)
147 147 oldv2.add(('F', _droponode(rec[1])))
148 148 for rec in v1records:
149 149 if rec not in oldv2:
150 150 return False
151 151 else:
152 152 return True
153 153
154 154 def _readrecordsv1(self):
155 155 """read on disk merge state for version 1 file
156 156
157 157 returns list of record [(TYPE, data), ...]
158 158
159 159 Note: the "F" data from this file are one entry short
160 160 (no "other file node" entry)
161 161 """
162 162 records = []
163 163 try:
164 164 f = self._repo.vfs(self.statepathv1)
165 165 for i, l in enumerate(f):
166 166 if i == 0:
167 167 records.append(('L', l[:-1]))
168 168 else:
169 169 records.append(('F', l[:-1]))
170 170 f.close()
171 171 except IOError as err:
172 172 if err.errno != errno.ENOENT:
173 173 raise
174 174 return records
175 175
176 176 def _readrecordsv2(self):
177 177 """read on disk merge state for version 2 file
178 178
179 179 returns list of record [(TYPE, data), ...]
180 180 """
181 181 records = []
182 182 try:
183 183 f = self._repo.vfs(self.statepathv2)
184 184 data = f.read()
185 185 off = 0
186 186 end = len(data)
187 187 while off < end:
188 188 rtype = data[off]
189 189 off += 1
190 190 length = _unpack('>I', data[off:(off + 4)])[0]
191 191 off += 4
192 192 record = data[off:(off + length)]
193 193 off += length
194 194 records.append((rtype, record))
195 195 f.close()
196 196 except IOError as err:
197 197 if err.errno != errno.ENOENT:
198 198 raise
199 199 return records
200 200
201 201 def active(self):
202 202 """Whether mergestate is active.
203 203
204 204 Returns True if there appears to be mergestate. This is a rough proxy
205 205 for "is a merge in progress."
206 206 """
207 207 # Check local variables before looking at filesystem for performance
208 208 # reasons.
209 209 return bool(self._local) or bool(self._state) or \
210 210 self._repo.vfs.exists(self.statepathv1) or \
211 211 self._repo.vfs.exists(self.statepathv2)
212 212
213 213 def commit(self):
214 214 """Write current state on disk (if necessary)"""
215 215 if self._dirty:
216 216 records = []
217 217 records.append(('L', hex(self._local)))
218 218 records.append(('O', hex(self._other)))
219 219 for d, v in self._state.iteritems():
220 220 records.append(('F', '\0'.join([d] + v)))
221 221 self._writerecords(records)
222 222 self._dirty = False
223 223
224 224 def _writerecords(self, records):
225 225 """Write current state on disk (both v1 and v2)"""
226 226 self._writerecordsv1(records)
227 227 self._writerecordsv2(records)
228 228
229 229 def _writerecordsv1(self, records):
230 230 """Write current state on disk in a version 1 file"""
231 231 f = self._repo.vfs(self.statepathv1, 'w')
232 232 irecords = iter(records)
233 233 lrecords = irecords.next()
234 234 assert lrecords[0] == 'L'
235 235 f.write(hex(self._local) + '\n')
236 236 for rtype, data in irecords:
237 237 if rtype == 'F':
238 238 f.write('%s\n' % _droponode(data))
239 239 f.close()
240 240
241 241 def _writerecordsv2(self, records):
242 242 """Write current state on disk in a version 2 file"""
243 243 f = self._repo.vfs(self.statepathv2, 'w')
244 244 for key, data in records:
245 245 assert len(key) == 1
246 246 format = '>sI%is' % len(data)
247 247 f.write(_pack(format, key, len(data), data))
248 248 f.close()
249 249
250 250 def add(self, fcl, fco, fca, fd):
251 251 """add a new (potentially?) conflicting file the merge state
252 252 fcl: file context for local,
253 253 fco: file context for remote,
254 254 fca: file context for ancestors,
255 255 fd: file path of the resulting merge.
256 256
257 257 note: also write the local version to the `.hg/merge` directory.
258 258 """
259 259 hash = util.sha1(fcl.path()).hexdigest()
260 260 self._repo.vfs.write('merge/' + hash, fcl.data())
261 261 self._state[fd] = ['u', hash, fcl.path(),
262 262 fca.path(), hex(fca.filenode()),
263 263 fco.path(), hex(fco.filenode()),
264 264 fcl.flags()]
265 265 self._dirty = True
266 266
267 267 def __contains__(self, dfile):
268 268 return dfile in self._state
269 269
270 270 def __getitem__(self, dfile):
271 271 return self._state[dfile][0]
272 272
273 273 def __iter__(self):
274 274 return iter(sorted(self._state))
275 275
276 276 def files(self):
277 277 return self._state.keys()
278 278
279 279 def mark(self, dfile, state):
280 280 self._state[dfile][0] = state
281 281 self._dirty = True
282 282
283 283 def unresolved(self):
284 284 """Obtain the paths of unresolved files."""
285 285
286 286 for f, entry in self._state.items():
287 287 if entry[0] == 'u':
288 288 yield f
289 289
290 290 def _resolve(self, dfile, wctx, labels=None):
291 291 """rerun merge process for file path `dfile`"""
292 292 if self[dfile] == 'r':
293 return 0
293 return True, 0
294 294 stateentry = self._state[dfile]
295 295 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
296 296 octx = self._repo[self._other]
297 297 fcd = wctx[dfile]
298 298 fco = octx[ofile]
299 299 fca = self._repo.filectx(afile, fileid=anode)
300 300 # "premerge" x flags
301 301 flo = fco.flags()
302 302 fla = fca.flags()
303 303 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
304 304 if fca.node() == nullid:
305 305 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
306 306 afile)
307 307 elif flags == fla:
308 308 flags = flo
309 309 # restore local
310 310 f = self._repo.vfs('merge/' + hash)
311 311 self._repo.wwrite(dfile, f.read(), flags)
312 312 f.close()
313 313 complete, r = filemerge.premerge(self._repo, self._local, lfile, fcd,
314 314 fco, fca, labels=labels)
315 315 if not complete:
316 316 complete, r = filemerge.filemerge(self._repo, self._local, lfile,
317 317 fcd, fco, fca, labels=labels)
318 318 if r is None:
319 319 # no real conflict
320 320 del self._state[dfile]
321 321 self._dirty = True
322 322 elif not r:
323 323 self.mark(dfile, 'r')
324 return r
324 return complete, r
325 325
326 326 def resolve(self, dfile, wctx, labels=None):
327 327 """rerun merge process for file path `dfile`"""
328 return self._resolve(dfile, wctx, labels=labels)
328 return self._resolve(dfile, wctx, labels=labels)[1]
329 329
330 330 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
331 331 if f2 is None:
332 332 f2 = f
333 333 return (os.path.isfile(repo.wjoin(f))
334 334 and repo.wvfs.audit.check(f)
335 335 and repo.dirstate.normalize(f) not in repo.dirstate
336 336 and mctx[f2].cmp(wctx[f]))
337 337
338 338 def _checkunknownfiles(repo, wctx, mctx, force, actions):
339 339 """
340 340 Considers any actions that care about the presence of conflicting unknown
341 341 files. For some actions, the result is to abort; for others, it is to
342 342 choose a different action.
343 343 """
344 344 aborts = []
345 345 if not force:
346 346 for f, (m, args, msg) in actions.iteritems():
347 347 if m in ('c', 'dc'):
348 348 if _checkunknownfile(repo, wctx, mctx, f):
349 349 aborts.append(f)
350 350 elif m == 'dg':
351 351 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
352 352 aborts.append(f)
353 353
354 354 for f in sorted(aborts):
355 355 repo.ui.warn(_("%s: untracked file differs\n") % f)
356 356 if aborts:
357 357 raise error.Abort(_("untracked files in working directory differ "
358 358 "from files in requested revision"))
359 359
360 360 for f, (m, args, msg) in actions.iteritems():
361 361 if m == 'c':
362 362 actions[f] = ('g', args, msg)
363 363 elif m == 'cm':
364 364 fl2, anc = args
365 365 different = _checkunknownfile(repo, wctx, mctx, f)
366 366 if different:
367 367 actions[f] = ('m', (f, f, None, False, anc),
368 368 "remote differs from untracked local")
369 369 else:
370 370 actions[f] = ('g', (fl2,), "remote created")
371 371
372 372 def _forgetremoved(wctx, mctx, branchmerge):
373 373 """
374 374 Forget removed files
375 375
376 376 If we're jumping between revisions (as opposed to merging), and if
377 377 neither the working directory nor the target rev has the file,
378 378 then we need to remove it from the dirstate, to prevent the
379 379 dirstate from listing the file when it is no longer in the
380 380 manifest.
381 381
382 382 If we're merging, and the other revision has removed a file
383 383 that is not present in the working directory, we need to mark it
384 384 as removed.
385 385 """
386 386
387 387 actions = {}
388 388 m = 'f'
389 389 if branchmerge:
390 390 m = 'r'
391 391 for f in wctx.deleted():
392 392 if f not in mctx:
393 393 actions[f] = m, None, "forget deleted"
394 394
395 395 if not branchmerge:
396 396 for f in wctx.removed():
397 397 if f not in mctx:
398 398 actions[f] = 'f', None, "forget removed"
399 399
400 400 return actions
401 401
402 402 def _checkcollision(repo, wmf, actions):
403 403 # build provisional merged manifest up
404 404 pmmf = set(wmf)
405 405
406 406 if actions:
407 407 # k, dr, e and rd are no-op
408 408 for m in 'a', 'f', 'g', 'cd', 'dc':
409 409 for f, args, msg in actions[m]:
410 410 pmmf.add(f)
411 411 for f, args, msg in actions['r']:
412 412 pmmf.discard(f)
413 413 for f, args, msg in actions['dm']:
414 414 f2, flags = args
415 415 pmmf.discard(f2)
416 416 pmmf.add(f)
417 417 for f, args, msg in actions['dg']:
418 418 pmmf.add(f)
419 419 for f, args, msg in actions['m']:
420 420 f1, f2, fa, move, anc = args
421 421 if move:
422 422 pmmf.discard(f1)
423 423 pmmf.add(f)
424 424
425 425 # check case-folding collision in provisional merged manifest
426 426 foldmap = {}
427 427 for f in sorted(pmmf):
428 428 fold = util.normcase(f)
429 429 if fold in foldmap:
430 430 raise error.Abort(_("case-folding collision between %s and %s")
431 431 % (f, foldmap[fold]))
432 432 foldmap[fold] = f
433 433
434 434 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
435 435 acceptremote, followcopies):
436 436 """
437 437 Merge p1 and p2 with ancestor pa and generate merge action list
438 438
439 439 branchmerge and force are as passed in to update
440 440 partial = function to filter file lists
441 441 acceptremote = accept the incoming changes without prompting
442 442 """
443 443
444 444 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
445 445
446 446 # manifests fetched in order are going to be faster, so prime the caches
447 447 [x.manifest() for x in
448 448 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
449 449
450 450 if followcopies:
451 451 ret = copies.mergecopies(repo, wctx, p2, pa)
452 452 copy, movewithdir, diverge, renamedelete = ret
453 453
454 454 repo.ui.note(_("resolving manifests\n"))
455 455 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
456 456 % (bool(branchmerge), bool(force), bool(partial)))
457 457 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
458 458
459 459 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
460 460 copied = set(copy.values())
461 461 copied.update(movewithdir.values())
462 462
463 463 if '.hgsubstate' in m1:
464 464 # check whether sub state is modified
465 465 for s in sorted(wctx.substate):
466 466 if wctx.sub(s).dirty():
467 467 m1['.hgsubstate'] += '+'
468 468 break
469 469
470 470 # Compare manifests
471 471 diff = m1.diff(m2)
472 472
473 473 actions = {}
474 474 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
475 475 if partial and not partial(f):
476 476 continue
477 477 if n1 and n2: # file exists on both local and remote side
478 478 if f not in ma:
479 479 fa = copy.get(f, None)
480 480 if fa is not None:
481 481 actions[f] = ('m', (f, f, fa, False, pa.node()),
482 482 "both renamed from " + fa)
483 483 else:
484 484 actions[f] = ('m', (f, f, None, False, pa.node()),
485 485 "both created")
486 486 else:
487 487 a = ma[f]
488 488 fla = ma.flags(f)
489 489 nol = 'l' not in fl1 + fl2 + fla
490 490 if n2 == a and fl2 == fla:
491 491 actions[f] = ('k' , (), "remote unchanged")
492 492 elif n1 == a and fl1 == fla: # local unchanged - use remote
493 493 if n1 == n2: # optimization: keep local content
494 494 actions[f] = ('e', (fl2,), "update permissions")
495 495 else:
496 496 actions[f] = ('g', (fl2,), "remote is newer")
497 497 elif nol and n2 == a: # remote only changed 'x'
498 498 actions[f] = ('e', (fl2,), "update permissions")
499 499 elif nol and n1 == a: # local only changed 'x'
500 500 actions[f] = ('g', (fl1,), "remote is newer")
501 501 else: # both changed something
502 502 actions[f] = ('m', (f, f, f, False, pa.node()),
503 503 "versions differ")
504 504 elif n1: # file exists only on local side
505 505 if f in copied:
506 506 pass # we'll deal with it on m2 side
507 507 elif f in movewithdir: # directory rename, move local
508 508 f2 = movewithdir[f]
509 509 if f2 in m2:
510 510 actions[f2] = ('m', (f, f2, None, True, pa.node()),
511 511 "remote directory rename, both created")
512 512 else:
513 513 actions[f2] = ('dm', (f, fl1),
514 514 "remote directory rename - move from " + f)
515 515 elif f in copy:
516 516 f2 = copy[f]
517 517 actions[f] = ('m', (f, f2, f2, False, pa.node()),
518 518 "local copied/moved from " + f2)
519 519 elif f in ma: # clean, a different, no remote
520 520 if n1 != ma[f]:
521 521 if acceptremote:
522 522 actions[f] = ('r', None, "remote delete")
523 523 else:
524 524 actions[f] = ('cd', None, "prompt changed/deleted")
525 525 elif n1[20:] == 'a':
526 526 # This extra 'a' is added by working copy manifest to mark
527 527 # the file as locally added. We should forget it instead of
528 528 # deleting it.
529 529 actions[f] = ('f', None, "remote deleted")
530 530 else:
531 531 actions[f] = ('r', None, "other deleted")
532 532 elif n2: # file exists only on remote side
533 533 if f in copied:
534 534 pass # we'll deal with it on m1 side
535 535 elif f in movewithdir:
536 536 f2 = movewithdir[f]
537 537 if f2 in m1:
538 538 actions[f2] = ('m', (f2, f, None, False, pa.node()),
539 539 "local directory rename, both created")
540 540 else:
541 541 actions[f2] = ('dg', (f, fl2),
542 542 "local directory rename - get from " + f)
543 543 elif f in copy:
544 544 f2 = copy[f]
545 545 if f2 in m2:
546 546 actions[f] = ('m', (f2, f, f2, False, pa.node()),
547 547 "remote copied from " + f2)
548 548 else:
549 549 actions[f] = ('m', (f2, f, f2, True, pa.node()),
550 550 "remote moved from " + f2)
551 551 elif f not in ma:
552 552 # local unknown, remote created: the logic is described by the
553 553 # following table:
554 554 #
555 555 # force branchmerge different | action
556 556 # n * * | create
557 557 # y n * | create
558 558 # y y n | create
559 559 # y y y | merge
560 560 #
561 561 # Checking whether the files are different is expensive, so we
562 562 # don't do that when we can avoid it.
563 563 if not force:
564 564 actions[f] = ('c', (fl2,), "remote created")
565 565 elif not branchmerge:
566 566 actions[f] = ('c', (fl2,), "remote created")
567 567 else:
568 568 actions[f] = ('cm', (fl2, pa.node()),
569 569 "remote created, get or merge")
570 570 elif n2 != ma[f]:
571 571 if acceptremote:
572 572 actions[f] = ('c', (fl2,), "remote recreating")
573 573 else:
574 574 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
575 575
576 576 return actions, diverge, renamedelete
577 577
578 578 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
579 579 """Resolves false conflicts where the nodeid changed but the content
580 580 remained the same."""
581 581
582 582 for f, (m, args, msg) in actions.items():
583 583 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
584 584 # local did change but ended up with same content
585 585 actions[f] = 'r', None, "prompt same"
586 586 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
587 587 # remote did change but ended up with same content
588 588 del actions[f] # don't get = keep local deleted
589 589
590 590 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
591 591 acceptremote, followcopies):
592 592 "Calculate the actions needed to merge mctx into wctx using ancestors"
593 593
594 594 if len(ancestors) == 1: # default
595 595 actions, diverge, renamedelete = manifestmerge(
596 596 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
597 597 acceptremote, followcopies)
598 598 _checkunknownfiles(repo, wctx, mctx, force, actions)
599 599
600 600 else: # only when merge.preferancestor=* - the default
601 601 repo.ui.note(
602 602 _("note: merging %s and %s using bids from ancestors %s\n") %
603 603 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
604 604
605 605 # Call for bids
606 606 fbids = {} # mapping filename to bids (action method to list af actions)
607 607 diverge, renamedelete = None, None
608 608 for ancestor in ancestors:
609 609 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
610 610 actions, diverge1, renamedelete1 = manifestmerge(
611 611 repo, wctx, mctx, ancestor, branchmerge, force, partial,
612 612 acceptremote, followcopies)
613 613 _checkunknownfiles(repo, wctx, mctx, force, actions)
614 614
615 615 # Track the shortest set of warning on the theory that bid
616 616 # merge will correctly incorporate more information
617 617 if diverge is None or len(diverge1) < len(diverge):
618 618 diverge = diverge1
619 619 if renamedelete is None or len(renamedelete) < len(renamedelete1):
620 620 renamedelete = renamedelete1
621 621
622 622 for f, a in sorted(actions.iteritems()):
623 623 m, args, msg = a
624 624 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
625 625 if f in fbids:
626 626 d = fbids[f]
627 627 if m in d:
628 628 d[m].append(a)
629 629 else:
630 630 d[m] = [a]
631 631 else:
632 632 fbids[f] = {m: [a]}
633 633
634 634 # Pick the best bid for each file
635 635 repo.ui.note(_('\nauction for merging merge bids\n'))
636 636 actions = {}
637 637 for f, bids in sorted(fbids.items()):
638 638 # bids is a mapping from action method to list af actions
639 639 # Consensus?
640 640 if len(bids) == 1: # all bids are the same kind of method
641 641 m, l = bids.items()[0]
642 642 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
643 643 repo.ui.note(" %s: consensus for %s\n" % (f, m))
644 644 actions[f] = l[0]
645 645 continue
646 646 # If keep is an option, just do it.
647 647 if 'k' in bids:
648 648 repo.ui.note(" %s: picking 'keep' action\n" % f)
649 649 actions[f] = bids['k'][0]
650 650 continue
651 651 # If there are gets and they all agree [how could they not?], do it.
652 652 if 'g' in bids:
653 653 ga0 = bids['g'][0]
654 654 if all(a == ga0 for a in bids['g'][1:]):
655 655 repo.ui.note(" %s: picking 'get' action\n" % f)
656 656 actions[f] = ga0
657 657 continue
658 658 # TODO: Consider other simple actions such as mode changes
659 659 # Handle inefficient democrazy.
660 660 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
661 661 for m, l in sorted(bids.items()):
662 662 for _f, args, msg in l:
663 663 repo.ui.note(' %s -> %s\n' % (msg, m))
664 664 # Pick random action. TODO: Instead, prompt user when resolving
665 665 m, l = bids.items()[0]
666 666 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
667 667 (f, m))
668 668 actions[f] = l[0]
669 669 continue
670 670 repo.ui.note(_('end of auction\n\n'))
671 671
672 672 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
673 673
674 674 if wctx.rev() is None:
675 675 fractions = _forgetremoved(wctx, mctx, branchmerge)
676 676 actions.update(fractions)
677 677
678 678 return actions, diverge, renamedelete
679 679
680 680 def batchremove(repo, actions):
681 681 """apply removes to the working directory
682 682
683 683 yields tuples for progress updates
684 684 """
685 685 verbose = repo.ui.verbose
686 686 unlink = util.unlinkpath
687 687 wjoin = repo.wjoin
688 688 audit = repo.wvfs.audit
689 689 i = 0
690 690 for f, args, msg in actions:
691 691 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
692 692 if verbose:
693 693 repo.ui.note(_("removing %s\n") % f)
694 694 audit(f)
695 695 try:
696 696 unlink(wjoin(f), ignoremissing=True)
697 697 except OSError as inst:
698 698 repo.ui.warn(_("update failed to remove %s: %s!\n") %
699 699 (f, inst.strerror))
700 700 if i == 100:
701 701 yield i, f
702 702 i = 0
703 703 i += 1
704 704 if i > 0:
705 705 yield i, f
706 706
707 707 def batchget(repo, mctx, actions):
708 708 """apply gets to the working directory
709 709
710 710 mctx is the context to get from
711 711
712 712 yields tuples for progress updates
713 713 """
714 714 verbose = repo.ui.verbose
715 715 fctx = mctx.filectx
716 716 wwrite = repo.wwrite
717 717 i = 0
718 718 for f, args, msg in actions:
719 719 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
720 720 if verbose:
721 721 repo.ui.note(_("getting %s\n") % f)
722 722 wwrite(f, fctx(f).data(), args[0])
723 723 if i == 100:
724 724 yield i, f
725 725 i = 0
726 726 i += 1
727 727 if i > 0:
728 728 yield i, f
729 729
730 730 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
731 731 """apply the merge action list to the working directory
732 732
733 733 wctx is the working copy context
734 734 mctx is the context to be merged into the working copy
735 735
736 736 Return a tuple of counts (updated, merged, removed, unresolved) that
737 737 describes how many files were affected by the update.
738 738 """
739 739
740 740 updated, merged, removed, unresolved = 0, 0, 0, 0
741 741 ms = mergestate(repo)
742 742 ms.reset(wctx.p1().node(), mctx.node())
743 743 moves = []
744 744 for m, l in actions.items():
745 745 l.sort()
746 746
747 747 # prescan for merges
748 748 for f, args, msg in actions['m']:
749 749 f1, f2, fa, move, anc = args
750 750 if f == '.hgsubstate': # merged internally
751 751 continue
752 752 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
753 753 fcl = wctx[f1]
754 754 fco = mctx[f2]
755 755 actx = repo[anc]
756 756 if fa in actx:
757 757 fca = actx[fa]
758 758 else:
759 759 fca = repo.filectx(f1, fileid=nullrev)
760 760 ms.add(fcl, fco, fca, f)
761 761 if f1 != f and move:
762 762 moves.append(f1)
763 763
764 764 audit = repo.wvfs.audit
765 765 _updating = _('updating')
766 766 _files = _('files')
767 767 progress = repo.ui.progress
768 768
769 769 # remove renamed files after safely stored
770 770 for f in moves:
771 771 if os.path.lexists(repo.wjoin(f)):
772 772 repo.ui.debug("removing %s\n" % f)
773 773 audit(f)
774 774 util.unlinkpath(repo.wjoin(f))
775 775
776 776 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
777 777
778 778 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
779 779 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
780 780
781 781 # remove in parallel (must come first)
782 782 z = 0
783 783 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
784 784 for i, item in prog:
785 785 z += i
786 786 progress(_updating, z, item=item, total=numupdates, unit=_files)
787 787 removed = len(actions['r'])
788 788
789 789 # get in parallel
790 790 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
791 791 for i, item in prog:
792 792 z += i
793 793 progress(_updating, z, item=item, total=numupdates, unit=_files)
794 794 updated = len(actions['g'])
795 795
796 796 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
797 797 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
798 798
799 799 # forget (manifest only, just log it) (must come first)
800 800 for f, args, msg in actions['f']:
801 801 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
802 802 z += 1
803 803 progress(_updating, z, item=f, total=numupdates, unit=_files)
804 804
805 805 # re-add (manifest only, just log it)
806 806 for f, args, msg in actions['a']:
807 807 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
808 808 z += 1
809 809 progress(_updating, z, item=f, total=numupdates, unit=_files)
810 810
811 811 # keep (noop, just log it)
812 812 for f, args, msg in actions['k']:
813 813 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
814 814 # no progress
815 815
816 816 # directory rename, move local
817 817 for f, args, msg in actions['dm']:
818 818 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
819 819 z += 1
820 820 progress(_updating, z, item=f, total=numupdates, unit=_files)
821 821 f0, flags = args
822 822 repo.ui.note(_("moving %s to %s\n") % (f0, f))
823 823 audit(f)
824 824 repo.wwrite(f, wctx.filectx(f0).data(), flags)
825 825 util.unlinkpath(repo.wjoin(f0))
826 826 updated += 1
827 827
828 828 # local directory rename, get
829 829 for f, args, msg in actions['dg']:
830 830 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
831 831 z += 1
832 832 progress(_updating, z, item=f, total=numupdates, unit=_files)
833 833 f0, flags = args
834 834 repo.ui.note(_("getting %s to %s\n") % (f0, f))
835 835 repo.wwrite(f, mctx.filectx(f0).data(), flags)
836 836 updated += 1
837 837
838 838 # exec
839 839 for f, args, msg in actions['e']:
840 840 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
841 841 z += 1
842 842 progress(_updating, z, item=f, total=numupdates, unit=_files)
843 843 flags, = args
844 844 audit(f)
845 845 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
846 846 updated += 1
847 847
848 848 # merge
849 849 for f, args, msg in actions['m']:
850 850 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
851 851 z += 1
852 852 progress(_updating, z, item=f, total=numupdates, unit=_files)
853 853 if f == '.hgsubstate': # subrepo states need updating
854 854 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
855 855 overwrite)
856 856 continue
857 857 audit(f)
858 858 r = ms.resolve(f, wctx, labels=labels)
859 859 if r is not None and r > 0:
860 860 unresolved += 1
861 861 else:
862 862 if r is None:
863 863 updated += 1
864 864 else:
865 865 merged += 1
866 866
867 867 ms.commit()
868 868 progress(_updating, None, total=numupdates, unit=_files)
869 869
870 870 return updated, merged, removed, unresolved
871 871
872 872 def recordupdates(repo, actions, branchmerge):
873 873 "record merge actions to the dirstate"
874 874 # remove (must come first)
875 875 for f, args, msg in actions['r']:
876 876 if branchmerge:
877 877 repo.dirstate.remove(f)
878 878 else:
879 879 repo.dirstate.drop(f)
880 880
881 881 # forget (must come first)
882 882 for f, args, msg in actions['f']:
883 883 repo.dirstate.drop(f)
884 884
885 885 # re-add
886 886 for f, args, msg in actions['a']:
887 887 if not branchmerge:
888 888 repo.dirstate.add(f)
889 889
890 890 # exec change
891 891 for f, args, msg in actions['e']:
892 892 repo.dirstate.normallookup(f)
893 893
894 894 # keep
895 895 for f, args, msg in actions['k']:
896 896 pass
897 897
898 898 # get
899 899 for f, args, msg in actions['g']:
900 900 if branchmerge:
901 901 repo.dirstate.otherparent(f)
902 902 else:
903 903 repo.dirstate.normal(f)
904 904
905 905 # merge
906 906 for f, args, msg in actions['m']:
907 907 f1, f2, fa, move, anc = args
908 908 if branchmerge:
909 909 # We've done a branch merge, mark this file as merged
910 910 # so that we properly record the merger later
911 911 repo.dirstate.merge(f)
912 912 if f1 != f2: # copy/rename
913 913 if move:
914 914 repo.dirstate.remove(f1)
915 915 if f1 != f:
916 916 repo.dirstate.copy(f1, f)
917 917 else:
918 918 repo.dirstate.copy(f2, f)
919 919 else:
920 920 # We've update-merged a locally modified file, so
921 921 # we set the dirstate to emulate a normal checkout
922 922 # of that file some time in the past. Thus our
923 923 # merge will appear as a normal local file
924 924 # modification.
925 925 if f2 == f: # file not locally copied/moved
926 926 repo.dirstate.normallookup(f)
927 927 if move:
928 928 repo.dirstate.drop(f1)
929 929
930 930 # directory rename, move local
931 931 for f, args, msg in actions['dm']:
932 932 f0, flag = args
933 933 if branchmerge:
934 934 repo.dirstate.add(f)
935 935 repo.dirstate.remove(f0)
936 936 repo.dirstate.copy(f0, f)
937 937 else:
938 938 repo.dirstate.normal(f)
939 939 repo.dirstate.drop(f0)
940 940
941 941 # directory rename, get
942 942 for f, args, msg in actions['dg']:
943 943 f0, flag = args
944 944 if branchmerge:
945 945 repo.dirstate.add(f)
946 946 repo.dirstate.copy(f0, f)
947 947 else:
948 948 repo.dirstate.normal(f)
949 949
950 950 def update(repo, node, branchmerge, force, partial, ancestor=None,
951 951 mergeancestor=False, labels=None):
952 952 """
953 953 Perform a merge between the working directory and the given node
954 954
955 955 node = the node to update to, or None if unspecified
956 956 branchmerge = whether to merge between branches
957 957 force = whether to force branch merging or file overwriting
958 958 partial = a function to filter file lists (dirstate not updated)
959 959 mergeancestor = whether it is merging with an ancestor. If true,
960 960 we should accept the incoming changes for any prompts that occur.
961 961 If false, merging with an ancestor (fast-forward) is only allowed
962 962 between different named branches. This flag is used by rebase extension
963 963 as a temporary fix and should be avoided in general.
964 964
965 965 The table below shows all the behaviors of the update command
966 966 given the -c and -C or no options, whether the working directory
967 967 is dirty, whether a revision is specified, and the relationship of
968 968 the parent rev to the target rev (linear, on the same named
969 969 branch, or on another named branch).
970 970
971 971 This logic is tested by test-update-branches.t.
972 972
973 973 -c -C dirty rev | linear same cross
974 974 n n n n | ok (1) x
975 975 n n n y | ok ok ok
976 976 n n y n | merge (2) (2)
977 977 n n y y | merge (3) (3)
978 978 n y * * | --- discard ---
979 979 y n y * | --- (4) ---
980 980 y n n * | --- ok ---
981 981 y y * * | --- (5) ---
982 982
983 983 x = can't happen
984 984 * = don't-care
985 985 1 = abort: not a linear update (merge or update --check to force update)
986 986 2 = abort: uncommitted changes (commit and merge, or update --clean to
987 987 discard changes)
988 988 3 = abort: uncommitted changes (commit or update --clean to discard changes)
989 989 4 = abort: uncommitted changes (checked in commands.py)
990 990 5 = incompatible options (checked in commands.py)
991 991
992 992 Return the same tuple as applyupdates().
993 993 """
994 994
995 995 onode = node
996 996 wlock = repo.wlock()
997 997 try:
998 998 wc = repo[None]
999 999 pl = wc.parents()
1000 1000 p1 = pl[0]
1001 1001 pas = [None]
1002 1002 if ancestor is not None:
1003 1003 pas = [repo[ancestor]]
1004 1004
1005 1005 if node is None:
1006 1006 node = repo[destutil.destupdate(repo)].node()
1007 1007
1008 1008 overwrite = force and not branchmerge
1009 1009
1010 1010 p2 = repo[node]
1011 1011 if pas[0] is None:
1012 1012 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1013 1013 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1014 1014 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1015 1015 else:
1016 1016 pas = [p1.ancestor(p2, warn=branchmerge)]
1017 1017
1018 1018 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1019 1019
1020 1020 ### check phase
1021 1021 if not overwrite and len(pl) > 1:
1022 1022 raise error.Abort(_("outstanding uncommitted merge"))
1023 1023 if branchmerge:
1024 1024 if pas == [p2]:
1025 1025 raise error.Abort(_("merging with a working directory ancestor"
1026 1026 " has no effect"))
1027 1027 elif pas == [p1]:
1028 1028 if not mergeancestor and p1.branch() == p2.branch():
1029 1029 raise error.Abort(_("nothing to merge"),
1030 1030 hint=_("use 'hg update' "
1031 1031 "or check 'hg heads'"))
1032 1032 if not force and (wc.files() or wc.deleted()):
1033 1033 raise error.Abort(_("uncommitted changes"),
1034 1034 hint=_("use 'hg status' to list changes"))
1035 1035 for s in sorted(wc.substate):
1036 1036 wc.sub(s).bailifchanged()
1037 1037
1038 1038 elif not overwrite:
1039 1039 if p1 == p2: # no-op update
1040 1040 # call the hooks and exit early
1041 1041 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1042 1042 repo.hook('update', parent1=xp2, parent2='', error=0)
1043 1043 return 0, 0, 0, 0
1044 1044
1045 1045 if pas not in ([p1], [p2]): # nonlinear
1046 1046 dirty = wc.dirty(missing=True)
1047 1047 if dirty or onode is None:
1048 1048 # Branching is a bit strange to ensure we do the minimal
1049 1049 # amount of call to obsolete.background.
1050 1050 foreground = obsolete.foreground(repo, [p1.node()])
1051 1051 # note: the <node> variable contains a random identifier
1052 1052 if repo[node].node() in foreground:
1053 1053 pas = [p1] # allow updating to successors
1054 1054 elif dirty:
1055 1055 msg = _("uncommitted changes")
1056 1056 if onode is None:
1057 1057 hint = _("commit and merge, or update --clean to"
1058 1058 " discard changes")
1059 1059 else:
1060 1060 hint = _("commit or update --clean to discard"
1061 1061 " changes")
1062 1062 raise error.Abort(msg, hint=hint)
1063 1063 else: # node is none
1064 1064 msg = _("not a linear update")
1065 1065 hint = _("merge or update --check to force update")
1066 1066 raise error.Abort(msg, hint=hint)
1067 1067 else:
1068 1068 # Allow jumping branches if clean and specific rev given
1069 1069 pas = [p1]
1070 1070
1071 1071 # deprecated config: merge.followcopies
1072 1072 followcopies = False
1073 1073 if overwrite:
1074 1074 pas = [wc]
1075 1075 elif pas == [p2]: # backwards
1076 1076 pas = [wc.p1()]
1077 1077 elif not branchmerge and not wc.dirty(missing=True):
1078 1078 pass
1079 1079 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1080 1080 followcopies = True
1081 1081
1082 1082 ### calculate phase
1083 1083 actionbyfile, diverge, renamedelete = calculateupdates(
1084 1084 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1085 1085 followcopies)
1086 1086 # Convert to dictionary-of-lists format
1087 1087 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1088 1088 for f, (m, args, msg) in actionbyfile.iteritems():
1089 1089 if m not in actions:
1090 1090 actions[m] = []
1091 1091 actions[m].append((f, args, msg))
1092 1092
1093 1093 if not util.checkcase(repo.path):
1094 1094 # check collision between files only in p2 for clean update
1095 1095 if (not branchmerge and
1096 1096 (force or not wc.dirty(missing=True, branch=False))):
1097 1097 _checkcollision(repo, p2.manifest(), None)
1098 1098 else:
1099 1099 _checkcollision(repo, wc.manifest(), actions)
1100 1100
1101 1101 # Prompt and create actions. TODO: Move this towards resolve phase.
1102 1102 for f, args, msg in sorted(actions['cd']):
1103 1103 if repo.ui.promptchoice(
1104 1104 _("local changed %s which remote deleted\n"
1105 1105 "use (c)hanged version or (d)elete?"
1106 1106 "$$ &Changed $$ &Delete") % f, 0):
1107 1107 actions['r'].append((f, None, "prompt delete"))
1108 1108 else:
1109 1109 actions['a'].append((f, None, "prompt keep"))
1110 1110 del actions['cd'][:]
1111 1111
1112 1112 for f, args, msg in sorted(actions['dc']):
1113 1113 flags, = args
1114 1114 if repo.ui.promptchoice(
1115 1115 _("remote changed %s which local deleted\n"
1116 1116 "use (c)hanged version or leave (d)eleted?"
1117 1117 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1118 1118 actions['g'].append((f, (flags,), "prompt recreating"))
1119 1119 del actions['dc'][:]
1120 1120
1121 1121 ### apply phase
1122 1122 if not branchmerge: # just jump to the new rev
1123 1123 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1124 1124 if not partial:
1125 1125 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1126 1126 # note that we're in the middle of an update
1127 1127 repo.vfs.write('updatestate', p2.hex())
1128 1128
1129 1129 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1130 1130
1131 1131 # divergent renames
1132 1132 for f, fl in sorted(diverge.iteritems()):
1133 1133 repo.ui.warn(_("note: possible conflict - %s was renamed "
1134 1134 "multiple times to:\n") % f)
1135 1135 for nf in fl:
1136 1136 repo.ui.warn(" %s\n" % nf)
1137 1137
1138 1138 # rename and delete
1139 1139 for f, fl in sorted(renamedelete.iteritems()):
1140 1140 repo.ui.warn(_("note: possible conflict - %s was deleted "
1141 1141 "and renamed to:\n") % f)
1142 1142 for nf in fl:
1143 1143 repo.ui.warn(" %s\n" % nf)
1144 1144
1145 1145 if not partial:
1146 1146 repo.dirstate.beginparentchange()
1147 1147 repo.setparents(fp1, fp2)
1148 1148 recordupdates(repo, actions, branchmerge)
1149 1149 # update completed, clear state
1150 1150 util.unlink(repo.join('updatestate'))
1151 1151
1152 1152 if not branchmerge:
1153 1153 repo.dirstate.setbranch(p2.branch())
1154 1154 repo.dirstate.endparentchange()
1155 1155 finally:
1156 1156 wlock.release()
1157 1157
1158 1158 if not partial:
1159 1159 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1160 1160 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1161 1161 repo._afterlock(updatehook)
1162 1162 return stats
1163 1163
1164 1164 def graft(repo, ctx, pctx, labels):
1165 1165 """Do a graft-like merge.
1166 1166
1167 1167 This is a merge where the merge ancestor is chosen such that one
1168 1168 or more changesets are grafted onto the current changeset. In
1169 1169 addition to the merge, this fixes up the dirstate to include only
1170 1170 a single parent and tries to duplicate any renames/copies
1171 1171 appropriately.
1172 1172
1173 1173 ctx - changeset to rebase
1174 1174 pctx - merge base, usually ctx.p1()
1175 1175 labels - merge labels eg ['local', 'graft']
1176 1176
1177 1177 """
1178 1178 # If we're grafting a descendant onto an ancestor, be sure to pass
1179 1179 # mergeancestor=True to update. This does two things: 1) allows the merge if
1180 1180 # the destination is the same as the parent of the ctx (so we can use graft
1181 1181 # to copy commits), and 2) informs update that the incoming changes are
1182 1182 # newer than the destination so it doesn't prompt about "remote changed foo
1183 1183 # which local deleted".
1184 1184 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1185 1185
1186 1186 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1187 1187 mergeancestor=mergeancestor, labels=labels)
1188 1188
1189 1189 # drop the second merge parent
1190 1190 repo.dirstate.beginparentchange()
1191 1191 repo.setparents(repo['.'].node(), nullid)
1192 1192 repo.dirstate.write()
1193 1193 # fix up dirstate for copies and renames
1194 1194 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1195 1195 repo.dirstate.endparentchange()
1196 1196 return stats
General Comments 0
You need to be logged in to leave comments. Login now