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