git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[StGit PATCH] Read several objects at once with git cat-file --batch

From
Karl Hasselström <kha@treskal.com>
Date
Aug 8, 2008, 08:07 UTC
Message-ID
<20080808080614.23424.28169.stgit@yoghurt>
In-Reply-To
<20080808082728.GA24017@diana.vm.bytemark.co.uk>

Instead of spawning a separate cat-file process for every blob and commit we want to read. This speeds things up slightly: about 6-8% when uncommitting and rebasing 1470 linux-kernel patches (perftest.py rebase-newrebase-add-file-linux).

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/lib/git.py |   34 +++++++++++++++++++++++++++++++++-
 stgit/run.py     |   17 +++++++++++++++++
 2 files changed, 50 insertions(+), 1 deletions(-)
diff --git a/stgit/lib/git.py b/stgit/lib/git.py
index 648e190..95efd9a 100644
--- a/stgit/lib/git.py
+++ b/stgit/lib/git.py
@@ -520,6 +520,37 @@ class RunWithEnvCwd(RunWithEnv):
         @param args: Command and argument vector"""
         return RunWithEnv.run(self, args, self.env_in_cwd)
 
+class CatFileProcess(object):
+    def __init__(self, repo):
+        self.__repo = repo
+        self.__proc = None
+    def __get_process(self):
+        if not self.__proc:
+            self.__proc = self.__repo.run(['git', 'cat-file', '--batch']
+                                          ).run_background()
+        return self.__proc
+    def cat_file(self, sha1):
+        p = self.__get_process()
+        p.stdin.write('%s\n' % sha1)
+        p.stdin.flush()
+
+        # Read until we have the entire status line.
+        s = ''
+        while not '\n' in s:
+            s += os.read(p.stdout.fileno(), 4096)
+        h, b = s.split('\n', 1)
+        if h == '%s missing' % sha1:
+            raise SomeException()
+        hash, type, length = h.split()
+        assert hash == sha1
+        length = int(length)
+
+        # Read until we have the entire object plus the trailing
+        # newline.
+        while len(b) < length + 1:
+            b += os.read(p.stdout.fileno(), 4096)
+        return type, b[:-1]
+
 class Repository(RunWithEnv):
     """Represents a git repository."""
     def __init__(self, directory):
@@ -531,6 +562,7 @@ class Repository(RunWithEnv):
         self.__default_index = None
         self.__default_worktree = None
         self.__default_iw = None
+        self.__catfile = CatFileProcess(self)
     env = property(lambda self: { 'GIT_DIR': self.__git_dir })
     @classmethod
     def default(cls):
@@ -580,7 +612,7 @@ class Repository(RunWithEnv):
     directory = property(lambda self: self.__git_dir)
     refs = property(lambda self: self.__refs)
     def cat_object(self, sha1):
-        return self.run(['git', 'cat-file', '-p', sha1]).raw_output()
+        return self.__catfile.cat_file(sha1)[1]
     def rev_parse(self, rev):
         try:
             return self.get_commit(self.run(
diff --git a/stgit/run.py b/stgit/run.py
index 7493ed3..ccca059 100644
--- a/stgit/run.py
+++ b/stgit/run.py
@@ -130,6 +130,19 @@ class Run:
             raise self.exc('%s failed: %s' % (self.__cmd[0], e))
         self.__log_end(self.exitcode)
         self.__check_exitcode()
+    def __run_background(self):
+        """Run in background."""
+        assert self.__indata == None
+        try:
+            p = subprocess.Popen(self.__cmd, env = self.__env, cwd = self.__cwd,
+                                 stdin = subprocess.PIPE,
+                                 stdout = subprocess.PIPE,
+                                 stderr = subprocess.PIPE)
+        except OSError, e:
+            raise self.exc('%s failed: %s' % (self.__cmd[0], e))
+        self.stdin = p.stdin
+        self.stdout = p.stdout
+        self.stderr = p.stderr
     def returns(self, retvals):
         self.__good_retvals = retvals
         return self
@@ -181,6 +194,10 @@ class Run:
     def run(self):
         """Just run, with no IO redirection."""
         self.__run_noio()
+    def run_background(self):
+        """Run as a background process."""
+        self.__run_background()
+        return self
     def xargs(self, xargs):
         """Just run, with no IO redirection. The extra arguments are
         appended to the command line a few at a time; the command is
Previous: Karl HasselströmNext: Catalin Marinas
Message 2 of 7 in “[StGit] kha/{safe,experimental} updated”
  1. Karl HasselströmAug 8, 2008
  2. Read several objects at once with git cat-file --batchKarl Hasselström, Aug 8, 2008
  3. Catalin MarinasAug 10, 2008
  4. Karl HasselströmAug 10, 2008
  5. Catalin MarinasAug 13, 2008
  6. Karl HasselströmAug 17, 2008
  7. Catalin MarinasAug 21, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.