diff mercurial/localrepo.py @ 1680:c21b54f7f7b8

Merge with crew
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Wed, 01 Feb 2006 19:18:15 +0100
parents 675ca845c2f8 dee55c4a4963
children 20b621154e17
line wrap: on
line diff
--- a/mercurial/localrepo.py	Thu Dec 15 18:04:39 2005 +0100
+++ b/mercurial/localrepo.py	Wed Feb 01 19:18:15 2006 +0100
@@ -19,7 +19,8 @@
             while not os.path.isdir(os.path.join(p, ".hg")):
                 oldp = p
                 p = os.path.dirname(p)
-                if p == oldp: raise repo.RepoError(_("no repo found"))
+                if p == oldp:
+                    raise repo.RepoError(_("no repo found"))
             path = p
         self.path = os.path.join(path, ".hg")
 
@@ -44,7 +45,8 @@
         self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
         try:
             self.ui.readconfig(self.join("hgrc"))
-        except IOError: pass
+        except IOError:
+            pass
 
     def hook(self, name, **args):
         def runhook(name, cmd):
@@ -126,16 +128,16 @@
                 r = self.changelog.rev(n)
             except:
                 r = -2 # sort to the beginning of the list if unknown
-            l.append((r,t,n))
+            l.append((r, t, n))
         l.sort()
-        return [(t,n) for r,t,n in l]
+        return [(t, n) for r, t, n in l]
 
     def nodetags(self, node):
         '''return the tags associated with a node'''
         if not self.nodetagscache:
             self.nodetagscache = {}
-            for t,n in self.tags().items():
-                self.nodetagscache.setdefault(n,[]).append(t)
+            for t, n in self.tags().items():
+                self.nodetagscache.setdefault(n, []).append(t)
         return self.nodetagscache.get(node, [])
 
     def lookup(self, key):
@@ -160,7 +162,8 @@
         return os.path.join(self.root, f)
 
     def file(self, f):
-        if f[0] == '/': f = f[1:]
+        if f[0] == '/':
+            f = f[1:]
         return filelog.filelog(self.opener, f)
 
     def getcwd(self):
@@ -226,6 +229,8 @@
         if os.path.exists(self.join("journal")):
             self.ui.status(_("rolling back interrupted transaction\n"))
             transaction.rollback(self.opener, self.join("journal"))
+            self.manifest = manifest.manifest(self.opener)
+            self.changelog = changelog.changelog(self.opener)
             return True
         else:
             self.ui.warn(_("no interrupted transaction available\n"))
@@ -334,8 +339,8 @@
         if update_dirstate:
             self.dirstate.setparents(n, nullid)
 
-    def commit(self, files = None, text = "", user = None, date = None,
-               match = util.always, force=False):
+    def commit(self, files=None, text="", user=None, date=None,
+               match=util.always, force=False):
         commit = []
         remove = []
         changed = []
@@ -350,9 +355,9 @@
                 else:
                     self.ui.warn(_("%s not tracked!\n") % f)
         else:
-            (c, a, d, u) = self.changes(match=match)
-            commit = c + a
-            remove = d
+            modified, added, removed, deleted, unknown = self.changes(match=match)
+            commit = modified + added
+            remove = removed
 
         p1, p2 = self.dirstate.parents()
         c1 = self.changelog.read(p1)
@@ -398,10 +403,6 @@
                 fp1 = m1.get(f, nullid)
                 fp2 = m2.get(f, nullid)
 
-            # is the same revision on two branches of a merge?
-            if fp2 == fp1:
-                fp2 = nullid
-
             if fp2 != nullid:
                 # is one parent an ancestor of the other?
                 fpa = r.ancestor(fp1, fp2)
@@ -411,7 +412,7 @@
                     fp2 = nullid
 
                 # is the file unmodified from the parent?
-                if not meta and t == r.read(fp1):
+                if not meta and t == r.read(fp1) and fp2 == nullid:
                     # record the proper existing parent in manifest
                     # no need to add a revision
                     new[f] = fp1
@@ -423,6 +424,7 @@
             changed.append(f)
 
         # update manifest
+        m1 = m1.copy()
         m1.update(new)
         for f in remove:
             if f in m1:
@@ -449,7 +451,7 @@
             text = edittext
 
         user = user or self.ui.username()
-        n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
+        n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
         tr.close()
 
         self.dirstate.setparents(n)
@@ -474,9 +476,12 @@
             for src, fn in self.dirstate.walk(files, match):
                 yield src, fn
 
