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