From aa5e366889103172a9829730de1ba26d3dcbc01b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 20 Oct 2009 10:11:16 +0200 Subject: Moved specialized methods like dashify, touch and is_git_dir to module to the respective modules that use them fixed repo.daemon_export which did not work anymore due to incorrect touch implementation and wrong property names --- lib/git/cmd.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 500fcd93..d04a2bd0 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -20,6 +20,9 @@ extra = {} if sys.platform == 'win32': extra = {'shell': True} +def dashify(string): + return string.replace('_', '-') + class Git(object): """ The Git class manages communication with the Git binary. -- cgit v1.2.1 From 3c9f55dd8e6697ab2f9eaf384315abd4cbefad38 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 21 Oct 2009 22:53:51 +0200 Subject: remote: Added fetch, pull, push methods to the interface to make these operations more convenient, like repo.remotes.origin.fetch --- lib/git/cmd.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index d04a2bd0..485a1553 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -261,7 +261,9 @@ class Git(object): such as in 'ls_files' to call 'ls-files'. ``args`` - is the list of arguments + is the list of arguments. If None is included, it will be pruned. + This allows your commands to call git more conveniently as None + is realized as non-existent ``kwargs`` is a dict of keyword arguments. @@ -287,7 +289,7 @@ class Git(object): # Prepare the argument list opt_args = self.transform_kwargs(**kwargs) - ext_args = self.__unpack_args(args) + ext_args = self.__unpack_args([a for a in args if a is not None]) args = opt_args + ext_args call = ["git", dashify(method)] -- cgit v1.2.1 From 33fa178eeb7bf519f5fff118ebc8e27e76098363 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 11:04:30 +0200 Subject: added Object.data_stream property allowing to stream object data directly.Considering the implementation of the git commnd which temporarily keeps it in a cache, it doesnt make a huge diffence as the data is kept in memory while streaming. Only good thing is that it is in a different process so python will never see it if done properly --- lib/git/cmd.py | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 485a1553..836b599d 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -144,6 +144,7 @@ class Git(object): wrapper that will interrupt the process once it goes out of scope. If you use the command in iterators, you should pass the whole process instance instead of a single stream. + ``output_stream`` If set to a file-like object, data produced by the git command will be output to the given stream directly. -- cgit v1.2.1 From f62c9b9c0c9bda792c3fa531b18190e97eb53509 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 12:24:36 +0200 Subject: Git.cmd: removed with_raw_output option repo.archive: made it work with new way of custom output streams added test for repo.archive which was missing for some reason --- lib/git/cmd.py | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 836b599d..27eb20e8 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -13,7 +13,7 @@ from errors import GitCommandError GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', - 'with_exceptions', 'with_raw_output', 'as_process', + 'with_exceptions', 'as_process', 'output_stream' ) extra = {} @@ -105,7 +105,6 @@ class Git(object): with_keep_cwd=False, with_extended_output=False, with_exceptions=True, - with_raw_output=False, as_process=False, output_stream=None ): @@ -132,13 +131,10 @@ class Git(object): ``with_exceptions`` Whether to raise an exception when git returns a non-zero status. - ``with_raw_output`` - Whether to avoid stripping off trailing whitespace. - ``as_process`` Whether to return the created process instance directly from which - streams can be read on demand. This will render with_extended_output, - with_exceptions and with_raw_output ineffective - the caller will have + streams can be read on demand. This will render with_extended_output and + with_exceptions ineffective - the caller will have to deal with the details himself. It is important to note that the process will be placed into an AutoInterrupt wrapper that will interrupt the process once it goes out of scope. If you @@ -147,13 +143,21 @@ class Git(object): ``output_stream`` If set to a file-like object, data produced by the git command will be - output to the given stream directly. - Otherwise a new file will be opened. + output to the given stream directly. + This feature only has any effect if as_process is False. Processes will + always be created with a pipe due to issues with subprocess. + This merely is a workaround as data will be copied from the + output pipe to the given output stream directly. + Returns:: - str(output) # extended_output = False (Default) + str(output) # extended_output = False (Default) tuple(int(status), str(stdout), str(stderr)) # extended_output = True + + if ouput_stream is True, the stdout value will be your output stream: + output_stream # extended_output = False + tuple(int(status), output_stream, str(stderr))# extended_output = True Raise GitCommandError @@ -171,16 +175,12 @@ class Git(object): else: cwd=self.git_dir - ostream = subprocess.PIPE - if output_stream is not None: - ostream = output_stream - # Start the process proc = subprocess.Popen(command, cwd=cwd, stdin=istream, stderr=subprocess.PIPE, - stdout=ostream, + stdout=subprocess.PIPE, **extra ) @@ -190,18 +190,25 @@ class Git(object): # Wait for the process to return status = 0 try: - stdout_value = proc.stdout.read() + if output_stream is None: + stdout_value = proc.stdout.read() + else: + max_chunk_size = 1024*64 + while True: + chunk = proc.stdout.read(max_chunk_size) + output_stream.write(chunk) + if len(chunk) < max_chunk_size: + break + # END reading output stream + stdout_value = output_stream + # END stdout handling stderr_value = proc.stderr.read() + # waiting here should do nothing as we have finished stream reading status = proc.wait() finally: proc.stdout.close() proc.stderr.close() - # Strip off trailing whitespace by default - if not with_raw_output: - stdout_value = stdout_value.rstrip() - stderr_value = stderr_value.rstrip() - if with_exceptions and status != 0: raise GitCommandError(command, status, stderr_value) -- cgit v1.2.1 From 4fe5cfa0e063a8d51a1eb6f014e2aaa994e5e7d4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 12:28:04 +0200 Subject: Stream_data streams data to a given output stream most efficiently with a low memory footprint. Still, the git-cat-file command keeps all data in an interal buffer instead of streaming it directly. This is a git design issue though, and will be hard to address without some proper git-hacking. Conflicts: lib/git/cmd.py --- lib/git/cmd.py | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 27eb20e8..88d6008a 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -183,7 +183,6 @@ class Git(object): stdout=subprocess.PIPE, **extra ) - if as_process: return self.AutoInterrupt(proc) -- cgit v1.2.1 From c53eeaca19163b2b5484304a9ecce3ef92164d70 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 14:13:17 +0200 Subject: git cmd fix: After removing the with_raw_output flag, I actually did the wrong thing by _not_ stripping the final newline at the end of all git commands we use. This is the default now which cannot be changed - perhaps its wrong to remove it, but in way you'd always want the final newline stripped unless you get data directly, but there are better ways to do that ( blob.data, blob.data_stream, blob.stream_data ) --- lib/git/cmd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 88d6008a..d674224c 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -190,7 +190,7 @@ class Git(object): status = 0 try: if output_stream is None: - stdout_value = proc.stdout.read() + stdout_value = proc.stdout.read().rstrip() # strip trailing "\n" else: max_chunk_size = 1024*64 while True: @@ -201,7 +201,7 @@ class Git(object): # END reading output stream stdout_value = output_stream # END stdout handling - stderr_value = proc.stderr.read() + stderr_value = proc.stderr.read().rstrip() # strip trailing "\n" # waiting here should do nothing as we have finished stream reading status = proc.wait() finally: -- cgit v1.2.1 From 78a46c432b31a0ea4c12c391c404cd128df4d709 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 26 Oct 2009 19:51:00 +0100 Subject: cmd.wait: AutoKill wrapped process will automatically raise on errors to unify error handling amongst clients using the process directly. It might be needed to add a flag allowing to easily override that --- lib/git/cmd.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index d674224c..4b4b84af 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -42,12 +42,16 @@ class Git(object): """ Kill/Interrupt the stored process instance once this instance goes out of scope. It is used to prevent processes piling up in case iterators stop reading. - Besides all attributes are wired through to the contained process object + Besides all attributes are wired through to the contained process object. + + The wait method was overridden to perform automatic status code checking + and possibly raise. """ - __slots__= "proc" + __slots__= ("proc", "args") - def __init__(self, proc ): + def __init__(self, proc, args ): self.proc = proc + self.args = args def __del__(self): # did the process finish already so we have a return code ? @@ -64,6 +68,20 @@ class Git(object): def __getattr__(self, attr): return getattr(self.proc, attr) + + def wait(self): + """ + Wait for the process and return its status code. + + Raise + GitCommandError if the return status is not 0 + """ + status = self.proc.wait() + if status != 0: + raise GitCommandError(self.args, status, self.proc.stderr.read()) + # END status handling + return status + def __init__(self, git_dir=None): @@ -184,7 +202,7 @@ class Git(object): **extra ) if as_process: - return self.AutoInterrupt(proc) + return self.AutoInterrupt(proc, command) # Wait for the process to return status = 0 -- cgit v1.2.1 From f41d42ee7e264ce2fc32cea555e5f666fa1b1fe9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 4 Nov 2009 13:17:37 +0100 Subject: Improved cmd error handling in case an invalid revision is specified for an object repo.tree: improved to be less restricting --- lib/git/cmd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 4b4b84af..fb6f2998 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -336,8 +336,9 @@ class Git(object): """ tokens = header_line.split() if len(tokens) != 3: - raise ValueError( "SHA named %s could not be resolved" % tokens[0] ) - + raise ValueError("SHA named %s could not be resolved" % tokens[0] ) + if len(tokens[0]) != 40: + raise ValueError("Failed to parse header: %r" % header_line) return (tokens[0], tokens[1], int(tokens[2])) def __prepare_ref(self, ref): -- cgit v1.2.1