-    def changes(self, node1 = None, node2 = None, files = [],
-                match = util.always):
-        mf2, u = None, []
+    def changes(self, node1=None, node2=None, files=[], match=util.always):
+        """return changes between two nodes or node and working directory
+
+        If node1 is None, use the first dirstate parent instead.
+        If node2 is None, compare node1 with working directory.
+        """
 
         def fcmp(fn, mf):
             t1 = self.wread(fn)
@@ -484,7 +489,8 @@
             return cmp(t1, t2)
 
         def mfmatches(node):
-            mf = dict(self.manifest.read(node))
+            change = self.changelog.read(node)
+            mf = dict(self.manifest.read(change[0]))
             for fn in mf.keys():
                 if not match(fn):
                     del mf[fn]
@@ -496,60 +502,53 @@
                 wlock = self.wlock(wait=0)
             except lock.LockHeld:
                 wlock = None
-            l, c, a, d, u = self.dirstate.changes(files, match)
+            lookup, modified, added, removed, deleted, unknown = (
+                self.dirstate.changes(files, match))
 
             # are we comparing working dir against its parent?
             if not node1:
-                if l:
+                if lookup:
                     # do a full compare of any files that might have changed
-                    change = self.changelog.read(self.dirstate.parents()[0])
-                    mf2 = mfmatches(change[0])
-                    for f in l:
+                    mf2 = mfmatches(self.dirstate.parents()[0])
+                    for f in lookup:
                         if fcmp(f, mf2):
-                            c.append(f)
+                            modified.append(f)
                         elif wlock is not None:
                             self.dirstate.update([f], "n")
-
-                for l in c, a, d, u:
-                    l.sort()
-
-                return (c, a, d, u)
-
-        # are we comparing working dir against non-tip?
-        # generate a pseudo-manifest for the working dir
-        if not node2:
-            if not mf2:
-                change = self.changelog.read(self.dirstate.parents()[0])
-                mf2 = mfmatches(change[0])
-            for f in a + c + l:
-                mf2[f] = ""
-            for f in d:
-                if f in mf2: del mf2[f]
+            else:
+                # we are comparing working dir against non-parent
+                # generate a pseudo-manifest for the working dir
+                mf2 = mfmatches(self.dirstate.parents()[0])
+                for f in lookup + modified + added:
+                    mf2[f] = ""
+                for f in removed:
+                    if f in mf2:
+                        del mf2[f]
         else:
-            change = self.changelog.read(node2)
-            mf2 = mfmatches(change[0])
+            # we are comparing two revisions
+            deleted, unknown = [], []
+            mf2 = mfmatches(node2)
 
-        # flush lists from dirstate before comparing manifests
-        c, a = [], []
+        if node1:
+            # flush lists from dirstate before comparing manifests
+            modified, added = [], []
 
-        change = self.changelog.read(node1)
-        mf1 = mfmatches(change[0])
+            mf1 = mfmatches(node1)
 
-        for fn in mf2:
-            if mf1.has_key(fn):
-                if mf1[fn] != mf2[fn]:
-                    if mf2[fn] != "" or fcmp(fn, mf1):
-                        c.append(fn)
-                del mf1[fn]
-            else:
-                a.append(fn)
+            for fn in mf2:
+                if mf1.has_key(fn):
+                    if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
+                        modified.append(fn)
+                    del mf1[fn]
+                else:
+                    added.append(fn)
 
-        d = mf1.keys()
+            removed = mf1.keys()
 
-        for l in c, a, d, u:
+        # sort and return results:
+        for l in modified, added, removed, deleted, unknown:
             l.sort()
-
-        return (c, a, d, u)
+        return (modified, added, removed, deleted, unknown)
 
     def add(self, list):
         wlock = self.wlock()
@@ -558,7 +557,8 @@
             if not os.path.exists(p):
                 self.ui.warn(_("%s does not exist!\n") % f)
             elif not os.path.isfile(p):
-                self.ui.warn(_("%s not added: only files supported currently\n") % f)
+                self.ui.warn(_("%s not added: only files supported currently\n")
+                             % f)
             elif self.dirstate.state(f) in 'an':
                 self.ui.warn(_("%s already tracked!\n") % f)
             else:
@@ -578,7 +578,8 @@
                 try:
                     util.unlink(self.wjoin(f))
                 except OSError, inst:
-                    if inst.errno != errno.ENOENT: raise
+                    if inst.errno != errno.ENOENT:
+                        raise
         wlock = self.wlock()
         for f in list:
             p = self.wjoin(f)
@@ -733,7 +734,8 @@
         return out
 
     def branches(self, nodes):
