##// END OF EJS Templates
merge: fix UnboundLocalError (issue3791)...
Mads Kiilerich -
r18505:9de9727c stable
parent child Browse files
Show More
@@ -1,642 +1,642 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 node import nullid, nullrev, hex, bin
9 9 from i18n import _
10 10 import error, util, filemerge, copies, subrepo
11 11 import errno, os, shutil
12 12
13 13 class mergestate(object):
14 14 '''track 3-way merge state of individual files'''
15 15 def __init__(self, repo):
16 16 self._repo = repo
17 17 self._dirty = False
18 18 self._read()
19 19 def reset(self, node=None):
20 20 self._state = {}
21 21 if node:
22 22 self._local = node
23 23 shutil.rmtree(self._repo.join("merge"), True)
24 24 self._dirty = False
25 25 def _read(self):
26 26 self._state = {}
27 27 try:
28 28 f = self._repo.opener("merge/state")
29 29 for i, l in enumerate(f):
30 30 if i == 0:
31 31 self._local = bin(l[:-1])
32 32 else:
33 33 bits = l[:-1].split("\0")
34 34 self._state[bits[0]] = bits[1:]
35 35 f.close()
36 36 except IOError, err:
37 37 if err.errno != errno.ENOENT:
38 38 raise
39 39 self._dirty = False
40 40 def commit(self):
41 41 if self._dirty:
42 42 f = self._repo.opener("merge/state", "w")
43 43 f.write(hex(self._local) + "\n")
44 44 for d, v in self._state.iteritems():
45 45 f.write("\0".join([d] + v) + "\n")
46 46 f.close()
47 47 self._dirty = False
48 48 def add(self, fcl, fco, fca, fd):
49 49 hash = util.sha1(fcl.path()).hexdigest()
50 50 self._repo.opener.write("merge/" + hash, fcl.data())
51 51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 52 hex(fca.filenode()), fco.path(), fcl.flags()]
53 53 self._dirty = True
54 54 def __contains__(self, dfile):
55 55 return dfile in self._state
56 56 def __getitem__(self, dfile):
57 57 return self._state[dfile][0]
58 58 def __iter__(self):
59 59 l = self._state.keys()
60 60 l.sort()
61 61 for f in l:
62 62 yield f
63 63 def mark(self, dfile, state):
64 64 self._state[dfile][0] = state
65 65 self._dirty = True
66 66 def resolve(self, dfile, wctx, octx):
67 67 if self[dfile] == 'r':
68 68 return 0
69 69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
70 70 fcd = wctx[dfile]
71 71 fco = octx[ofile]
72 72 fca = self._repo.filectx(afile, fileid=anode)
73 73 # "premerge" x flags
74 74 flo = fco.flags()
75 75 fla = fca.flags()
76 76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
77 77 if fca.node() == nullid:
78 78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
79 79 afile)
80 80 elif flags == fla:
81 81 flags = flo
82 82 # restore local
83 83 f = self._repo.opener("merge/" + hash)
84 84 self._repo.wwrite(dfile, f.read(), flags)
85 85 f.close()
86 86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
87 87 if r is None:
88 88 # no real conflict
89 89 del self._state[dfile]
90 90 elif not r:
91 91 self.mark(dfile, 'r')
92 92 return r
93 93
94 94 def _checkunknownfile(repo, wctx, mctx, f):
95 95 return (not repo.dirstate._ignore(f)
96 96 and os.path.isfile(repo.wjoin(f))
97 97 and repo.dirstate.normalize(f) not in repo.dirstate
98 98 and mctx[f].cmp(wctx[f]))
99 99
100 100 def _checkunknown(repo, wctx, mctx):
101 101 "check for collisions between unknown files and files in mctx"
102 102
103 103 error = False
104 104 for f in mctx:
105 105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
106 106 error = True
107 107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
108 108 if error:
109 109 raise util.Abort(_("untracked files in working directory differ "
110 110 "from files in requested revision"))
111 111
112 112 def _remains(f, m, ma, workingctx=False):
113 113 """check whether specified file remains after merge.
114 114
115 115 It is assumed that specified file is not contained in the manifest
116 116 of the other context.
117 117 """
118 118 if f in ma:
119 119 n = m[f]
120 120 if n != ma[f]:
121 121 return True # because it is changed locally
122 122 # even though it doesn't remain, if "remote deleted" is
123 123 # chosen in manifestmerge()
124 124 elif workingctx and n[20:] == "a":
125 125 return True # because it is added locally (linear merge specific)
126 126 else:
127 127 return False # because it is removed remotely
128 128 else:
129 129 return True # because it is added locally
130 130
131 131 def _checkcollision(mctx, extractxs):
132 132 "check for case folding collisions in the destination context"
133 133 folded = {}
134 134 for fn in mctx:
135 135 fold = util.normcase(fn)
136 136 if fold in folded:
137 137 raise util.Abort(_("case-folding collision between %s and %s")
138 138 % (fn, folded[fold]))
139 139 folded[fold] = fn
140 140
141 141 if extractxs:
142 142 wctx, actx = extractxs
143 143 # class to delay looking up copy mapping
144 144 class pathcopies(object):
145 145 @util.propertycache
146 146 def map(self):
147 147 # {dst@mctx: src@wctx} copy mapping
148 148 return copies.pathcopies(wctx, mctx)
149 149 pc = pathcopies()
150 150
151 151 for fn in wctx:
152 152 fold = util.normcase(fn)
153 153 mfn = folded.get(fold, None)
154 154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
155 155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
156 156 _remains(mfn, mctx.manifest(), actx.manifest())):
157 157 raise util.Abort(_("case-folding collision between %s and %s")
158 158 % (mfn, fn))
159 159
160 160 def _forgetremoved(wctx, mctx, branchmerge):
161 161 """
162 162 Forget removed files
163 163
164 164 If we're jumping between revisions (as opposed to merging), and if
165 165 neither the working directory nor the target rev has the file,
166 166 then we need to remove it from the dirstate, to prevent the
167 167 dirstate from listing the file when it is no longer in the
168 168 manifest.
169 169
170 170 If we're merging, and the other revision has removed a file
171 171 that is not present in the working directory, we need to mark it
172 172 as removed.
173 173 """
174 174
175 175 actions = []
176 176 state = branchmerge and 'r' or 'f'
177 177 for f in wctx.deleted():
178 178 if f not in mctx:
179 179 actions.append((f, state))
180 180
181 181 if not branchmerge:
182 182 for f in wctx.removed():
183 183 if f not in mctx:
184 184 actions.append((f, "f"))
185 185
186 186 return actions
187 187
188 188 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
189 189 """
190 190 Merge p1 and p2 with ancestor pa and generate merge action list
191 191
192 192 overwrite = whether we clobber working files
193 193 partial = function to filter file lists
194 194 """
195 195
196 196 def act(msg, m, f, *args):
197 197 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
198 198 actions.append((f, m) + args)
199 199
200 200 actions, copy, movewithdir = [], {}, {}
201 201
202 202 if overwrite:
203 203 pa = p1
204 204 elif pa == p2: # backwards
205 205 pa = p1.p1()
206 206 elif pa and repo.ui.configbool("merge", "followcopies", True):
207 207 ret = copies.mergecopies(repo, p1, p2, pa)
208 208 copy, movewithdir, diverge, renamedelete = ret
209 209 for of, fl in diverge.iteritems():
210 210 act("divergent renames", "dr", of, fl)
211 211 for of, fl in renamedelete.iteritems():
212 212 act("rename and delete", "rd", of, fl)
213 213
214 214 repo.ui.note(_("resolving manifests\n"))
215 215 repo.ui.debug(" overwrite: %s, partial: %s\n"
216 216 % (bool(overwrite), bool(partial)))
217 217 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
218 218
219 219 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
220 220 copied = set(copy.values())
221 221 copied.update(movewithdir.values())
222 222
223 223 if '.hgsubstate' in m1:
224 224 # check whether sub state is modified
225 225 for s in sorted(p1.substate):
226 226 if p1.sub(s).dirty():
227 227 m1['.hgsubstate'] += "+"
228 228 break
229 229
230 230 # Compare manifests
231 231 visit = m1.iteritems()
232 232 if repo.ui.debugflag:
233 233 visit = sorted(visit)
234 234 for f, n in visit:
235 235 if partial and not partial(f):
236 236 continue
237 237 if f in m2:
238 238 n2 = m2[f]
239 239 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
240 240 nol = 'l' not in fl1 + fl2 + fla
241 241 a = ma.get(f, nullid)
242 242 if n == n2 and fl1 == fl2:
243 243 pass # same - keep local
244 244 elif n2 == a and fl2 == fla:
245 245 pass # remote unchanged - keep local
246 246 elif n == a and fl1 == fla: # local unchanged - use remote
247 247 if n == n2: # optimization: keep local content
248 248 act("update permissions", "e", f, fl2)
249 249 else:
250 250 act("remote is newer", "g", f, fl2)
251 251 elif nol and n2 == a: # remote only changed 'x'
252 252 act("update permissions", "e", f, fl2)
253 253 elif nol and n == a: # local only changed 'x'
254 act("remote is newer", "g", f, fl)
254 act("remote is newer", "g", f, fl1)
255 255 else: # both changed something
256 256 act("versions differ", "m", f, f, f, False)
257 257 elif f in copied: # files we'll deal with on m2 side
258 258 pass
259 259 elif f in movewithdir: # directory rename
260 260 f2 = movewithdir[f]
261 261 act("remote renamed directory to " + f2, "d", f, None, f2,
262 262 m1.flags(f))
263 263 elif f in copy:
264 264 f2 = copy[f]
265 265 act("local copied/moved to " + f2, "m", f, f2, f, False)
266 266 elif f in ma: # clean, a different, no remote
267 267 if n != ma[f]:
268 268 if repo.ui.promptchoice(
269 269 _(" local changed %s which remote deleted\n"
270 270 "use (c)hanged version or (d)elete?") % f,
271 271 (_("&Changed"), _("&Delete")), 0):
272 272 act("prompt delete", "r", f)
273 273 else:
274 274 act("prompt keep", "a", f)
275 275 elif n[20:] == "a": # added, no remote
276 276 act("remote deleted", "f", f)
277 277 else:
278 278 act("other deleted", "r", f)
279 279
280 280 visit = m2.iteritems()
281 281 if repo.ui.debugflag:
282 282 visit = sorted(visit)
283 283 for f, n in visit:
284 284 if partial and not partial(f):
285 285 continue
286 286 if f in m1 or f in copied: # files already visited
287 287 continue
288 288 if f in movewithdir:
289 289 f2 = movewithdir[f]
290 290 act("local renamed directory to " + f2, "d", None, f, f2,
291 291 m2.flags(f))
292 292 elif f in copy:
293 293 f2 = copy[f]
294 294 if f2 in m2:
295 295 act("remote copied to " + f, "m",
296 296 f2, f, f, False)
297 297 else:
298 298 act("remote moved to " + f, "m",
299 299 f2, f, f, True)
300 300 elif f not in ma:
301 301 if (not overwrite
302 302 and _checkunknownfile(repo, p1, p2, f)):
303 303 act("remote differs from untracked local",
304 304 "m", f, f, f, False)
305 305 else:
306 306 act("remote created", "g", f, m2.flags(f))
307 307 elif n != ma[f]:
308 308 if repo.ui.promptchoice(
309 309 _("remote changed %s which local deleted\n"
310 310 "use (c)hanged version or leave (d)eleted?") % f,
311 311 (_("&Changed"), _("&Deleted")), 0) == 0:
312 312 act("prompt recreating", "g", f, m2.flags(f))
313 313
314 314 return actions
315 315
316 316 def actionkey(a):
317 317 return a[1] == "r" and -1 or 0, a
318 318
319 319 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
320 320 """apply the merge action list to the working directory
321 321
322 322 wctx is the working copy context
323 323 mctx is the context to be merged into the working copy
324 324 actx is the context of the common ancestor
325 325
326 326 Return a tuple of counts (updated, merged, removed, unresolved) that
327 327 describes how many files were affected by the update.
328 328 """
329 329
330 330 updated, merged, removed, unresolved = 0, 0, 0, 0
331 331 ms = mergestate(repo)
332 332 ms.reset(wctx.p1().node())
333 333 moves = []
334 334 actions.sort(key=actionkey)
335 335
336 336 # prescan for merges
337 337 for a in actions:
338 338 f, m = a[:2]
339 339 if m == "m": # merge
340 340 f2, fd, move = a[2:]
341 341 if fd == '.hgsubstate': # merged internally
342 342 continue
343 343 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
344 344 fcl = wctx[f]
345 345 fco = mctx[f2]
346 346 if mctx == actx: # backwards, use working dir parent as ancestor
347 347 if fcl.parents():
348 348 fca = fcl.p1()
349 349 else:
350 350 fca = repo.filectx(f, fileid=nullrev)
351 351 else:
352 352 fca = fcl.ancestor(fco, actx)
353 353 if not fca:
354 354 fca = repo.filectx(f, fileid=nullrev)
355 355 ms.add(fcl, fco, fca, fd)
356 356 if f != fd and move:
357 357 moves.append(f)
358 358
359 359 audit = repo.wopener.audit
360 360
361 361 # remove renamed files after safely stored
362 362 for f in moves:
363 363 if os.path.lexists(repo.wjoin(f)):
364 364 repo.ui.debug("removing %s\n" % f)
365 365 audit(f)
366 366 util.unlinkpath(repo.wjoin(f))
367 367
368 368 numupdates = len(actions)
369 369 for i, a in enumerate(actions):
370 370 f, m = a[:2]
371 371 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
372 372 unit=_('files'))
373 373 if m == "r": # remove
374 374 repo.ui.note(_("removing %s\n") % f)
375 375 audit(f)
376 376 if f == '.hgsubstate': # subrepo states need updating
377 377 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
378 378 try:
379 379 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
380 380 except OSError, inst:
381 381 repo.ui.warn(_("update failed to remove %s: %s!\n") %
382 382 (f, inst.strerror))
383 383 removed += 1
384 384 elif m == "m": # merge
385 385 if fd == '.hgsubstate': # subrepo states need updating
386 386 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
387 387 overwrite)
388 388 continue
389 389 f2, fd, move = a[2:]
390 390 audit(fd)
391 391 r = ms.resolve(fd, wctx, mctx)
392 392 if r is not None and r > 0:
393 393 unresolved += 1
394 394 else:
395 395 if r is None:
396 396 updated += 1
397 397 else:
398 398 merged += 1
399 399 elif m == "g": # get
400 400 flags = a[2]
401 401 repo.ui.note(_("getting %s\n") % f)
402 402 repo.wwrite(f, mctx.filectx(f).data(), flags)
403 403 updated += 1
404 404 if f == '.hgsubstate': # subrepo states need updating
405 405 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
406 406 elif m == "d": # directory rename
407 407 f2, fd, flags = a[2:]
408 408 if f:
409 409 repo.ui.note(_("moving %s to %s\n") % (f, fd))
410 410 audit(f)
411 411 repo.wwrite(fd, wctx.filectx(f).data(), flags)
412 412 util.unlinkpath(repo.wjoin(f))
413 413 if f2:
414 414 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
415 415 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
416 416 updated += 1
417 417 elif m == "dr": # divergent renames
418 418 fl = a[2]
419 419 repo.ui.warn(_("note: possible conflict - %s was renamed "
420 420 "multiple times to:\n") % f)
421 421 for nf in fl:
422 422 repo.ui.warn(" %s\n" % nf)
423 423 elif m == "rd": # rename and delete
424 424 fl = a[2]
425 425 repo.ui.warn(_("note: possible conflict - %s was deleted "
426 426 "and renamed to:\n") % f)
427 427 for nf in fl:
428 428 repo.ui.warn(" %s\n" % nf)
429 429 elif m == "e": # exec
430 430 flags = a[2]
431 431 audit(f)
432 432 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
433 433 updated += 1
434 434 ms.commit()
435 435 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
436 436
437 437 return updated, merged, removed, unresolved
438 438
439 439 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
440 440 "Calculate the actions needed to merge mctx into tctx"
441 441 actions = []
442 442 folding = not util.checkcase(repo.path)
443 443 if folding:
444 444 # collision check is not needed for clean update
445 445 if (not branchmerge and
446 446 (force or not tctx.dirty(missing=True, branch=False))):
447 447 _checkcollision(mctx, None)
448 448 else:
449 449 _checkcollision(mctx, (tctx, ancestor))
450 450 if not force:
451 451 _checkunknown(repo, tctx, mctx)
452 452 if tctx.rev() is None:
453 453 actions += _forgetremoved(tctx, mctx, branchmerge)
454 454 actions += manifestmerge(repo, tctx, mctx,
455 455 ancestor,
456 456 force and not branchmerge,
457 457 partial)
458 458 return actions
459 459
460 460 def recordupdates(repo, actions, branchmerge):
461 461 "record merge actions to the dirstate"
462 462
463 463 for a in actions:
464 464 f, m = a[:2]
465 465 if m == "r": # remove
466 466 if branchmerge:
467 467 repo.dirstate.remove(f)
468 468 else:
469 469 repo.dirstate.drop(f)
470 470 elif m == "a": # re-add
471 471 if not branchmerge:
472 472 repo.dirstate.add(f)
473 473 elif m == "f": # forget
474 474 repo.dirstate.drop(f)
475 475 elif m == "e": # exec change
476 476 repo.dirstate.normallookup(f)
477 477 elif m == "g": # get
478 478 if branchmerge:
479 479 repo.dirstate.otherparent(f)
480 480 else:
481 481 repo.dirstate.normal(f)
482 482 elif m == "m": # merge
483 483 f2, fd, move = a[2:]
484 484 if branchmerge:
485 485 # We've done a branch merge, mark this file as merged
486 486 # so that we properly record the merger later
487 487 repo.dirstate.merge(fd)
488 488 if f != f2: # copy/rename
489 489 if move:
490 490 repo.dirstate.remove(f)
491 491 if f != fd:
492 492 repo.dirstate.copy(f, fd)
493 493 else:
494 494 repo.dirstate.copy(f2, fd)
495 495 else:
496 496 # We've update-merged a locally modified file, so
497 497 # we set the dirstate to emulate a normal checkout
498 498 # of that file some time in the past. Thus our
499 499 # merge will appear as a normal local file
500 500 # modification.
501 501 if f2 == fd: # file not locally copied/moved
502 502 repo.dirstate.normallookup(fd)
503 503 if move:
504 504 repo.dirstate.drop(f)
505 505 elif m == "d": # directory rename
506 506 f2, fd, flag = a[2:]
507 507 if not f2 and f not in repo.dirstate:
508 508 # untracked file moved
509 509 continue
510 510 if branchmerge:
511 511 repo.dirstate.add(fd)
512 512 if f:
513 513 repo.dirstate.remove(f)
514 514 repo.dirstate.copy(f, fd)
515 515 if f2:
516 516 repo.dirstate.copy(f2, fd)
517 517 else:
518 518 repo.dirstate.normal(fd)
519 519 if f:
520 520 repo.dirstate.drop(f)
521 521
522 522 def update(repo, node, branchmerge, force, partial, ancestor=None,
523 523 mergeancestor=False):
524 524 """
525 525 Perform a merge between the working directory and the given node
526 526
527 527 node = the node to update to, or None if unspecified
528 528 branchmerge = whether to merge between branches
529 529 force = whether to force branch merging or file overwriting
530 530 partial = a function to filter file lists (dirstate not updated)
531 531 mergeancestor = if false, merging with an ancestor (fast-forward)
532 532 is only allowed between different named branches. This flag
533 533 is used by rebase extension as a temporary fix and should be
534 534 avoided in general.
535 535
536 536 The table below shows all the behaviors of the update command
537 537 given the -c and -C or no options, whether the working directory
538 538 is dirty, whether a revision is specified, and the relationship of
539 539 the parent rev to the target rev (linear, on the same named
540 540 branch, or on another named branch).
541 541
542 542 This logic is tested by test-update-branches.t.
543 543
544 544 -c -C dirty rev | linear same cross
545 545 n n n n | ok (1) x
546 546 n n n y | ok ok ok
547 547 n n y * | merge (2) (2)
548 548 n y * * | --- discard ---
549 549 y n y * | --- (3) ---
550 550 y n n * | --- ok ---
551 551 y y * * | --- (4) ---
552 552
553 553 x = can't happen
554 554 * = don't-care
555 555 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
556 556 2 = abort: crosses branches (use 'hg merge' to merge or
557 557 use 'hg update -C' to discard changes)
558 558 3 = abort: uncommitted local changes
559 559 4 = incompatible options (checked in commands.py)
560 560
561 561 Return the same tuple as applyupdates().
562 562 """
563 563
564 564 onode = node
565 565 wlock = repo.wlock()
566 566 try:
567 567 wc = repo[None]
568 568 if node is None:
569 569 # tip of current branch
570 570 try:
571 571 node = repo.branchtip(wc.branch())
572 572 except error.RepoLookupError:
573 573 if wc.branch() == "default": # no default branch!
574 574 node = repo.lookup("tip") # update to tip
575 575 else:
576 576 raise util.Abort(_("branch %s not found") % wc.branch())
577 577 overwrite = force and not branchmerge
578 578 pl = wc.parents()
579 579 p1, p2 = pl[0], repo[node]
580 580 if ancestor:
581 581 pa = repo[ancestor]
582 582 else:
583 583 pa = p1.ancestor(p2)
584 584
585 585 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
586 586
587 587 ### check phase
588 588 if not overwrite and len(pl) > 1:
589 589 raise util.Abort(_("outstanding uncommitted merges"))
590 590 if branchmerge:
591 591 if pa == p2:
592 592 raise util.Abort(_("merging with a working directory ancestor"
593 593 " has no effect"))
594 594 elif pa == p1:
595 595 if not mergeancestor and p1.branch() == p2.branch():
596 596 raise util.Abort(_("nothing to merge"),
597 597 hint=_("use 'hg update' "
598 598 "or check 'hg heads'"))
599 599 if not force and (wc.files() or wc.deleted()):
600 600 raise util.Abort(_("outstanding uncommitted changes"),
601 601 hint=_("use 'hg status' to list changes"))
602 602 for s in sorted(wc.substate):
603 603 if wc.sub(s).dirty():
604 604 raise util.Abort(_("outstanding uncommitted changes in "
605 605 "subrepository '%s'") % s)
606 606
607 607 elif not overwrite:
608 608 if pa == p1 or pa == p2: # linear
609 609 pass # all good
610 610 elif wc.dirty(missing=True):
611 611 raise util.Abort(_("crosses branches (merge branches or use"
612 612 " --clean to discard changes)"))
613 613 elif onode is None:
614 614 raise util.Abort(_("crosses branches (merge branches or update"
615 615 " --check to force update)"))
616 616 else:
617 617 # Allow jumping branches if clean and specific rev given
618 618 pa = p1
619 619
620 620 ### calculate phase
621 621 actions = calculateupdates(repo, wc, p2, pa,
622 622 branchmerge, force, partial)
623 623
624 624 ### apply phase
625 625 if not branchmerge: # just jump to the new rev
626 626 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
627 627 if not partial:
628 628 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
629 629
630 630 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
631 631
632 632 if not partial:
633 633 repo.setparents(fp1, fp2)
634 634 recordupdates(repo, actions, branchmerge)
635 635 if not branchmerge:
636 636 repo.dirstate.setbranch(p2.branch())
637 637 finally:
638 638 wlock.release()
639 639
640 640 if not partial:
641 641 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
642 642 return stats
@@ -1,356 +1,376 b''
1 1 $ "$TESTDIR/hghave" symlink execbit || exit 80
2 2
3 3 $ tellmeabout() {
4 4 > if [ -h $1 ]; then
5 5 > echo $1 is a symlink:
6 6 > $TESTDIR/readlink.py $1
7 7 > elif [ -x $1 ]; then
8 8 > echo $1 is an executable file with content:
9 9 > cat $1
10 10 > else
11 11 > echo $1 is a plain file with content:
12 12 > cat $1
13 13 > fi
14 14 > }
15 15
16 16 $ hg init test1
17 17 $ cd test1
18 18
19 19 $ echo a > a
20 20 $ hg ci -Aqmadd
21 21 $ chmod +x a
22 22 $ hg ci -mexecutable
23 23
24 24 $ hg up -q 0
25 25 $ rm a
26 26 $ ln -s symlink a
27 27 $ hg ci -msymlink
28 28 created new head
29 29
30 30 Symlink is local parent, executable is other:
31 31
32 32 $ hg merge --debug
33 33 searching for copies back to rev 1
34 34 resolving manifests
35 35 overwrite: False, partial: False
36 36 ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
37 37 a: versions differ -> m
38 38 preserving a for resolve of a
39 39 updating: a 1/1 files (100.00%)
40 40 picked tool 'internal:merge' for a (binary False symlink True)
41 41 merging a
42 42 my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
43 43 warning: internal:merge cannot merge symlinks for a
44 44 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
45 45 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
46 46 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
47 47 [1]
48 48
49 49 $ tellmeabout a
50 50 a is a symlink:
51 51 a -> symlink
52 52 $ hg resolve a --tool internal:other
53 53 $ tellmeabout a
54 54 a is an executable file with content:
55 55 a
56 56 $ hg st
57 57 M a
58 58 ? a.orig
59 59
60 60 Symlink is other parent, executable is local:
61 61
62 62 $ hg update -C 1
63 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64
65 65 $ hg merge --debug
66 66 searching for copies back to rev 1
67 67 resolving manifests
68 68 overwrite: False, partial: False
69 69 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
70 70 a: versions differ -> m
71 71 preserving a for resolve of a
72 72 updating: a 1/1 files (100.00%)
73 73 picked tool 'internal:merge' for a (binary False symlink True)
74 74 merging a
75 75 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
76 76 warning: internal:merge cannot merge symlinks for a
77 77 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
78 78 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
79 79 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
80 80 [1]
81 81
82 82 $ tellmeabout a
83 83 a is an executable file with content:
84 84 a
85 85
86 86 Update to link without local change should get us a symlink (issue3316):
87 87
88 88 $ hg up -C 0
89 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 $ hg up
91 91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 92 $ hg st
93 93 ? a.orig
94 94
95 95 Update to link with local change should cause a merge prompt (issue3200):
96 96
97 97 $ hg up -Cq 0
98 98 $ echo data > a
99 99 $ HGMERGE= hg up -y --debug
100 100 searching for copies back to rev 2
101 101 resolving manifests
102 102 overwrite: False, partial: False
103 103 ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
104 104 a: versions differ -> m
105 105 preserving a for resolve of a
106 106 updating: a 1/1 files (100.00%)
107 107 (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
108 108 picked tool 'internal:prompt' for a (binary False symlink True)
109 109 no tool found to merge a
110 110 keep (l)ocal or take (o)ther? l
111 111 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
112 112 $ hg diff --git
113 113 diff --git a/a b/a
114 114 old mode 120000
115 115 new mode 100644
116 116 --- a/a
117 117 +++ b/a
118 118 @@ -1,1 +1,1 @@
119 119 -symlink
120 120 \ No newline at end of file
121 121 +data
122 122
123 123
124 124 Test only 'l' change - happens rarely, except when recovering from situations
125 125 where that was what happened.
126 126
127 127 $ hg init test2
128 128 $ cd test2
129 129 $ printf base > f
130 130 $ hg ci -Aqm0
131 131 $ echo file > f
132 132 $ echo content >> f
133 133 $ hg ci -qm1
134 134 $ hg up -qr0
135 135 $ rm f
136 136 $ ln -s base f
137 137 $ hg ci -qm2
138 138 $ hg merge
139 139 merging f
140 140 warning: internal:merge cannot merge symlinks for f
141 141 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
142 142 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
143 143 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
144 144 [1]
145 145 $ tellmeabout f
146 146 f is a symlink:
147 147 f -> base
148 148
149 149 $ hg up -Cqr1
150 150 $ hg merge
151 151 merging f
152 152 warning: internal:merge cannot merge symlinks for f
153 153 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
154 154 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
155 155 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
156 156 [1]
157 157 $ tellmeabout f
158 158 f is a plain file with content:
159 159 file
160 160 content
161 161
162 162 $ cd ..
163 163
164 164 Test removed 'x' flag merged with change to symlink
165 165
166 166 $ hg init test3
167 167 $ cd test3
168 168 $ echo f > f
169 169 $ chmod +x f
170 170 $ hg ci -Aqm0
171 171 $ chmod -x f
172 172 $ hg ci -qm1
173 173 $ hg up -qr0
174 174 $ rm f
175 175 $ ln -s dangling f
176 176 $ hg ci -qm2
177 177 $ hg merge
178 178 merging f
179 179 warning: internal:merge cannot merge symlinks for f
180 180 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
181 181 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
182 182 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
183 183 [1]
184 184 $ tellmeabout f
185 185 f is a symlink:
186 186 f -> dangling
187 187
188 188 $ hg up -Cqr1
189 189 $ hg merge
190 190 merging f
191 191 warning: internal:merge cannot merge symlinks for f
192 192 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
193 193 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
194 194 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
195 195 [1]
196 196 $ tellmeabout f
197 197 f is a plain file with content:
198 198 f
199 199
200 Test removed 'x' flag merged with content change - both ways
201
202 $ hg up -Cqr0
203 $ echo change > f
204 $ hg ci -qm3
205 $ hg merge -r1
206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 (branch merge, don't forget to commit)
208 $ tellmeabout f
209 f is a plain file with content:
210 change
211
212 $ hg up -qCr1
213 $ hg merge -r3
214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
215 (branch merge, don't forget to commit)
216 $ tellmeabout f
217 f is a plain file with content:
218 change
219
200 220 $ cd ..
201 221
202 222 Test merge with no common ancestor:
203 223 a: just different
204 224 b: x vs -, different (cannot calculate x, cannot ask merge tool)
205 225 c: x vs -, same (cannot calculate x, merge tool is no good)
206 226 d: x vs l, different
207 227 e: x vs l, same
208 228 f: - vs l, different
209 229 g: - vs l, same
210 230 h: l vs l, different
211 231 (where same means the filelog entry is shared and there thus is an ancestor!)
212 232
213 233 $ hg init test4
214 234 $ cd test4
215 235 $ echo 0 > 0
216 236 $ hg ci -Aqm0
217 237
218 238 $ echo 1 > a
219 239 $ echo 1 > b
220 240 $ chmod +x b
221 241 $ echo x > c
222 242 $ chmod +x c
223 243 $ echo 1 > d
224 244 $ chmod +x d
225 245 $ printf x > e
226 246 $ chmod +x e
227 247 $ echo 1 > f
228 248 $ printf x > g
229 249 $ ln -s 1 h
230 250 $ hg ci -qAm1
231 251
232 252 $ hg up -qr0
233 253 $ echo 2 > a
234 254 $ echo 2 > b
235 255 $ echo x > c
236 256 $ ln -s 2 d
237 257 $ ln -s x e
238 258 $ ln -s 2 f
239 259 $ ln -s x g
240 260 $ ln -s 2 h
241 261 $ hg ci -Aqm2
242 262
243 263 $ hg merge
244 264 merging a
245 265 warning: conflicts during merge.
246 266 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
247 267 warning: cannot merge flags for b
248 268 merging b
249 269 warning: conflicts during merge.
250 270 merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
251 271 merging d
252 272 warning: internal:merge cannot merge symlinks for d
253 273 merging d incomplete! (edit conflicts, then use 'hg resolve --mark')
254 274 merging f
255 275 warning: internal:merge cannot merge symlinks for f
256 276 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
257 277 merging h
258 278 warning: internal:merge cannot merge symlinks for h
259 279 merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
260 280 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
261 281 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
262 282 [1]
263 283 $ hg resolve -l
264 284 U a
265 285 U b
266 286 U d
267 287 U f
268 288 U h
269 289 $ tellmeabout a
270 290 a is a plain file with content:
271 291 <<<<<<< local
272 292 2
273 293 =======
274 294 1
275 295 >>>>>>> other
276 296 $ tellmeabout b
277 297 b is a plain file with content:
278 298 <<<<<<< local
279 299 2
280 300 =======
281 301 1
282 302 >>>>>>> other
283 303 $ tellmeabout c
284 304 c is a plain file with content:
285 305 x
286 306 $ tellmeabout d
287 307 d is a symlink:
288 308 d -> 2
289 309 $ tellmeabout e
290 310 e is a symlink:
291 311 e -> x
292 312 $ tellmeabout f
293 313 f is a symlink:
294 314 f -> 2
295 315 $ tellmeabout g
296 316 g is a symlink:
297 317 g -> x
298 318 $ tellmeabout h
299 319 h is a symlink:
300 320 h -> 2
301 321
302 322 $ hg up -Cqr1
303 323 $ hg merge
304 324 merging a
305 325 warning: conflicts during merge.
306 326 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
307 327 warning: cannot merge flags for b
308 328 merging b
309 329 warning: conflicts during merge.
310 330 merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
311 331 merging d
312 332 warning: internal:merge cannot merge symlinks for d
313 333 merging d incomplete! (edit conflicts, then use 'hg resolve --mark')
314 334 merging f
315 335 warning: internal:merge cannot merge symlinks for f
316 336 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
317 337 merging h
318 338 warning: internal:merge cannot merge symlinks for h
319 339 merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
320 340 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
321 341 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
322 342 [1]
323 343 $ tellmeabout a
324 344 a is a plain file with content:
325 345 <<<<<<< local
326 346 1
327 347 =======
328 348 2
329 349 >>>>>>> other
330 350 $ tellmeabout b
331 351 b is an executable file with content:
332 352 <<<<<<< local
333 353 1
334 354 =======
335 355 2
336 356 >>>>>>> other
337 357 $ tellmeabout c
338 358 c is a plain file with content:
339 359 x
340 360 $ tellmeabout d
341 361 d is an executable file with content:
342 362 1
343 363 $ tellmeabout e
344 364 e is an executable file with content:
345 365 x (no-eol)
346 366 $ tellmeabout f
347 367 f is a plain file with content:
348 368 1
349 369 $ tellmeabout g
350 370 g is a plain file with content:
351 371 x (no-eol)
352 372 $ tellmeabout h
353 373 h is a symlink:
354 374 h -> 1
355 375
356 376 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now