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@xxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html