-        if not nodes: nodes = [self.changelog.tip()]
+        if not nodes:
+            nodes = [self.changelog.tip()]
         b = []
         for n in nodes:
             t = n
@@ -805,7 +807,8 @@
                 if n[0] in seen:
                     continue
 
-                self.ui.debug(_("examining %s:%s\n") % (short(n[0]), short(n[1])))
+                self.ui.debug(_("examining %s:%s\n")
+                              % (short(n[0]), short(n[1])))
                 if n[0] == nullid:
                     break
                 if n in seenbranch:
@@ -841,7 +844,8 @@
                         self.ui.debug(_("received %s:%s\n") %
                                       (short(b[0]), short(b[1])))
                         if b[0] in m:
-                            self.ui.debug(_("found base node %s\n") % short(b[0]))
+                            self.ui.debug(_("found base node %s\n")
+                                          % short(b[0]))
                             base[b[0]] = 1
                         elif b[0] not in seen:
                             unknown.append(b)
@@ -914,7 +918,7 @@
         # this is the set of all roots we have to push
         return subset
 
-    def pull(self, remote, heads = None):
+    def pull(self, remote, heads=None):
         lock = self.lock()
 
         # if we have an empty repo, fetch everything
@@ -1199,8 +1203,11 @@
                 filerevlog = self.file(fname)
                 # Toss out the filenodes that the recipient isn't really
                 # missing.
-                prune_filenodes(fname, filerevlog)
-                msng_filenode_lst = msng_filenode_set[fname].keys()
+                if msng_filenode_set.has_key(fname):
+                    prune_filenodes(fname, filerevlog)
+                    msng_filenode_lst = msng_filenode_set[fname].keys()
+                else:
+                    msng_filenode_lst = []
                 # If any filenodes are left, generate the group for them,
                 # otherwise don't bother.
                 if len(msng_filenode_lst) > 0:
@@ -1214,8 +1221,9 @@
                                              lookup_filenode_link_func(fname))
                     for chnk in group:
                         yield chnk
-                # Don't need this anymore, toss it to free memory.
-                del msng_filenode_set[fname]
+                if msng_filenode_set.has_key(fname):
+                    # Don't need this anymore, toss it to free memory.
+                    del msng_filenode_set[fname]
             # Signal that no more groups are left.
             yield struct.pack(">l", 0)
 
@@ -1285,9 +1293,11 @@
 
         def getchunk():
             d = source.read(4)
-            if not d: return ""
+            if not d:
+                return ""
             l = struct.unpack(">l", d)[0]
-            if l <= 4: return ""
+            if l <= 4:
+                return ""
             d = source.read(l - 4)
             if len(d) < l - 4:
                 raise repo.RepoError(_("premature EOF reading chunk"
@@ -1298,7 +1308,8 @@
         def getgroup():
             while 1:
                 c = getchunk()
-                if not c: break
+                if not c:
+                    break
                 yield c
 
         def csmap(x):
@@ -1308,7 +1319,8 @@
         def revmap(x):
             return self.changelog.rev(x)
 
-        if not source: return
+        if not source:
+            return
         changesets = files = revisions = 0
 
         tr = self.transaction()
@@ -1333,7 +1345,8 @@
         self.ui.status(_("adding file changes\n"))
         while 1:
             f = getchunk()
-            if not f: break
+            if not f:
+                break
             self.ui.debug(_("adding %s revisions\n") % f)
             fl = self.file(f)
             o = fl.count()
@@ -1354,7 +1367,7 @@
 
         if changesets > 0:
             if not self.hook("changegroup",
-                              node=hex(self.changelog.node(cor+1))):
+                             node=hex(self.changelog.node(cor+1))):
                 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
                 return 1
 
@@ -1370,6 +1383,8 @@
             self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
             return 1
 
+        err = False
+
         p1, p2 = pl[0], node
         pa = self.changelog.ancestor(p1, p2)
         m1n = self.changelog.read(p1)[0]
@@ -1377,29 +1392,32 @@
         man = self.manifest.ancestor(m1n, m2n)
         m1 = self.manifest.read(m1n)
         mf1 = self.manifest.readflags(m1n)
-        m2 = self.manifest.read(m2n)
+        m2 = self.manifest.read(m2n).copy()
         mf2 = self.manifest.readflags(m2n)
         ma = self.manifest.read(man)
         mfa = self.manifest.readflags(man)
 
-        (c, a, d, u) = self.changes()
-
-        if allow and not forcemerge:
-            if c or a or d:
-                raise util.Abort(_("outstanding uncommited changes"))
-        if not forcemerge and not force:
-            for f in u:
-                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)
+        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'"))
+        if allow and not forcemerge:
+            if modified or added or removed:
+                raise util.Abort(_("outstanding uncommited 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"))
@@ -1415,17 +1433,18 @@
         # construct a working dir manifest
         mw = m1.copy()
         mfw = mf1.copy()
