[PATCH 3 of 5] localrepo: add memcommit() to commit without working directory

Patrick Mezard pmezard at gmail.com
Mon Jun 2 16:31:38 CDT 2008


# HG changeset patch
# User Patrick Mezard <pmezard at gmail.com>
# Date 1212441833 -7200
# Node ID 50a52215368f4127b5fe720ad49ad214a9ddf8de
# Parent  994a2a32961411354900a07bdb4a7f50ddc3a01d
localrepo: add memcommit() to commit without working directory

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -12,6 +12,23 @@
 import lock, transaction, stat, errno, ui
 import os, revlog, time, util, extensions, hook, inspect
 import match as match_
+
+class fileobject:
+    def name(self):
+        raise NotImplementedError()
+    def data(self):
+        """Return the file raw data, no filters are applied when
+        committed. Raise IOError or OSError if the file does not
+        exist.
+        """
+        raise NotImplementedError()
+    def islink(self):
+        return False
+    def isexec(self):
+        return False
+    def copied(self):
+        """Return the manifest name of the copied file or None."""
+        return None
 
 class localrepository(repo.repository):
     capabilities = util.set(('lookup', 'changegroupsubset'))
@@ -931,6 +948,110 @@
                 self.dirstate.invalidate()
             del tr, lock, wlock
 
+    def memcommit(self, files=[], text="", user=None, date=None,
+               p1=None, p2=None, extra={}):
+        """Commit directly without relying on the working directory.
+
+        files is be a list of fileobject instances.
+        """
+        wlock = lock = tr = None
+        valid = 0 # don't save the dirstate if this isn't set
+
+        fileobjs = {}
+        for f in files:
+            if f.name() in fileobjs:
+                raise util.Abort(_("%s appears twice in commit list!"))
+            fileobjs[f.name()] = f
+        files = list(files)
+        files.sort(lambda a,b: cmp(a.name(), b.name()))
+        
+        try:
+            wlock = self.wlock()
+            lock = self.lock()
+
+            p1, p2 = p1, p2 or nullid
+            update_dirstate = (self.dirstate.parents()[0] == p1)
+
+            c1 = self.changelog.read(p1)
+            c2 = self.changelog.read(p2)
+            m1 = self.manifest.read(c1[0]).copy()
+            m2 = self.manifest.read(c2[0])
+
+            xp1 = hex(p1)
+            if p2 == nullid: xp2 = ''
+            else: xp2 = hex(p2)
+
+            tr = self.transaction()
+            trp = weakref.proxy(tr)
+
+            # check in files
+            new = {}
+            remove = []
+            changed = []
+            linkrev = self.changelog.count()
+            for fobj in files:
+                f = fobj.name()
+                self.ui.note(f + "\n")
+                try:
+                    new[f] = self.filecommit(f, fobj.data(), fobj.copied(), m1,
+                                             m2, linkrev, trp, changed)
+                    new_exec = fobj.isexec()
+                    new_link = fobj.islink()
+                    if ((not changed or changed[-1] != f) and
+                        m2.get(f) != new[f]):
+                        # mention the file in the changelog if some
+                        # flag changed, even if there was no content
+                        # change.
+                        old_exec = m1.execf(f)
+                        old_link = m1.linkf(f)
+                        if old_exec != new_exec or old_link != new_link:
+                            changed.append(f)
+                    m1.set(f, new_exec, new_link)
+                except (OSError, IOError):
+                    remove.append(f)
+
+            # update manifest
+            m1.update(new)
+            remove.sort()
+            removed = []
+
+            for f in remove:
+                if f in m1:
+                    del m1[f]
+                    removed.append(f)
+                elif f in m2:
+                    removed.append(f)
+            mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
+                                   (new, removed))
+
+            # add changeset
+            user = user or self.ui.username()
+
+            lines = [line.rstrip() for line in text.rstrip().splitlines()]
+            while lines and not lines[0]:
+                del lines[0]
+            text = '\n'.join(lines)
+
+            n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
+                                   user, date, extra.copy())
+            self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
+                      parent2=xp2)
+            tr.close()
+
+            if self.branchcache:
+                self.branchtags()
+
+            if update_dirstate:
+                self.dirstate.setparents(n)
+            valid = 1 # our dirstate updates are complete
+
+            self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
+            return n
+        finally:
+            if not valid: # don't save our updated dirstate
+                self.dirstate.invalidate()
+            del tr, lock, wlock
+
     def walk(self, match, node=None):
         '''
         walk recursively through the directory tree or a given


More information about the Mercurial-devel mailing list