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