##// END OF EJS Templates
merge: duplicate 'if f in copied' into each branch
Martin von Zweigbergk -
r23474:9f4ac44a default
parent child Browse files
Show More
@@ -1,1156 +1,1158
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):
301 301 return (not repo.dirstate._ignore(f)
302 302 and os.path.isfile(repo.wjoin(f))
303 303 and repo.wopener.audit.check(f)
304 304 and repo.dirstate.normalize(f) not in repo.dirstate
305 305 and mctx[f].cmp(wctx[f]))
306 306
307 307 def _forgetremoved(wctx, mctx, branchmerge):
308 308 """
309 309 Forget removed files
310 310
311 311 If we're jumping between revisions (as opposed to merging), and if
312 312 neither the working directory nor the target rev has the file,
313 313 then we need to remove it from the dirstate, to prevent the
314 314 dirstate from listing the file when it is no longer in the
315 315 manifest.
316 316
317 317 If we're merging, and the other revision has removed a file
318 318 that is not present in the working directory, we need to mark it
319 319 as removed.
320 320 """
321 321
322 322 ractions = []
323 323 factions = xactions = []
324 324 if branchmerge:
325 325 xactions = ractions
326 326 for f in wctx.deleted():
327 327 if f not in mctx:
328 328 xactions.append((f, None, "forget deleted"))
329 329
330 330 if not branchmerge:
331 331 for f in wctx.removed():
332 332 if f not in mctx:
333 333 factions.append((f, None, "forget removed"))
334 334
335 335 return ractions, factions
336 336
337 337 def _checkcollision(repo, wmf, actions):
338 338 # build provisional merged manifest up
339 339 pmmf = set(wmf)
340 340
341 341 if actions:
342 342 # k, dr, e and rd are no-op
343 343 for m in 'a', 'f', 'g', 'cd', 'dc':
344 344 for f, args, msg in actions[m]:
345 345 pmmf.add(f)
346 346 for f, args, msg in actions['r']:
347 347 pmmf.discard(f)
348 348 for f, args, msg in actions['dm']:
349 349 f2, flags = args
350 350 pmmf.discard(f2)
351 351 pmmf.add(f)
352 352 for f, args, msg in actions['dg']:
353 353 pmmf.add(f)
354 354 for f, args, msg in actions['m']:
355 355 f1, f2, fa, move, anc = args
356 356 if move:
357 357 pmmf.discard(f1)
358 358 pmmf.add(f)
359 359
360 360 # check case-folding collision in provisional merged manifest
361 361 foldmap = {}
362 362 for f in sorted(pmmf):
363 363 fold = util.normcase(f)
364 364 if fold in foldmap:
365 365 raise util.Abort(_("case-folding collision between %s and %s")
366 366 % (f, foldmap[fold]))
367 367 foldmap[fold] = f
368 368
369 369 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
370 370 acceptremote, followcopies):
371 371 """
372 372 Merge p1 and p2 with ancestor pa and generate merge action list
373 373
374 374 branchmerge and force are as passed in to update
375 375 partial = function to filter file lists
376 376 acceptremote = accept the incoming changes without prompting
377 377 """
378 378
379 379 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
380 380 copy, movewithdir = {}, {}
381 381
382 382 # manifests fetched in order are going to be faster, so prime the caches
383 383 [x.manifest() for x in
384 384 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
385 385
386 386 if followcopies:
387 387 ret = copies.mergecopies(repo, wctx, p2, pa)
388 388 copy, movewithdir, diverge, renamedelete = ret
389 389 for of, fl in diverge.iteritems():
390 390 actions['dr'].append((of, (fl,), "divergent renames"))
391 391 for of, fl in renamedelete.iteritems():
392 392 actions['rd'].append((of, (fl,), "rename and delete"))
393 393
394 394 repo.ui.note(_("resolving manifests\n"))
395 395 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
396 396 % (bool(branchmerge), bool(force), bool(partial)))
397 397 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
398 398
399 399 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
400 400 copied = set(copy.values())
401 401 copied.update(movewithdir.values())
402 402
403 403 if '.hgsubstate' in m1:
404 404 # check whether sub state is modified
405 405 for s in sorted(wctx.substate):
406 406 if wctx.sub(s).dirty():
407 407 m1['.hgsubstate'] += '+'
408 408 break
409 409
410 410 aborts = []
411 411 # Compare manifests
412 412 diff = m1.diff(m2)
413 413
414 414 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
415 415 if partial and not partial(f):
416 416 continue
417 417 if n1 and n2: # file exists on both local and remote side
418 418 if f not in ma:
419 419 fa = copy.get(f, None)
420 420 if fa is not None:
421 421 actions['m'].append((f, (f, f, fa, False, pa.node()),
422 422 "both renamed from " + fa))
423 423 else:
424 424 actions['m'].append((f, (f, f, None, False, pa.node()),
425 425 "both created"))
426 426 else:
427 427 a = ma[f]
428 428 fla = ma.flags(f)
429 429 nol = 'l' not in fl1 + fl2 + fla
430 430 if n2 == a and fl2 == fla:
431 431 actions['k'].append((f, (), "keep")) # remote unchanged
432 432 elif n1 == a and fl1 == fla: # local unchanged - use remote
433 433 if n1 == n2: # optimization: keep local content
434 434 actions['e'].append((f, (fl2,), "update permissions"))
435 435 else:
436 436 actions['g'].append((f, (fl2,), "remote is newer"))
437 437 elif nol and n2 == a: # remote only changed 'x'
438 438 actions['e'].append((f, (fl2,), "update permissions"))
439 439 elif nol and n1 == a: # local only changed 'x'
440 440 actions['g'].append((f, (fl1,), "remote is newer"))
441 441 else: # both changed something
442 442 actions['m'].append((f, (f, f, f, False, pa.node()),
443 443 "versions differ"))
444 elif f in copied: # files we'll deal with on m2 side
445 pass
446 444 elif n1: # file exists only on local side
447 if f in movewithdir: # directory rename, move local
445 if f in copied:
446 pass # we'll deal with it on m2 side
447 elif f in movewithdir: # directory rename, move local
448 448 f2 = movewithdir[f]
449 449 actions['dm'].append((f2, (f, fl1),
450 450 "remote directory rename - move from " + f))
451 451 elif f in copy:
452 452 f2 = copy[f]
453 453 actions['m'].append((f, (f, f2, f2, False, pa.node()),
454 454 "local copied/moved from " + f2))
455 455 elif f in ma: # clean, a different, no remote
456 456 if n1 != ma[f]:
457 457 if acceptremote:
458 458 actions['r'].append((f, None, "remote delete"))
459 459 else:
460 460 actions['cd'].append((f, None,
461 461 "prompt changed/deleted"))
462 462 elif n1[20:] == 'a':
463 463 # This extra 'a' is added by working copy manifest to mark
464 464 # the file as locally added. We should forget it instead of
465 465 # deleting it.
466 466 actions['f'].append((f, None, "remote deleted"))
467 467 else:
468 468 actions['r'].append((f, None, "other deleted"))
469 469 elif n2: # file exists only on remote side
470 if f in movewithdir:
470 if f in copied:
471 pass # we'll deal with it on m1 side
472 elif f in movewithdir:
471 473 f2 = movewithdir[f]
472 474 actions['dg'].append((f2, (f, fl2),
473 475 "local directory rename - get from " + f))
474 476 elif f in copy:
475 477 f2 = copy[f]
476 478 if f2 in m2:
477 479 actions['m'].append((f, (f2, f, f2, False, pa.node()),
478 480 "remote copied from " + f2))
479 481 else:
480 482 actions['m'].append((f, (f2, f, f2, True, pa.node()),
481 483 "remote moved from " + f2))
482 484 elif f not in ma:
483 485 # local unknown, remote created: the logic is described by the
484 486 # following table:
485 487 #
486 488 # force branchmerge different | action
487 489 # n * n | get
488 490 # n * y | abort
489 491 # y n * | get
490 492 # y y n | get
491 493 # y y y | merge
492 494 #
493 495 # Checking whether the files are different is expensive, so we
494 496 # don't do that when we can avoid it.
495 497 if force and not branchmerge:
496 498 actions['g'].append((f, (fl2,), "remote created"))
497 499 else:
498 500 different = _checkunknownfile(repo, wctx, p2, f)
499 501 if force and branchmerge and different:
500 502 actions['m'].append((f, (f, f, None, False, pa.node()),
501 503 "remote differs from untracked local"))
502 504 elif not force and different:
503 505 aborts.append((f, 'ud'))
504 506 else:
505 507 actions['g'].append((f, (fl2,), "remote created"))
506 508 elif n2 != ma[f]:
507 509 different = _checkunknownfile(repo, wctx, p2, f)
508 510 if not force and different:
509 511 aborts.append((f, 'ud'))
510 512 else:
511 513 if acceptremote:
512 514 actions['g'].append((f, (fl2,), "remote recreating"))
513 515 else:
514 516 actions['dc'].append((f, (fl2,),
515 517 "prompt deleted/changed"))
516 518
517 519 for f, m in sorted(aborts):
518 520 if m == 'ud':
519 521 repo.ui.warn(_("%s: untracked file differs\n") % f)
520 522 else: assert False, m
521 523 if aborts:
522 524 raise util.Abort(_("untracked files in working directory differ "
523 525 "from files in requested revision"))
524 526
525 527 if not util.checkcase(repo.path):
526 528 # check collision between files only in p2 for clean update
527 529 if (not branchmerge and
528 530 (force or not wctx.dirty(missing=True, branch=False))):
529 531 _checkcollision(repo, m2, None)
530 532 else:
531 533 _checkcollision(repo, m1, actions)
532 534
533 535 return actions
534 536
535 537 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
536 538 acceptremote, followcopies):
537 539 "Calculate the actions needed to merge mctx into wctx using ancestors"
538 540
539 541 if len(ancestors) == 1: # default
540 542 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
541 543 branchmerge, force,
542 544 partial, acceptremote, followcopies)
543 545
544 546 else: # only when merge.preferancestor=* - the default
545 547 repo.ui.note(
546 548 _("note: merging %s and %s using bids from ancestors %s\n") %
547 549 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
548 550
549 551 # Call for bids
550 552 fbids = {} # mapping filename to bids (action method to list af actions)
551 553 for ancestor in ancestors:
552 554 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
553 555 actions = manifestmerge(repo, wctx, mctx, ancestor,
554 556 branchmerge, force,
555 557 partial, acceptremote, followcopies)
556 558 for m, l in sorted(actions.items()):
557 559 for a in l:
558 560 f, args, msg = a
559 561 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
560 562 if f in fbids:
561 563 d = fbids[f]
562 564 if m in d:
563 565 d[m].append(a)
564 566 else:
565 567 d[m] = [a]
566 568 else:
567 569 fbids[f] = {m: [a]}
568 570
569 571 # Pick the best bid for each file
570 572 repo.ui.note(_('\nauction for merging merge bids\n'))
571 573 actions = dict((m, []) for m in actions.keys())
572 574 for f, bids in sorted(fbids.items()):
573 575 # bids is a mapping from action method to list af actions
574 576 # Consensus?
575 577 if len(bids) == 1: # all bids are the same kind of method
576 578 m, l = bids.items()[0]
577 579 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
578 580 repo.ui.note(" %s: consensus for %s\n" % (f, m))
579 581 actions[m].append(l[0])
580 582 continue
581 583 # If keep is an option, just do it.
582 584 if 'k' in bids:
583 585 repo.ui.note(" %s: picking 'keep' action\n" % f)
584 586 actions['k'].append(bids['k'][0])
585 587 continue
586 588 # If there are gets and they all agree [how could they not?], do it.
587 589 if 'g' in bids:
588 590 ga0 = bids['g'][0]
589 591 if util.all(a == ga0 for a in bids['g'][1:]):
590 592 repo.ui.note(" %s: picking 'get' action\n" % f)
591 593 actions['g'].append(ga0)
592 594 continue
593 595 # TODO: Consider other simple actions such as mode changes
594 596 # Handle inefficient democrazy.
595 597 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
596 598 for m, l in sorted(bids.items()):
597 599 for _f, args, msg in l:
598 600 repo.ui.note(' %s -> %s\n' % (msg, m))
599 601 # Pick random action. TODO: Instead, prompt user when resolving
600 602 m, l = bids.items()[0]
601 603 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
602 604 (f, m))
603 605 actions[m].append(l[0])
604 606 continue
605 607 repo.ui.note(_('end of auction\n\n'))
606 608
607 609 # Prompt and create actions. TODO: Move this towards resolve phase.
608 610 for f, args, msg in sorted(actions['cd']):
609 611 if f in ancestors[0] and not wctx[f].cmp(ancestors[0][f]):
610 612 # local did change but ended up with same content
611 613 actions['r'].append((f, None, "prompt same"))
612 614 elif repo.ui.promptchoice(
613 615 _("local changed %s which remote deleted\n"
614 616 "use (c)hanged version or (d)elete?"
615 617 "$$ &Changed $$ &Delete") % f, 0):
616 618 actions['r'].append((f, None, "prompt delete"))
617 619 else:
618 620 actions['a'].append((f, None, "prompt keep"))
619 621 del actions['cd'][:]
620 622
621 623 for f, args, msg in sorted(actions['dc']):
622 624 flags, = args
623 625 if f in ancestors[0] and not mctx[f].cmp(ancestors[0][f]):
624 626 # remote did change but ended up with same content
625 627 pass # don't get = keep local deleted
626 628 elif repo.ui.promptchoice(
627 629 _("remote changed %s which local deleted\n"
628 630 "use (c)hanged version or leave (d)eleted?"
629 631 "$$ &Changed $$ &Deleted") % f, 0) == 0:
630 632 actions['g'].append((f, (flags,), "prompt recreating"))
631 633 del actions['dc'][:]
632 634
633 635 if wctx.rev() is None:
634 636 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
635 637 actions['r'].extend(ractions)
636 638 actions['f'].extend(factions)
637 639
638 640 return actions
639 641
640 642 def batchremove(repo, actions):
641 643 """apply removes to the working directory
642 644
643 645 yields tuples for progress updates
644 646 """
645 647 verbose = repo.ui.verbose
646 648 unlink = util.unlinkpath
647 649 wjoin = repo.wjoin
648 650 audit = repo.wopener.audit
649 651 i = 0
650 652 for f, args, msg in actions:
651 653 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
652 654 if verbose:
653 655 repo.ui.note(_("removing %s\n") % f)
654 656 audit(f)
655 657 try:
656 658 unlink(wjoin(f), ignoremissing=True)
657 659 except OSError, inst:
658 660 repo.ui.warn(_("update failed to remove %s: %s!\n") %
659 661 (f, inst.strerror))
660 662 if i == 100:
661 663 yield i, f
662 664 i = 0
663 665 i += 1
664 666 if i > 0:
665 667 yield i, f
666 668
667 669 def batchget(repo, mctx, actions):
668 670 """apply gets to the working directory
669 671
670 672 mctx is the context to get from
671 673
672 674 yields tuples for progress updates
673 675 """
674 676 verbose = repo.ui.verbose
675 677 fctx = mctx.filectx
676 678 wwrite = repo.wwrite
677 679 i = 0
678 680 for f, args, msg in actions:
679 681 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
680 682 if verbose:
681 683 repo.ui.note(_("getting %s\n") % f)
682 684 wwrite(f, fctx(f).data(), args[0])
683 685 if i == 100:
684 686 yield i, f
685 687 i = 0
686 688 i += 1
687 689 if i > 0:
688 690 yield i, f
689 691
690 692 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
691 693 """apply the merge action list to the working directory
692 694
693 695 wctx is the working copy context
694 696 mctx is the context to be merged into the working copy
695 697
696 698 Return a tuple of counts (updated, merged, removed, unresolved) that
697 699 describes how many files were affected by the update.
698 700 """
699 701
700 702 updated, merged, removed, unresolved = 0, 0, 0, 0
701 703 ms = mergestate(repo)
702 704 ms.reset(wctx.p1().node(), mctx.node())
703 705 moves = []
704 706 for m, l in actions.items():
705 707 l.sort()
706 708
707 709 # prescan for merges
708 710 for f, args, msg in actions['m']:
709 711 f1, f2, fa, move, anc = args
710 712 if f == '.hgsubstate': # merged internally
711 713 continue
712 714 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
713 715 fcl = wctx[f1]
714 716 fco = mctx[f2]
715 717 actx = repo[anc]
716 718 if fa in actx:
717 719 fca = actx[fa]
718 720 else:
719 721 fca = repo.filectx(f1, fileid=nullrev)
720 722 ms.add(fcl, fco, fca, f)
721 723 if f1 != f and move:
722 724 moves.append(f1)
723 725
724 726 audit = repo.wopener.audit
725 727 _updating = _('updating')
726 728 _files = _('files')
727 729 progress = repo.ui.progress
728 730
729 731 # remove renamed files after safely stored
730 732 for f in moves:
731 733 if os.path.lexists(repo.wjoin(f)):
732 734 repo.ui.debug("removing %s\n" % f)
733 735 audit(f)
734 736 util.unlinkpath(repo.wjoin(f))
735 737
736 738 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
737 739
738 740 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
739 741 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
740 742
741 743 # remove in parallel (must come first)
742 744 z = 0
743 745 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
744 746 for i, item in prog:
745 747 z += i
746 748 progress(_updating, z, item=item, total=numupdates, unit=_files)
747 749 removed = len(actions['r'])
748 750
749 751 # get in parallel
750 752 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
751 753 for i, item in prog:
752 754 z += i
753 755 progress(_updating, z, item=item, total=numupdates, unit=_files)
754 756 updated = len(actions['g'])
755 757
756 758 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
757 759 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
758 760
759 761 # forget (manifest only, just log it) (must come first)
760 762 for f, args, msg in actions['f']:
761 763 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
762 764 z += 1
763 765 progress(_updating, z, item=f, total=numupdates, unit=_files)
764 766
765 767 # re-add (manifest only, just log it)
766 768 for f, args, msg in actions['a']:
767 769 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
768 770 z += 1
769 771 progress(_updating, z, item=f, total=numupdates, unit=_files)
770 772
771 773 # keep (noop, just log it)
772 774 for f, args, msg in actions['k']:
773 775 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
774 776 # no progress
775 777
776 778 # merge
777 779 for f, args, msg in actions['m']:
778 780 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
779 781 z += 1
780 782 progress(_updating, z, item=f, total=numupdates, unit=_files)
781 783 if f == '.hgsubstate': # subrepo states need updating
782 784 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
783 785 overwrite)
784 786 continue
785 787 audit(f)
786 788 r = ms.resolve(f, wctx, labels=labels)
787 789 if r is not None and r > 0:
788 790 unresolved += 1
789 791 else:
790 792 if r is None:
791 793 updated += 1
792 794 else:
793 795 merged += 1
794 796
795 797 # directory rename, move local
796 798 for f, args, msg in actions['dm']:
797 799 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
798 800 z += 1
799 801 progress(_updating, z, item=f, total=numupdates, unit=_files)
800 802 f0, flags = args
801 803 repo.ui.note(_("moving %s to %s\n") % (f0, f))
802 804 audit(f)
803 805 repo.wwrite(f, wctx.filectx(f0).data(), flags)
804 806 util.unlinkpath(repo.wjoin(f0))
805 807 updated += 1
806 808
807 809 # local directory rename, get
808 810 for f, args, msg in actions['dg']:
809 811 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
810 812 z += 1
811 813 progress(_updating, z, item=f, total=numupdates, unit=_files)
812 814 f0, flags = args
813 815 repo.ui.note(_("getting %s to %s\n") % (f0, f))
814 816 repo.wwrite(f, mctx.filectx(f0).data(), flags)
815 817 updated += 1
816 818
817 819 # divergent renames
818 820 for f, args, msg in actions['dr']:
819 821 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
820 822 z += 1
821 823 progress(_updating, z, item=f, total=numupdates, unit=_files)
822 824 fl, = args
823 825 repo.ui.warn(_("note: possible conflict - %s was renamed "
824 826 "multiple times to:\n") % f)
825 827 for nf in fl:
826 828 repo.ui.warn(" %s\n" % nf)
827 829
828 830 # rename and delete
829 831 for f, args, msg in actions['rd']:
830 832 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
831 833 z += 1
832 834 progress(_updating, z, item=f, total=numupdates, unit=_files)
833 835 fl, = args
834 836 repo.ui.warn(_("note: possible conflict - %s was deleted "
835 837 "and renamed to:\n") % f)
836 838 for nf in fl:
837 839 repo.ui.warn(" %s\n" % nf)
838 840
839 841 # exec
840 842 for f, args, msg in actions['e']:
841 843 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
842 844 z += 1
843 845 progress(_updating, z, item=f, total=numupdates, unit=_files)
844 846 flags, = args
845 847 audit(f)
846 848 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
847 849 updated += 1
848 850
849 851 ms.commit()
850 852 progress(_updating, None, total=numupdates, unit=_files)
851 853
852 854 return updated, merged, removed, unresolved
853 855
854 856 def recordupdates(repo, actions, branchmerge):
855 857 "record merge actions to the dirstate"
856 858 # remove (must come first)
857 859 for f, args, msg in actions['r']:
858 860 if branchmerge:
859 861 repo.dirstate.remove(f)
860 862 else:
861 863 repo.dirstate.drop(f)
862 864
863 865 # forget (must come first)
864 866 for f, args, msg in actions['f']:
865 867 repo.dirstate.drop(f)
866 868
867 869 # re-add
868 870 for f, args, msg in actions['a']:
869 871 if not branchmerge:
870 872 repo.dirstate.add(f)
871 873
872 874 # exec change
873 875 for f, args, msg in actions['e']:
874 876 repo.dirstate.normallookup(f)
875 877
876 878 # keep
877 879 for f, args, msg in actions['k']:
878 880 pass
879 881
880 882 # get
881 883 for f, args, msg in actions['g']:
882 884 if branchmerge:
883 885 repo.dirstate.otherparent(f)
884 886 else:
885 887 repo.dirstate.normal(f)
886 888
887 889 # merge
888 890 for f, args, msg in actions['m']:
889 891 f1, f2, fa, move, anc = args
890 892 if branchmerge:
891 893 # We've done a branch merge, mark this file as merged
892 894 # so that we properly record the merger later
893 895 repo.dirstate.merge(f)
894 896 if f1 != f2: # copy/rename
895 897 if move:
896 898 repo.dirstate.remove(f1)
897 899 if f1 != f:
898 900 repo.dirstate.copy(f1, f)
899 901 else:
900 902 repo.dirstate.copy(f2, f)
901 903 else:
902 904 # We've update-merged a locally modified file, so
903 905 # we set the dirstate to emulate a normal checkout
904 906 # of that file some time in the past. Thus our
905 907 # merge will appear as a normal local file
906 908 # modification.
907 909 if f2 == f: # file not locally copied/moved
908 910 repo.dirstate.normallookup(f)
909 911 if move:
910 912 repo.dirstate.drop(f1)
911 913
912 914 # directory rename, move local
913 915 for f, args, msg in actions['dm']:
914 916 f0, flag = args
915 917 if branchmerge:
916 918 repo.dirstate.add(f)
917 919 repo.dirstate.remove(f0)
918 920 repo.dirstate.copy(f0, f)
919 921 else:
920 922 repo.dirstate.normal(f)
921 923 repo.dirstate.drop(f0)
922 924
923 925 # directory rename, get
924 926 for f, args, msg in actions['dg']:
925 927 f0, flag = args
926 928 if branchmerge:
927 929 repo.dirstate.add(f)
928 930 repo.dirstate.copy(f0, f)
929 931 else:
930 932 repo.dirstate.normal(f)
931 933
932 934 def update(repo, node, branchmerge, force, partial, ancestor=None,
933 935 mergeancestor=False, labels=None):
934 936 """
935 937 Perform a merge between the working directory and the given node
936 938
937 939 node = the node to update to, or None if unspecified
938 940 branchmerge = whether to merge between branches
939 941 force = whether to force branch merging or file overwriting
940 942 partial = a function to filter file lists (dirstate not updated)
941 943 mergeancestor = whether it is merging with an ancestor. If true,
942 944 we should accept the incoming changes for any prompts that occur.
943 945 If false, merging with an ancestor (fast-forward) is only allowed
944 946 between different named branches. This flag is used by rebase extension
945 947 as a temporary fix and should be avoided in general.
946 948
947 949 The table below shows all the behaviors of the update command
948 950 given the -c and -C or no options, whether the working directory
949 951 is dirty, whether a revision is specified, and the relationship of
950 952 the parent rev to the target rev (linear, on the same named
951 953 branch, or on another named branch).
952 954
953 955 This logic is tested by test-update-branches.t.
954 956
955 957 -c -C dirty rev | linear same cross
956 958 n n n n | ok (1) x
957 959 n n n y | ok ok ok
958 960 n n y n | merge (2) (2)
959 961 n n y y | merge (3) (3)
960 962 n y * * | --- discard ---
961 963 y n y * | --- (4) ---
962 964 y n n * | --- ok ---
963 965 y y * * | --- (5) ---
964 966
965 967 x = can't happen
966 968 * = don't-care
967 969 1 = abort: not a linear update (merge or update --check to force update)
968 970 2 = abort: uncommitted changes (commit and merge, or update --clean to
969 971 discard changes)
970 972 3 = abort: uncommitted changes (commit or update --clean to discard changes)
971 973 4 = abort: uncommitted changes (checked in commands.py)
972 974 5 = incompatible options (checked in commands.py)
973 975
974 976 Return the same tuple as applyupdates().
975 977 """
976 978
977 979 onode = node
978 980 wlock = repo.wlock()
979 981 try:
980 982 wc = repo[None]
981 983 pl = wc.parents()
982 984 p1 = pl[0]
983 985 pas = [None]
984 986 if ancestor is not None:
985 987 pas = [repo[ancestor]]
986 988
987 989 if node is None:
988 990 # Here is where we should consider bookmarks, divergent bookmarks,
989 991 # foreground changesets (successors), and tip of current branch;
990 992 # but currently we are only checking the branch tips.
991 993 try:
992 994 node = repo.branchtip(wc.branch())
993 995 except errormod.RepoLookupError:
994 996 if wc.branch() == 'default': # no default branch!
995 997 node = repo.lookup('tip') # update to tip
996 998 else:
997 999 raise util.Abort(_("branch %s not found") % wc.branch())
998 1000
999 1001 if p1.obsolete() and not p1.children():
1000 1002 # allow updating to successors
1001 1003 successors = obsolete.successorssets(repo, p1.node())
1002 1004
1003 1005 # behavior of certain cases is as follows,
1004 1006 #
1005 1007 # divergent changesets: update to highest rev, similar to what
1006 1008 # is currently done when there are more than one head
1007 1009 # (i.e. 'tip')
1008 1010 #
1009 1011 # replaced changesets: same as divergent except we know there
1010 1012 # is no conflict
1011 1013 #
1012 1014 # pruned changeset: no update is done; though, we could
1013 1015 # consider updating to the first non-obsolete parent,
1014 1016 # similar to what is current done for 'hg prune'
1015 1017
1016 1018 if successors:
1017 1019 # flatten the list here handles both divergent (len > 1)
1018 1020 # and the usual case (len = 1)
1019 1021 successors = [n for sub in successors for n in sub]
1020 1022
1021 1023 # get the max revision for the given successors set,
1022 1024 # i.e. the 'tip' of a set
1023 1025 node = repo.revs('max(%ln)', successors).first()
1024 1026 pas = [p1]
1025 1027
1026 1028 overwrite = force and not branchmerge
1027 1029
1028 1030 p2 = repo[node]
1029 1031 if pas[0] is None:
1030 1032 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1031 1033 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1032 1034 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1033 1035 else:
1034 1036 pas = [p1.ancestor(p2, warn=branchmerge)]
1035 1037
1036 1038 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1037 1039
1038 1040 ### check phase
1039 1041 if not overwrite and len(pl) > 1:
1040 1042 raise util.Abort(_("outstanding uncommitted merge"))
1041 1043 if branchmerge:
1042 1044 if pas == [p2]:
1043 1045 raise util.Abort(_("merging with a working directory ancestor"
1044 1046 " has no effect"))
1045 1047 elif pas == [p1]:
1046 1048 if not mergeancestor and p1.branch() == p2.branch():
1047 1049 raise util.Abort(_("nothing to merge"),
1048 1050 hint=_("use 'hg update' "
1049 1051 "or check 'hg heads'"))
1050 1052 if not force and (wc.files() or wc.deleted()):
1051 1053 raise util.Abort(_("uncommitted changes"),
1052 1054 hint=_("use 'hg status' to list changes"))
1053 1055 for s in sorted(wc.substate):
1054 1056 if wc.sub(s).dirty():
1055 1057 raise util.Abort(_("uncommitted changes in "
1056 1058 "subrepository '%s'") % s)
1057 1059
1058 1060 elif not overwrite:
1059 1061 if p1 == p2: # no-op update
1060 1062 # call the hooks and exit early
1061 1063 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1062 1064 repo.hook('update', parent1=xp2, parent2='', error=0)
1063 1065 return 0, 0, 0, 0
1064 1066
1065 1067 if pas not in ([p1], [p2]): # nonlinear
1066 1068 dirty = wc.dirty(missing=True)
1067 1069 if dirty or onode is None:
1068 1070 # Branching is a bit strange to ensure we do the minimal
1069 1071 # amount of call to obsolete.background.
1070 1072 foreground = obsolete.foreground(repo, [p1.node()])
1071 1073 # note: the <node> variable contains a random identifier
1072 1074 if repo[node].node() in foreground:
1073 1075 pas = [p1] # allow updating to successors
1074 1076 elif dirty:
1075 1077 msg = _("uncommitted changes")
1076 1078 if onode is None:
1077 1079 hint = _("commit and merge, or update --clean to"
1078 1080 " discard changes")
1079 1081 else:
1080 1082 hint = _("commit or update --clean to discard"
1081 1083 " changes")
1082 1084 raise util.Abort(msg, hint=hint)
1083 1085 else: # node is none
1084 1086 msg = _("not a linear update")
1085 1087 hint = _("merge or update --check to force update")
1086 1088 raise util.Abort(msg, hint=hint)
1087 1089 else:
1088 1090 # Allow jumping branches if clean and specific rev given
1089 1091 pas = [p1]
1090 1092
1091 1093 followcopies = False
1092 1094 if overwrite:
1093 1095 pas = [wc]
1094 1096 elif pas == [p2]: # backwards
1095 1097 pas = [wc.p1()]
1096 1098 elif not branchmerge and not wc.dirty(missing=True):
1097 1099 pass
1098 1100 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1099 1101 followcopies = True
1100 1102
1101 1103 ### calculate phase
1102 1104 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1103 1105 partial, mergeancestor, followcopies)
1104 1106
1105 1107 ### apply phase
1106 1108 if not branchmerge: # just jump to the new rev
1107 1109 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1108 1110 if not partial:
1109 1111 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1110 1112 # note that we're in the middle of an update
1111 1113 repo.vfs.write('updatestate', p2.hex())
1112 1114
1113 1115 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1114 1116
1115 1117 if not partial:
1116 1118 repo.dirstate.beginparentchange()
1117 1119 repo.setparents(fp1, fp2)
1118 1120 recordupdates(repo, actions, branchmerge)
1119 1121 # update completed, clear state
1120 1122 util.unlink(repo.join('updatestate'))
1121 1123
1122 1124 if not branchmerge:
1123 1125 repo.dirstate.setbranch(p2.branch())
1124 1126 repo.dirstate.endparentchange()
1125 1127 finally:
1126 1128 wlock.release()
1127 1129
1128 1130 if not partial:
1129 1131 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1130 1132 return stats
1131 1133
1132 1134 def graft(repo, ctx, pctx, labels):
1133 1135 """Do a graft-like merge.
1134 1136
1135 1137 This is a merge where the merge ancestor is chosen such that one
1136 1138 or more changesets are grafted onto the current changeset. In
1137 1139 addition to the merge, this fixes up the dirstate to include only
1138 1140 a single parent and tries to duplicate any renames/copies
1139 1141 appropriately.
1140 1142
1141 1143 ctx - changeset to rebase
1142 1144 pctx - merge base, usually ctx.p1()
1143 1145 labels - merge labels eg ['local', 'graft']
1144 1146
1145 1147 """
1146 1148
1147 1149 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1148 1150 labels=labels)
1149 1151 # drop the second merge parent
1150 1152 repo.dirstate.beginparentchange()
1151 1153 repo.setparents(repo['.'].node(), nullid)
1152 1154 repo.dirstate.write()
1153 1155 # fix up dirstate for copies and renames
1154 1156 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1155 1157 repo.dirstate.endparentchange()
1156 1158 return stats
General Comments 0
You need to be logged in to leave comments. Login now