summaryrefslogtreecommitdiff
path: root/lib/git/cmd.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git/cmd.py')
-rw-r--r--lib/git/cmd.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/lib/git/cmd.py b/lib/git/cmd.py
new file mode 100644
index 00000000..422552a7
--- /dev/null
+++ b/lib/git/cmd.py
@@ -0,0 +1,190 @@
+import os
+import subprocess
+import re
+from utils import *
+from method_missing import MethodMissingMixin
+from errors import GitCommandError
+
+# Enables debugging of GitPython's git commands
+GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)
+
+class Git(MethodMissingMixin):
+ """
+ The Git class manages communication with the Git binary
+ """
+ def __init__(self, git_dir=None):
+ super(Git, self).__init__()
+ if git_dir:
+ self.find_git_dir(git_dir)
+ else:
+ self.find_git_dir(os.getcwd())
+
+ def find_git_dir(self, path):
+ """Find the best value for self.git_dir.
+ For bare repositories, this is the path to the bare repository.
+ For repositories with work trees, this is the work tree path.
+
+ When barerepo.git is passed in, self.git_dir = barerepo.git
+ When worktree/.git is passed in, self.git_dir = worktree
+ When worktree is passed in, self.git_dir = worktree
+ """
+
+ path = os.path.abspath(path)
+ self.git_dir = path
+
+ cdup = self.execute(["git", "rev-parse", "--show-cdup"])
+ if cdup:
+ path = os.path.abspath(os.path.join(self.git_dir, cdup))
+ else:
+ is_bare_repository =\
+ self.rev_parse(is_bare_repository=True) == "true"
+ is_inside_git_dir =\
+ self.rev_parse(is_inside_git_dir=True) == "true"
+
+ if not is_bare_repository and is_inside_git_dir:
+ path = os.path.dirname(self.git_dir)
+
+ self.git_dir = path
+
+ @property
+ def get_dir(self):
+ return self.git_dir
+
+ def execute(self, command,
+ istream = None,
+ with_status = False,
+ with_stderr = False,
+ with_exceptions = False,
+ with_raw_output = False,
+ ):
+ """
+ Handles executing the command on the shell and consumes and returns
+ the returned information (stdout)
+
+ ``command``
+ The command argument list to execute
+
+ ``istream``
+ Standard input filehandle passed to subprocess.Popen.
+
+ ``with_status``
+ Whether to return a (status, str) tuple.
+
+ ``with_stderr``
+ Whether to combine stderr into the output.
+
+ ``with_exceptions``
+ Whether to raise an exception when git returns a non-zero status.
+
+ ``with_raw_output``
+ Whether to avoid stripping off trailing whitespace.
+
+ Returns
+ str(output) # with_status = False (Default)
+ tuple(int(status), str(output)) # with_status = True
+ """
+
+ if GIT_PYTHON_TRACE:
+ print command
+
+ # Allow stderr to be merged into stdout when with_stderr is True.
+ # Otherwise, throw stderr away.
+ if with_stderr:
+ stderr = subprocess.STDOUT
+ else:
+ stderr = subprocess.PIPE
+
+ # Start the process
+ proc = subprocess.Popen(command,
+ cwd = self.git_dir,
+ stdin = istream,
+ stderr = stderr,
+ stdout = subprocess.PIPE
+ )
+
+ # Wait for the process to return
+ stdout_value, err = proc.communicate()
+ proc.stdout.close()
+ if proc.stderr:
+ proc.stderr.close()
+
+ # Strip off trailing whitespace by default
+ if not with_raw_output:
+ stdout_value = stdout_value.rstrip()
+
+ # Grab the exit status
+ status = proc.poll()
+ if with_exceptions and status != 0:
+ raise GitCommandError("%s returned exit status %d"
+ % (str(command), status))
+
+ # Allow access to the command's status code
+ if with_status:
+ return (status, stdout_value)
+ else:
+ return stdout_value
+
+ def transform_kwargs(self, **kwargs):
+ """
+ Transforms Python style kwargs into git command line options.
+ """
+ args = []
+ for k, v in kwargs.items():
+ if len(k) == 1:
+ if v is True:
+ args.append("-%s" % k)
+ else:
+ args.append("-%s%s" % (k, v))
+ else:
+ if v is True:
+ args.append("--%s" % dashify(k))
+ else:
+ args.append("--%s=%s" % (dashify(k), v))
+ return args
+
+ def method_missing(self, method, *args, **kwargs):
+ """
+ Run the given git command with the specified arguments and return
+ the result as a String
+
+ ``method``
+ is the command
+
+ ``args``
+ is the list of arguments
+
+ ``kwargs``
+ is a dict of keyword arguments.
+ This function accepts the same optional keyword arguments
+ as execute().
+
+ Examples
+ git.rev_list('master', max_count=10, header=True)
+
+ Returns
+ Same as execute()
+ """
+
+ # Handle optional arguments prior to calling transform_kwargs
+ # otherwise these'll end up in args, which is bad.
+ istream = kwargs.pop("istream", None)
+ with_status = kwargs.pop("with_status", None)
+ with_stderr = kwargs.pop("with_stderr", None)
+ with_exceptions = kwargs.pop("with_exceptions", None)
+ with_raw_output = kwargs.pop("with_raw_output", None)
+
+ # Prepare the argument list
+ opt_args = self.transform_kwargs(**kwargs)
+ ext_args = map(str, args)
+ args = opt_args + ext_args
+
+ call = ["git", dashify(method)]
+ call.extend(args)
+
+ return self.execute(call,
+ istream = istream,
+ with_status = with_status,
+ with_stderr = with_stderr,
+ with_exceptions = with_exceptions,
+ with_raw_output = with_raw_output,
+ )