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