summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/git/cmd.py78
-rw-r--r--test/git/test_git.py35
2 files changed, 109 insertions, 4 deletions
diff --git a/lib/git/cmd.py b/lib/git/cmd.py
index 867baee7..92ef3bda 100644
--- a/lib/git/cmd.py
+++ b/lib/git/cmd.py
@@ -34,7 +34,6 @@ class Git(object):
of the command to stdout.
Set its value to 'full' to see details about the returned values.
"""
-
class AutoInterrupt(object):
"""
Kill/Interrupt the stored process instance once this instance goes out of scope. It is
@@ -50,7 +49,7 @@ class Git(object):
# did the process finish already so we have a return code ?
if self.proc.poll() is not None:
return
-
+
# try to kill it
try:
os.kill(self.proc.pid, 2) # interrupt signal
@@ -73,6 +72,10 @@ class Git(object):
"""
super(Git, self).__init__()
self.git_dir = git_dir
+
+ # cached command slots
+ self.cat_file_header = None
+ self.cat_file_all = None
def __getattr__(self, name):
"""
@@ -262,3 +265,74 @@ class Git(object):
call.extend(args)
return self.execute(call, **_kwargs)
+
+ def _parse_object_header(self, header_line):
+ """
+ ``header_line``
+ <hex_sha> type_string size_as_int
+
+ Returns
+ (type_string, size_as_int)
+
+ Raises
+ ValueError if the header contains indication for an error due to incorrect
+ input sha
+ """
+ tokens = header_line.split()
+ if len(tokens) != 3:
+ raise ValueError( "SHA named %s could not be resolved" % tokens[0] )
+
+ return (tokens[1], int(tokens[2]))
+
+ def __prepare_ref(self, ref):
+ # required for command to separate refs on stdin
+ refstr = str(ref) # could be ref-object
+ if refstr.endswith("\n"):
+ return refstr
+ return refstr + "\n"
+
+ def __get_persistent_cmd(self, attr_name, cmd_name, *args,**kwargs):
+ cur_val = getattr(self, attr_name)
+ if cur_val is not None:
+ return cur_val
+
+ options = { "istream" : subprocess.PIPE, "as_process" : True }
+ options.update( kwargs )
+
+ cmd = self._call_process( cmd_name, *args, **options )
+ setattr(self, attr_name, cmd )
+ return cmd
+
+ def __get_object_header(self, cmd, ref):
+ cmd.stdin.write(self.__prepare_ref(ref))
+ cmd.stdin.flush()
+ return self._parse_object_header(cmd.stdout.readline())
+
+ def get_object_header(self, ref):
+ """
+ Use this method to quickly examine the type and size of the object behind
+ the given ref.
+
+ NOTE
+ The method will only suffer from the costs of command invocation
+ once and reuses the command in subsequent calls.
+
+ Return:
+ (type_string, size_as_int)
+ """
+ cmd = self.__get_persistent_cmd("cat_file_header", "cat_file", batch_check=True)
+ return self.__get_object_header(cmd, ref)
+
+ def get_object_data(self, ref):
+ """
+ As get_object_header, but returns object data as well
+
+ Return:
+ (type_string, size_as_int,data_string)
+ """
+ cmd = self.__get_persistent_cmd("cat_file_all", "cat_file", batch=True)
+ typename, size = self.__get_object_header(cmd, ref)
+ data = cmd.stdout.read(size)
+ cmd.stdout.read(1) # finishing newlines
+
+ return (typename, size, data)
diff --git a/test/git/test_git.py b/test/git/test_git.py
index c9f399cc..591d5939 100644
--- a/test/git/test_git.py
+++ b/test/git/test_git.py
@@ -10,8 +10,7 @@ from git import Git, GitCommandError
class TestGit(object):
def setup(self):
- base = os.path.join(os.path.dirname(__file__), "../..")
- self.git = Git(base)
+ self.git = Git(GIT_REPO)
@patch_object(Git, 'execute')
def test_call_process_calls_execute(self, git):
@@ -56,3 +55,35 @@ class TestGit(object):
# this_should_not_be_ignored=False implies it *should* be ignored
output = self.git.version(pass_this_kwarg=False)
assert_true("pass_this_kwarg" not in git.call_args[1])
+
+ def test_persistent_cat_file_command(self):
+ # read header only
+ import subprocess as sp
+ hexsha = "b2339455342180c7cc1e9bba3e9f181f7baa5167"
+ g = self.git.cat_file(batch_check=True, istream=sp.PIPE,as_process=True)
+ g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
+ g.stdin.flush()
+ obj_info = g.stdout.readline()
+
+ # read header + data
+ g = self.git.cat_file(batch=True, istream=sp.PIPE,as_process=True)
+ g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
+ g.stdin.flush()
+ obj_info_two = g.stdout.readline()
+ assert obj_info == obj_info_two
+
+ # read data - have to read it in one large chunk
+ size = int(obj_info.split()[2])
+ data = g.stdout.read(size)
+ terminating_newline = g.stdout.read(1)
+
+ # now we should be able to read a new object
+ g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
+ g.stdin.flush()
+ assert g.stdout.readline() == obj_info
+
+
+ # same can be achived using the respective command functions
+ typename, size = self.git.get_object_header(hexsha)
+ typename_two, size_two, data = self.git.get_object_data(hexsha)
+ assert typename == typename_two and size == size_two