From c5083752d5d02d6c46bc2f18ba53a28d119af5b9 Mon Sep 17 00:00:00 2001 From: Govind Salinas Date: Fri, 6 Jun 2008 22:49:15 -0500 Subject: Determine git_dir and git_work_tree in python. Calling git to find the git_dir and work_tree is very costly. This patch uses the same mechanisim to find the git_dir as native git does without shelling out. Signed-off-by: Govind Salinas --- lib/git/cmd.py | 81 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 31 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index cf0f066d..55e81e78 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -15,40 +15,59 @@ class Git(MethodMissingMixin): def __init__(self, git_dir=None): super(Git, self).__init__() if git_dir: - self.find_git_dir(git_dir) + self._location = os.path.abspath(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 + self._location = os.getcwd() + self.refresh() + + def refresh(self): + self._git_dir = None + self._is_in_repo = not not self.get_git_dir() + self._work_tree = None + + def _is_git_dir(self, d): + """ This is taken from the git setup.c:is_git_directory + function.""" + + if os.path.isdir(d) and \ + os.path.isdir(os.path.join(d, 'objects')) and \ + os.path.isdir(os.path.join(d, 'refs')): + headref = os.path.join(d, 'HEAD') + return os.path.isfile(headref) or \ + (os.path.islink(headref) and + os.readlink(headref).startswith('refs')) + return False + + def get_git_dir(self): + if not self._git_dir: + self._git_dir = os.getenv('GIT_DIR') + if self._git_dir and self._is_git_dir(self._git_dir): + return self._git_dir + curpath = self._location + while curpath: + if self._is_git_dir(curpath): + self._git_dir = curpath + break + gitpath = os.path.join(curpath, '.git') + if self._is_git_dir(gitpath): + self._git_dir = gitpath + break + curpath, dummy = os.path.split(curpath) + if not dummy: + break + return self._git_dir + + def get_work_tree(self): + if not self._work_tree: + self._work_tree = os.getenv('GIT_WORK_TREE') + if not self._work_tree or not os.path.isdir(self._work_tree): + self._work_tree = os.path.abspath( + os.path.join(self._git_dir, '..')) + return self._work_tree @property def get_dir(self): - return self.git_dir + return self._git_dir def execute(self, command, istream=None, @@ -96,7 +115,7 @@ class Git(MethodMissingMixin): # Start the process proc = subprocess.Popen(command, - cwd=self.git_dir, + cwd=self._git_dir, stdin=istream, stderr=stderr, stdout=subprocess.PIPE -- cgit v1.2.1 From abc2e538c6f2fe50f93e6c3fe927236bb41f94f4 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 12 Jun 2008 03:01:09 -0700 Subject: cmd: properly handle cwd for repos with work trees This is a fix on top of Govind's latest performance improvement. self._cwd was always set to self._git_dir which means a lot of commands that require work trees were not available to GitPython. Execute now uses self._cwd which is equal to self._git_dir by default, and self.get_work_tree() if a work tree exists. Signed-off-by: David Aguilar --- lib/git/cmd.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 55e81e78..d3a7e36b 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -24,6 +24,9 @@ class Git(MethodMissingMixin): self._git_dir = None self._is_in_repo = not not self.get_git_dir() self._work_tree = None + self._cwd = self._git_dir + if self._git_dir: + self._cwd = self.get_work_tree() def _is_git_dir(self, d): """ This is taken from the git setup.c:is_git_directory @@ -115,7 +118,7 @@ class Git(MethodMissingMixin): # Start the process proc = subprocess.Popen(command, - cwd=self._git_dir, + cwd=self._cwd, stdin=istream, stderr=stderr, stdout=subprocess.PIPE -- cgit v1.2.1 From b38020ae17ed9f83af75ce176e96267dcce6ecbd Mon Sep 17 00:00:00 2001 From: Sverre Rabbelier Date: Fri, 13 Jun 2008 19:40:14 +0200 Subject: Improved the GIT_PYTHON_TRACE=full output format It now also shows stderr if there was any on it, and only shows stdout if there was any output. Also added a '->' between the command and the return value as a visual clue. --- lib/git/cmd.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index cf0f066d..1eed1f84 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -107,6 +107,10 @@ class Git(MethodMissingMixin): status = proc.wait() proc.stdout.close() + if proc.stderr: + stderr_value = proc.stderr.read() + proc.stderr.close() + # Strip off trailing whitespace by default if not with_raw_output: stdout_value = stdout_value.rstrip() @@ -118,7 +122,12 @@ class Git(MethodMissingMixin): % (str(command), status)) if GIT_PYTHON_TRACE == 'full': - print "%s %d: '%s'" % (command, status, stdout_value) + if stderr_value: + print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value) + elif stdout_value: + print "%s -> %d: '%s'" % (command, status, stdout_value) + else: + print "%s -> %d" % (command, status) # Allow access to the command's status code if with_status: -- cgit v1.2.1 From 0aa1ce356c7c3d53d6ee035b4c7bcf425e108cdc Mon Sep 17 00:00:00 2001 From: Sverre Rabbelier Date: Fri, 13 Jun 2008 20:10:01 +0200 Subject: Added a with_keep_cwd option When executing commands, if the with_keep_cwd option is specified, the current working directory will be set to os.getcwd() instead of the directory containing the .git directory. --- lib/git/cmd.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 1eed1f84..8a3de181 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -56,6 +56,7 @@ class Git(MethodMissingMixin): with_stderr=False, with_exceptions=False, with_raw_output=False, + with_keep_cwd=False, ): """ Handles executing the command on the shell and consumes and returns @@ -79,6 +80,9 @@ class Git(MethodMissingMixin): ``with_raw_output`` Whether to avoid stripping off trailing whitespace. + ``with_keep_cwd`` + Whether to use the current working directory from os.getcwd(). + Returns str(output) # with_status = False (Default) tuple(int(status), str(output)) # with_status = True @@ -94,9 +98,15 @@ class Git(MethodMissingMixin): else: stderr = subprocess.PIPE + # Allow the user to have the command executed in their working dir. + if with_keep_cwd: + cwd = os.getcwd() + else: + cwd=self.git_dir + # Start the process proc = subprocess.Popen(command, - cwd=self.git_dir, + cwd=cwd, stdin=istream, stderr=stderr, stdout=subprocess.PIPE @@ -183,6 +193,7 @@ class Git(MethodMissingMixin): with_stderr = kwargs.pop("with_stderr", None) with_exceptions = kwargs.pop("with_exceptions", None) with_raw_output = kwargs.pop("with_raw_output", None) + with_keep_cwd = kwargs.pop("with_keep_cwd", None) # Prepare the argument list opt_args = self.transform_kwargs(**kwargs) @@ -198,4 +209,5 @@ class Git(MethodMissingMixin): with_stderr = with_stderr, with_exceptions = with_exceptions, with_raw_output = with_raw_output, + with_keep_cwd = with_keep_cwd, ) -- cgit v1.2.1 From fd5f111439e7a2e730b602a155aa533c68badbf8 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 15 Jun 2008 16:45:29 -0700 Subject: cmd: better support for bare repositories In order to avoid the expense of parsing .git/config just to know whether or not a repository is bare at __init__ time, we just pass an optional flag to Git.__init__(): bare_repo with a default value of False. Repo.init_bare() was updated to pass this flag. We could have an optional Git.read_bare_status() function that does the expensive lookup. Then, users can optionally call it at runtime if they really need to know whether or not a repository is bare. That seems like a decent tradeoff between speed, correctness, and common use cases. Signed-off-by: David Aguilar --- lib/git/cmd.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index d3a7e36b..1a764ed3 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -12,12 +12,13 @@ class Git(MethodMissingMixin): """ The Git class manages communication with the Git binary """ - def __init__(self, git_dir=None): + def __init__(self, git_dir=None, bare_repo=False): super(Git, self).__init__() if git_dir: self._location = os.path.abspath(git_dir) else: self._location = os.getcwd() + self._is_bare_repo = bare_repo self.refresh() def refresh(self): @@ -25,7 +26,7 @@ class Git(MethodMissingMixin): self._is_in_repo = not not self.get_git_dir() self._work_tree = None self._cwd = self._git_dir - if self._git_dir: + if self._git_dir and not self._is_bare_repo: self._cwd = self.get_work_tree() def _is_git_dir(self, d): @@ -61,6 +62,8 @@ class Git(MethodMissingMixin): return self._git_dir def get_work_tree(self): + if self._is_bare_repo: + return None if not self._work_tree: self._work_tree = os.getenv('GIT_WORK_TREE') if not self._work_tree or not os.path.isdir(self._work_tree): -- cgit v1.2.1 From 3980f11a9411d0b487b73279346cfae938845e7a Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 15 Jun 2008 17:03:53 -0700 Subject: cmd: rename with_keep_cwd to keep_cwd Having execute() use a different directory is an important piece of API information so I added more documentation about it and renamed the flag to just "keep_cwd" since that's shorter and simpler. Signed-off-by: David Aguilar --- lib/git/cmd.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'lib/git/cmd.py') diff --git a/lib/git/cmd.py b/lib/git/cmd.py index bd8da4ca..029fcbc9 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -77,11 +77,11 @@ class Git(MethodMissingMixin): def execute(self, command, istream=None, + keep_cwd=False, with_status=False, with_stderr=False, with_exceptions=False, with_raw_output=False, - with_keep_cwd=False, ): """ Handles executing the command on the shell and consumes and returns @@ -93,6 +93,11 @@ class Git(MethodMissingMixin): ``istream`` Standard input filehandle passed to subprocess.Popen. + ``keep_cwd`` + Whether to use the current working directory from os.getcwd(). + GitPython uses get_work_tree() as its working directory by + default and get_git_dir() for bare repositories. + ``with_status`` Whether to return a (status, str) tuple. @@ -105,9 +110,6 @@ class Git(MethodMissingMixin): ``with_raw_output`` Whether to avoid stripping off trailing whitespace. - ``with_keep_cwd`` - Whether to use the current working directory from os.getcwd(). - Returns str(output) # with_status = False (Default) tuple(int(status), str(output)) # with_status = True @@ -124,7 +126,7 @@ class Git(MethodMissingMixin): stderr = subprocess.PIPE # Allow the user to have the command executed in their working dir. - if with_keep_cwd: + if keep_cwd: cwd = os.getcwd() else: cwd=self._cwd @@ -214,11 +216,11 @@ class Git(MethodMissingMixin): # Handle optional arguments prior to calling transform_kwargs # otherwise these'll end up in args, which is bad. istream = kwargs.pop("istream", None) + keep_cwd = kwargs.pop("keep_cwd", 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) - with_keep_cwd = kwargs.pop("with_keep_cwd", None) # Prepare the argument list opt_args = self.transform_kwargs(**kwargs) @@ -230,9 +232,9 @@ class Git(MethodMissingMixin): return self.execute(call, istream = istream, + keep_cwd = keep_cwd, with_status = with_status, with_stderr = with_stderr, with_exceptions = with_exceptions, with_raw_output = with_raw_output, - with_keep_cwd = with_keep_cwd, ) -- cgit v1.2.1