--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1693,342 +1693,6 @@ class localrepository(repo.repository):
return newheads - oldheads + 1
- def update(self, node, allow=False, force=False, choose=None,
- moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
- pl = self.dirstate.parents()
- if not force and pl[1] != nullid:
- raise util.Abort(_("outstanding uncommitted merges"))
-
- err = False
-
- p1, p2 = pl[0], node
- pa = self.changelog.ancestor(p1, p2)
- m1n = self.changelog.read(p1)[0]
- m2n = self.changelog.read(p2)[0]
- man = self.manifest.ancestor(m1n, m2n)
- m1 = self.manifest.read(m1n)
- mf1 = self.manifest.readflags(m1n)
- m2 = self.manifest.read(m2n).copy()
- mf2 = self.manifest.readflags(m2n)
- ma = self.manifest.read(man)
- mfa = self.manifest.readflags(man)
-
- modified, added, removed, deleted, unknown = self.changes()
-
- # is this a jump, or a merge? i.e. is there a linear path
- # from p1 to p2?
- linear_path = (pa == p1 or pa == p2)
-
- if allow and linear_path:
- raise util.Abort(_("there is nothing to merge, just use "
- "'hg update' or look at 'hg heads'"))
- if allow and not forcemerge:
- if modified or added or removed:
- raise util.Abort(_("outstanding uncommitted changes"))
-
- if not forcemerge and not force:
- for f in unknown:
- if f in m2:
- t1 = self.wread(f)
- t2 = self.file(f).read(m2[f])
- if cmp(t1, t2) != 0:
- raise util.Abort(_("'%s' already exists in the working"
- " dir and differs from remote") % f)
-
- # resolve the manifest to determine which files
- # we care about merging
- self.ui.note(_("resolving manifests\n"))
- self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
- (force, allow, moddirstate, linear_path))
- self.ui.debug(_(" ancestor %s local %s remote %s\n") %
- (short(man), short(m1n), short(m2n)))
-
- merge = {}
- get = {}
- remove = []
-
- # construct a working dir manifest
- mw = m1.copy()
- mfw = mf1.copy()
- umap = dict.fromkeys(unknown)
-
- for f in added + modified + unknown:
- mw[f] = ""
- mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
-
- if moddirstate and not wlock:
- wlock = self.wlock()
-
- for f in deleted + removed:
- if f in mw:
- del mw[f]
-
- # If we're jumping between revisions (as opposed to merging),
- # and if neither the working directory nor the target rev has
- # the file, then we need to remove it from the dirstate, to
- # prevent the dirstate from listing the file when it is no
- # longer in the manifest.
- if moddirstate and linear_path and f not in m2:
- self.dirstate.forget((f,))
-
- # Compare manifests
- for f, n in mw.iteritems():
- if choose and not choose(f):
- continue
- if f in m2:
- s = 0
-
- # is the wfile new since m1, and match m2?
- if f not in m1:
- t1 = self.wread(f)
- t2 = self.file(f).read(m2[f])
- if cmp(t1, t2) == 0:
- n = m2[f]
- del t1, t2
-
- # are files different?
- if n != m2[f]:
- a = ma.get(f, nullid)
- # are both different from the ancestor?
- if n != a and m2[f] != a:
- self.ui.debug(_(" %s versions differ, resolve\n") % f)
- # merge executable bits
- # "if we changed or they changed, change in merge"
- a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
- mode = ((a^b) | (a^c)) ^ a
- merge[f] = (m1.get(f, nullid), m2[f], mode)
- s = 1
- # are we clobbering?
- # is remote's version newer?
- # or are we going back in time?
- elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
- self.ui.debug(_(" remote %s is newer, get\n") % f)
- get[f] = m2[f]
- s = 1
- elif f in umap or f in added:
- # this unknown file is the same as the checkout
- # we need to reset the dirstate if the file was added
- get[f] = m2[f]
-
- if not s and mfw[f] != mf2[f]:
- if force:
- self.ui.debug(_(" updating permissions for %s\n") % f)
- util.set_exec(self.wjoin(f), mf2[f])
- else:
- a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
- mode = ((a^b) | (a^c)) ^ a
- if mode != b:
- self.ui.debug(_(" updating permissions for %s\n")
- % f)
- util.set_exec(self.wjoin(f), mode)
- del m2[f]
- elif f in ma:
- if n != ma[f]:
- r = _("d")
- if not force and (linear_path or allow):
- r = self.ui.prompt(
- (_(" local changed %s which remote deleted\n") % f) +
- _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
- if r == _("d"):
- remove.append(f)
- else:
- self.ui.debug(_("other deleted %s\n") % f)
- remove.append(f) # other deleted it
- else:
- # file is created on branch or in working directory
- if force and f not in umap:
- self.ui.debug(_("remote deleted %s, clobbering\n") % f)
- remove.append(f)
- elif n == m1.get(f, nullid): # same as parent
- if p2 == pa: # going backwards?
- self.ui.debug(_("remote deleted %s\n") % f)
- remove.append(f)
- else:
- self.ui.debug(_("local modified %s, keeping\n") % f)
- else:
- self.ui.debug(_("working dir created %s, keeping\n") % f)
-
- for f, n in m2.iteritems():
- if choose and not choose(f):
- continue
- if f[0] == "/":
- continue
- if f in ma and n != ma[f]:
- r = _("k")
- if not force and (linear_path or allow):
- r = self.ui.prompt(
- (_("remote changed %s which local deleted\n") % f) +
- _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
- if r == _("k"):
- get[f] = n
- elif f not in ma:
- self.ui.debug(_("remote created %s\n") % f)
- get[f] = n
- else:
- if force or p2 == pa: # going backwards?
- self.ui.debug(_("local deleted %s, recreating\n") % f)
- get[f] = n
- else:
- self.ui.debug(_("local deleted %s\n") % f)
-
- del mw, m1, m2, ma
-
- if force:
- for f in merge:
- get[f] = merge[f][1]
- merge = {}
-
- if linear_path or force:
- # we don't need to do any magic, just jump to the new rev
- branch_merge = False
- p1, p2 = p2, nullid
- else:
- if not allow:
- self.ui.status(_("this update spans a branch"
- " affecting the following files:\n"))
- fl = merge.keys() + get.keys()
- fl.sort()
- for f in fl:
- cf = ""
- if f in merge:
- cf = _(" (resolve)")
- self.ui.status(" %s%s\n" % (f, cf))
- self.ui.warn(_("aborting update spanning branches!\n"))
- self.ui.status(_("(use 'hg merge' to merge across branches"
- " or 'hg update -C' to lose changes)\n"))
- return 1
- branch_merge = True
-
- xp1 = hex(p1)
- xp2 = hex(p2)
- if p2 == nullid: xxp2 = ''
- else: xxp2 = xp2
-
- self.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
-
- # get the files we don't need to change
- files = get.keys()
- files.sort()
- for f in files:
- if f[0] == "/":
- continue
- self.ui.note(_("getting %s\n") % f)
- t = self.file(f).read(get[f])
- self.wwrite(f, t)
- util.set_exec(self.wjoin(f), mf2[f])
- if moddirstate:
- if branch_merge:
- self.dirstate.update([f], 'n', st_mtime=-1)
- else:
- self.dirstate.update([f], 'n')
-
- # merge the tricky bits
- failedmerge = []
- files = merge.keys()
- files.sort()
- for f in files:
- self.ui.status(_("merging %s\n") % f)
- my, other, flag = merge[f]
- ret = self.merge3(f, my, other, xp1, xp2)
- if ret:
- err = True
- failedmerge.append(f)
- util.set_exec(self.wjoin(f), flag)
- if moddirstate:
- if branch_merge:
- # We've done a branch merge, mark this file as merged
- # so that we properly record the merger later
- self.dirstate.update([f], 'm')
- else:
- # We've update-merged a locally modified file, so
- # we set the dirstate to emulate a normal checkout
- # of that file some time in the past. Thus our
- # merge will appear as a normal local file
- # modification.
- f_len = len(self.file(f).read(other))
- self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
-
- remove.sort()
- for f in remove:
- self.ui.note(_("removing %s\n") % f)
- util.audit_path(f)
- try:
- util.unlink(self.wjoin(f))
- except OSError, inst:
- if inst.errno != errno.ENOENT:
- self.ui.warn(_("update failed to remove %s: %s!\n") %
- (f, inst.strerror))
- if moddirstate:
- if branch_merge:
- self.dirstate.update(remove, 'r')
- else:
- self.dirstate.forget(remove)
-
- if moddirstate:
- self.dirstate.setparents(p1, p2)
-
- if show_stats:
- stats = ((len(get), _("updated")),
- (len(merge) - len(failedmerge), _("merged")),
- (len(remove), _("removed")),
- (len(failedmerge), _("unresolved")))
- note = ", ".join([_("%d files %s") % s for s in stats])
- self.ui.status("%s\n" % note)
- if moddirstate:
- if branch_merge:
- if failedmerge:
- self.ui.status(_("There are unresolved merges,"
- " you can redo the full merge using:\n"
- " hg update -C %s\n"
- " hg merge %s\n"
- % (self.changelog.rev(p1),
- self.changelog.rev(p2))))
- else:
- self.ui.status(_("(branch merge, don't forget to commit)\n"))
- elif failedmerge:
- self.ui.status(_("There are unresolved merges with"
- " locally modified files.\n"))
-
- self.hook('update', parent1=xp1, parent2=xxp2, error=int(err))
- return err
-
- def merge3(self, fn, my, other, p1, p2):
- """perform a 3-way merge in the working directory"""
-
- def temp(prefix, node):
- pre = "%s~%s." % (os.path.basename(fn), prefix)
- (fd, name) = tempfile.mkstemp(prefix=pre)
- f = os.fdopen(fd, "wb")
- self.wwrite(fn, fl.read(node), f)
- f.close()
- return name
-
- fl = self.file(fn)
- base = fl.ancestor(my, other)
- a = self.wjoin(fn)
- b = temp("base", base)
- c = temp("other", other)
-
- self.ui.note(_("resolving %s\n") % fn)
- self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
- (fn, short(my), short(other), short(base)))
-
- cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
- or "hgmerge")
- r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
- environ={'HG_FILE': fn,
- 'HG_MY_NODE': p1,
- 'HG_OTHER_NODE': p2,
- 'HG_FILE_MY_NODE': hex(my),
- 'HG_FILE_OTHER_NODE': hex(other),
- 'HG_FILE_BASE_NODE': hex(base)})
- if r:
- self.ui.warn(_("merging %s failed!\n") % fn)
-
- os.unlink(b)
- os.unlink(c)
- return r
-
def verify(self):
filelinkrevs = {}
filenodes = {}