| |   |
| 12 | 12 | """ |
| 13 | 13 | The Git class manages communication with the Git binary |
| 14 | 14 | """ |
| 15 | | def __init__(self, git_dir=None): |
| 15 | def __init__(self, git_dir=None, bare_repo=False): |
| 16 | 16 | super(Git, self).__init__() |
| 17 | 17 | if git_dir: |
| 18 | | self.find_git_dir(git_dir) |
| 18 | self._location = os.path.abspath(git_dir) |
| 19 | 19 | else: |
| 20 | | self.find_git_dir(os.getcwd()) |
| 21 | | |
| 22 | | def find_git_dir(self, path): |
| 23 | | """Find the best value for self.git_dir. |
| 24 | | For bare repositories, this is the path to the bare repository. |
| 25 | | For repositories with work trees, this is the work tree path. |
| 26 | | |
| 27 | | When barerepo.git is passed in, self.git_dir = barerepo.git |
| 28 | | When worktree/.git is passed in, self.git_dir = worktree |
| 29 | | When worktree is passed in, self.git_dir = worktree |
| 30 | | """ |
| 31 | | |
| 32 | | path = os.path.abspath(path) |
| 33 | | self.git_dir = path |
| 34 | | |
| 35 | | cdup = self.execute(["git", "rev-parse", "--show-cdup"]) |
| 36 | | if cdup: |
| 37 | | path = os.path.abspath(os.path.join(self.git_dir, cdup)) |
| 38 | | else: |
| 39 | | is_bare_repository =\ |
| 40 | | self.rev_parse(is_bare_repository=True) == "true" |
| 41 | | is_inside_git_dir =\ |
| 42 | | self.rev_parse(is_inside_git_dir=True) == "true" |
| 43 | | |
| 44 | | if not is_bare_repository and is_inside_git_dir: |
| 45 | | path = os.path.dirname(self.git_dir) |
| 46 | | |
| 47 | | self.git_dir = path |
| 20 | self._location = os.getcwd() |
| 21 | self._is_bare_repo = bare_repo |
| 22 | self.refresh() |
| 23 | |
| 24 | def refresh(self): |
| 25 | self._git_dir = None |
| 26 | self._is_in_repo = not not self.get_git_dir() |
| 27 | self._work_tree = None |
| 28 | self._cwd = self._git_dir |
| 29 | if self._git_dir and not self._is_bare_repo: |
| 30 | self._cwd = self.get_work_tree() |
| 31 | |
| 32 | def _is_git_dir(self, d): |
| 33 | """ This is taken from the git setup.c:is_git_directory |
| 34 | function.""" |
| 35 | |
| 36 | if os.path.isdir(d) and \ |
| 37 | os.path.isdir(os.path.join(d, 'objects')) and \ |
| 38 | os.path.isdir(os.path.join(d, 'refs')): |
| 39 | headref = os.path.join(d, 'HEAD') |
| 40 | return os.path.isfile(headref) or \ |
| 41 | (os.path.islink(headref) and |
| 42 | os.readlink(headref).startswith('refs')) |
| 43 | return False |
| 44 | |
| 45 | def get_git_dir(self): |
| 46 | if not self._git_dir: |
| 47 | self._git_dir = os.getenv('GIT_DIR') |
| 48 | if self._git_dir and self._is_git_dir(self._git_dir): |
| 49 | return self._git_dir |
| 50 | curpath = self._location |
| 51 | while curpath: |
| 52 | if self._is_git_dir(curpath): |
| 53 | self._git_dir = curpath |
| 54 | break |
| 55 | gitpath = os.path.join(curpath, '.git') |
| 56 | if self._is_git_dir(gitpath): |
| 57 | self._git_dir = gitpath |
| 58 | break |
| 59 | curpath, dummy = os.path.split(curpath) |
| 60 | if not dummy: |
| 61 | break |
| 62 | return self._git_dir |
| 63 | |
| 64 | def get_work_tree(self): |
| 65 | if self._is_bare_repo: |
| 66 | return None |
| 67 | if not self._work_tree: |
| 68 | self._work_tree = os.getenv('GIT_WORK_TREE') |
| 69 | if not self._work_tree or not os.path.isdir(self._work_tree): |
| 70 | self._work_tree = os.path.abspath( |
| 71 | os.path.join(self._git_dir, '..')) |
| 72 | return self._work_tree |
| 48 | 73 | |
| 49 | 74 | @property |
| 50 | 75 | def get_dir(self): |
| 51 | | return self.git_dir |
| 76 | return self._git_dir |
| 52 | 77 | |
| 53 | 78 | def execute(self, command, |
| 54 | 79 | istream=None, |
| 80 | keep_cwd=False, |
| 55 | 81 | with_status=False, |
| 56 | 82 | with_stderr=False, |
| 57 | 83 | with_exceptions=False, |
| … | … | |
| 93 | 93 | ``istream`` |
| 94 | 94 | Standard input filehandle passed to subprocess.Popen. |
| 95 | 95 | |
| 96 | ``keep_cwd`` |
| 97 | Whether to use the current working directory from os.getcwd(). |
| 98 | GitPython uses get_work_tree() as its working directory by |
| 99 | default and get_git_dir() for bare repositories. |
| 100 | |
| 96 | 101 | ``with_status`` |
| 97 | 102 | Whether to return a (status, str) tuple. |
| 98 | 103 | |
| … | … | |
| 125 | 125 | else: |
| 126 | 126 | stderr = subprocess.PIPE |
| 127 | 127 | |
| 128 | # Allow the user to have the command executed in their working dir. |
| 129 | if keep_cwd: |
| 130 | cwd = os.getcwd() |
| 131 | else: |
| 132 | cwd=self._cwd |
| 133 | |
| 128 | 134 | # Start the process |
| 129 | 135 | proc = subprocess.Popen(command, |
| 130 | | cwd=self.git_dir, |
| 136 | cwd=cwd, |
| 131 | 137 | stdin=istream, |
| 132 | 138 | stderr=stderr, |
| 133 | 139 | stdout=subprocess.PIPE |
| … | … | |
| 144 | 144 | status = proc.wait() |
| 145 | 145 | proc.stdout.close() |
| 146 | 146 | |
| 147 | if proc.stderr: |
| 148 | stderr_value = proc.stderr.read() |
| 149 | proc.stderr.close() |
| 150 | |
| 147 | 151 | # Strip off trailing whitespace by default |
| 148 | 152 | if not with_raw_output: |
| 149 | 153 | stdout_value = stdout_value.rstrip() |
| … | … | |
| 159 | 159 | % (str(command), status)) |
| 160 | 160 | |
| 161 | 161 | if GIT_PYTHON_TRACE == 'full': |
| 162 | | print "%s %d: '%s'" % (command, status, stdout_value) |
| 162 | if stderr_value: |
| 163 | print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value) |
| 164 | elif stdout_value: |
| 165 | print "%s -> %d: '%s'" % (command, status, stdout_value) |
| 166 | else: |
| 167 | print "%s -> %d" % (command, status) |
| 163 | 168 | |
| 164 | 169 | # Allow access to the command's status code |
| 165 | 170 | if with_status: |
| … | … | |
| 216 | 216 | # Handle optional arguments prior to calling transform_kwargs |
| 217 | 217 | # otherwise these'll end up in args, which is bad. |
| 218 | 218 | istream = kwargs.pop("istream", None) |
| 219 | keep_cwd = kwargs.pop("keep_cwd", None) |
| 219 | 220 | with_status = kwargs.pop("with_status", None) |
| 220 | 221 | with_stderr = kwargs.pop("with_stderr", None) |
| 221 | 222 | with_exceptions = kwargs.pop("with_exceptions", None) |
| … | … | |
| 232 | 232 | |
| 233 | 233 | return self.execute(call, |
| 234 | 234 | istream = istream, |
| 235 | keep_cwd = keep_cwd, |
| 235 | 236 | with_status = with_status, |
| 236 | 237 | with_stderr = with_stderr, |
| 237 | 238 | with_exceptions = with_exceptions, |
| toggle raw diff |
--- a/lib/git/cmd.py
+++ b/lib/git/cmd.py
@@ -12,46 +12,72 @@ 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.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._is_bare_repo = bare_repo
+ self.refresh()
+
+ def refresh(self):
+ 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 and not self._is_bare_repo:
+ self._cwd = self.get_work_tree()
+
+ 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 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):
+ 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,
+ keep_cwd=False,
with_status=False,
with_stderr=False,
with_exceptions=False,
@@ -67,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.
@@ -94,9 +125,15 @@ class Git(MethodMissingMixin):
else:
stderr = subprocess.PIPE
+ # Allow the user to have the command executed in their working dir.
+ if keep_cwd:
+ cwd = os.getcwd()
+ else:
+ cwd=self._cwd
+
# Start the process
proc = subprocess.Popen(command,
- cwd=self.git_dir,
+ cwd=cwd,
stdin=istream,
stderr=stderr,
stdout=subprocess.PIPE
@@ -107,6 +144,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 +159,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:
@@ -170,6 +216,7 @@ 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)
@@ -185,6 +232,7 @@ 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, |
| |   |
| 12 | 12 | class Repo(object): |
| 13 | 13 | DAEMON_EXPORT_FILE = 'git-daemon-export-ok' |
| 14 | 14 | |
| 15 | | def __init__(self, path): |
| 15 | def __init__(self, path=None): |
| 16 | 16 | """ |
| 17 | 17 | Create a new Repo instance |
| 18 | 18 | |
| … | … | |
| 27 | 27 | Returns |
| 28 | 28 | ``GitPython.Repo`` |
| 29 | 29 | """ |
| 30 | | epath = os.path.abspath(path) |
| 30 | if not os.path.exists(path): |
| 31 | raise NoSuchPathError(path) |
| 32 | |
| 33 | self.git = Git(path) |
| 34 | self.path = self.git.get_git_dir() |
| 35 | if not self.path: |
| 36 | raise InvalidGitRepositoryError(path) |
| 37 | epath = self.git.get_work_tree() |
| 31 | 38 | |
| 32 | 39 | if os.path.exists(os.path.join(epath, '.git')): |
| 33 | | self.path = os.path.join(epath, '.git') |
| 34 | 40 | self.bare = False |
| 35 | | elif os.path.exists(epath) and re.search('\.git$', epath): |
| 36 | | self.path = epath |
| 41 | elif os.path.exists(epath) and epath.endswith('.git'): |
| 37 | 42 | self.bare = True |
| 38 | 43 | elif os.path.exists(epath): |
| 39 | 44 | raise InvalidGitRepositoryError(epath) |
| 40 | 45 | else: |
| 41 | 46 | raise NoSuchPathError(epath) |
| 42 | | self.git = Git(self.path) |
| 47 | |
| 43 | 48 | |
| 44 | 49 | @property |
| 45 | 50 | def description(self): |
| … | … | |
| 280 | 280 | if mkdir and not os.path.exists(path): |
| 281 | 281 | os.makedirs(path, 0755) |
| 282 | 282 | |
| 283 | | git = Git(path) |
| 283 | git = Git(path, bare_repo=True) |
| 284 | 284 | output = git.init(**kwargs) |
| 285 | 285 | return Repo(path) |
| 286 | 286 | create = init_bare |
| toggle raw diff |
--- a/lib/git/repo.py
+++ b/lib/git/repo.py
@@ -12,7 +12,7 @@ from tree import Tree
class Repo(object):
DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
- def __init__(self, path):
+ def __init__(self, path=None):
"""
Create a new Repo instance
@@ -27,19 +27,24 @@ class Repo(object):
Returns
``GitPython.Repo``
"""
- epath = os.path.abspath(path)
+ if not os.path.exists(path):
+ raise NoSuchPathError(path)
+
+ self.git = Git(path)
+ self.path = self.git.get_git_dir()
+ if not self.path:
+ raise InvalidGitRepositoryError(path)
+ epath = self.git.get_work_tree()
if os.path.exists(os.path.join(epath, '.git')):
- self.path = os.path.join(epath, '.git')
self.bare = False
- elif os.path.exists(epath) and re.search('\.git$', epath):
- self.path = epath
+ elif os.path.exists(epath) and epath.endswith('.git'):
self.bare = True
elif os.path.exists(epath):
raise InvalidGitRepositoryError(epath)
else:
raise NoSuchPathError(epath)
- self.git = Git(self.path)
+
@property
def description(self):
@@ -275,7 +280,7 @@ class Repo(object):
if mkdir and not os.path.exists(path):
os.makedirs(path, 0755)
- git = Git(path)
+ git = Git(path, bare_repo=True)
output = git.init(**kwargs)
return Repo(path)
create = init_bare |