-        umap = dict.fromkeys(u)
+        umap = dict.fromkeys(unknown)
 
-        for f in a + c + u:
+        for f in added + modified + unknown:
             mw[f] = ""
             mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
 
         if moddirstate:
             wlock = self.wlock()
 
-        for f in d:
-            if f in mw: del mw[f]
+        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
@@ -1437,7 +1456,8 @@
 
         # Compare manifests
         for f, n in mw.iteritems():
-            if choose and not choose(f): continue
+            if choose and not choose(f):
+                continue
             if f in m2:
                 s = 0
 
@@ -1480,7 +1500,8 @@
                         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)
+                            self.ui.debug(_(" updating permissions for %s\n")
+                                          % f)
                             util.set_exec(self.wjoin(f), mode)
                 del m2[f]
             elif f in ma:
@@ -1510,15 +1531,18 @@
                     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 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
+                if r == _("k"):
+                    get[f] = n
             elif f not in ma:
                 self.ui.debug(_("remote created %s\n") % f)
                 get[f] = n
@@ -1548,7 +1572,8 @@
                 fl.sort()
                 for f in fl:
                     cf = ""
-                    if f in merge: cf = _(" (resolve)")
+                    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 update -m to merge across branches"
@@ -1560,7 +1585,8 @@
         files = get.keys()
         files.sort()
         for f in files:
-            if f[0] == "/": continue
+            if f[0] == "/":
+                continue
             self.ui.note(_("getting %s\n") % f)
             t = self.file(f).read(get[f])
             self.wwrite(f, t)
@@ -1577,7 +1603,9 @@
         for f in files:
             self.ui.status(_("merging %s\n") % f)
             my, other, flag = merge[f]
-            self.merge3(f, my, other)
+            ret = self.merge3(f, my, other)
+            if ret:
+                err = True
             util.set_exec(self.wjoin(f), flag)
             if moddirstate:
                 if branch_merge:
@@ -1610,6 +1638,7 @@
 
         if moddirstate:
             self.dirstate.setparents(p1, p2)
+        return err
 
     def merge3(self, fn, my, other):
         """perform a 3-way merge in the working directory"""
@@ -1640,6 +1669,7 @@
 
         os.unlink(b)
         os.unlink(c)
+        return r
 
     def verify(self):
         filelinkrevs = {}
@@ -1652,11 +1682,17 @@
             self.ui.warn(msg + "\n")
             errors[0] += 1
 
+        def checksize(obj, name):
+            d = obj.checksize()
+            if d[0]:
+                err(_("%s data length off by %d bytes") % (name, d[0]))
+            if d[1]:
+                err(_("%s index contains %d extra bytes") % (name, d[1]))
+
         seen = {}
         self.ui.status(_("checking changesets\n"))
-        d = self.changelog.checksize()
-        if d:
-            err(_("changeset data short %d bytes") % d)
+        checksize(self.changelog, "changelog")
+
         for i in range(self.changelog.count()):
             changesets += 1
             n = self.changelog.node(i)
@@ -1686,9 +1722,8 @@
 
         seen = {}
         self.ui.status(_("checking manifests\n"))
-        d = self.manifest.checksize()
-        if d:
-            err(_("manifest data short %d bytes") % d)
+        checksize(self.manifest, "manifest")
+
         for i in range(self.manifest.count()):
             n = self.manifest.node(i)
             l = self.manifest.linkrev(n)
@@ -1723,7 +1758,7 @@
 
         self.ui.status(_("crosschecking files in changesets and manifests\n"))
 
-        for m,c in neededmanifests.items():
+        for m, c in neededmanifests.items():
             err(_("Changeset %s refers to unknown manifest %s") %
                 (short(m), short(c)))
         del neededmanifests
@@ -1740,14 +1775,13 @@
         ff = filenodes.keys()
         ff.sort()
         for f in ff:
-            if f == "/dev/null": continue
+            if f == "/dev/null":
+                continue
             files += 1
             fl = self.file(f)
-            d = fl.checksize()
-            if d:
-                err(_("%s file data short %d bytes") % (f, d))
+            checksize(fl, f)
 
-            nodes = { nullid: 1 }
+            nodes = {nullid: 1}
             seen = {}
             for i in range(fl.count()):
                 revisions += 1