summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2017-09-28 15:44:15 +0200
committerGitHub <noreply@github.com>2017-09-28 15:44:15 +0200
commit2eb6cf0855232da2b8f37785677d1f58c8e86817 (patch)
treeecb856bea6ed63e9c2baf8365a4e0a9030b0b3a3
parent2af601d5800a39ab04e9fe6cf22ef7b917ab5d67 (diff)
parenta56136f9cb48a17ae15b59ae0f3f99d9303b1cb1 (diff)
downloadgitpython-2eb6cf0855232da2b8f37785677d1f58c8e86817.tar.gz
Merge pull request #640 from njalerikson/adding_setup_for_git_executable
Adding setup for git executable
-rw-r--r--AUTHORS1
-rw-r--r--git/__init__.py21
-rw-r--r--git/cmd.py142
-rw-r--r--git/remote.py41
-rw-r--r--git/test/test_git.py9
5 files changed, 196 insertions, 18 deletions
diff --git a/AUTHORS b/AUTHORS
index f0c02e33..0d3bdacf 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,6 +19,7 @@ Contributors are:
-Timothy B. Hartman <tbhartman _at_ gmail.com>
-Konstantin Popov <konstantin.popov.89 _at_ yandex.ru>
-Peter Jones <pjones _at_ redhat.com>
+-Ken Odegard <ken.odegard _at_ gmail.com>
-Alexis Horgix Chotard
Portions derived from other open source works and are clearly marked.
diff --git a/git/__init__.py b/git/__init__.py
index 75247dbf..74609e79 100644
--- a/git/__init__.py
+++ b/git/__init__.py
@@ -60,3 +60,24 @@ except GitError as exc:
__all__ = [name for name, obj in locals().items()
if not (name.startswith('_') or inspect.ismodule(obj))]
+
+
+#{ Initialize git executable path
+GIT_OK = None
+
+def refresh(path=None):
+ """Convenience method for setting the git executable path."""
+ global GIT_OK
+ GIT_OK = False
+
+ if not Git.refresh(path=path):
+ return
+ if not FetchInfo.refresh():
+ return
+
+ GIT_OK = True
+#} END initialize git executable path
+
+#################
+refresh()
+#################
diff --git a/git/cmd.py b/git/cmd.py
index a2fcf50d..312f933a 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -17,6 +17,7 @@ from subprocess import (
import subprocess
import sys
import threading
+from textwrap import dedent
from git.compat import (
string_types,
@@ -182,16 +183,141 @@ class Git(LazyMixin):
# Enables debugging of GitPython's git commands
GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)
- # Provide the full path to the git executable. Otherwise it assumes git is in the path
- _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE"
- GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name)
-
# If True, a shell will be used when executing git commands.
# This should only be desirable on Windows, see https://github.com/gitpython-developers/GitPython/pull/126
# and check `git/test_repo.py:TestRepo.test_untracked_files()` TC for an example where it is required.
# Override this value using `Git.USE_SHELL = True`
USE_SHELL = False
+ # Provide the full path to the git executable. Otherwise it assumes git is in the path
+ _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE"
+ _refresh_env_var = "GIT_PYTHON_REFRESH"
+ GIT_PYTHON_GIT_EXECUTABLE = None
+ # note that the git executable is actually found during the refresh step in
+ # the top level __init__
+
+ @classmethod
+ def refresh(cls, path=None):
+ """This gets called by the refresh function (see the top level
+ __init__).
+ """
+ # discern which path to refresh with
+ if path is not None:
+ new_git = os.path.expanduser(path)
+ new_git = os.path.abspath(new_git)
+ else:
+ new_git = os.environ.get(cls._git_exec_env_var, cls.git_exec_name)
+
+ # keep track of the old and new git executable path
+ old_git = cls.GIT_PYTHON_GIT_EXECUTABLE
+ cls.GIT_PYTHON_GIT_EXECUTABLE = new_git
+
+ # test if the new git executable path is valid
+
+ if sys.version_info < (3,):
+ # - a GitCommandNotFound error is spawned by ourselves
+ # - a OSError is spawned if the git executable provided
+ # cannot be executed for whatever reason
+ exceptions = (GitCommandNotFound, OSError)
+ else:
+ # - a GitCommandNotFound error is spawned by ourselves
+ # - a PermissionError is spawned if the git executable provided
+ # cannot be executed for whatever reason
+ exceptions = (GitCommandNotFound, PermissionError)
+
+ has_git = False
+ try:
+ cls().version()
+ has_git = True
+ except exceptions:
+ pass
+
+ # warn or raise exception if test failed
+ if not has_git:
+ err = dedent("""\
+ Bad git executable.
+ The git executable must be specified in one of the following ways:
+ - be included in your $PATH
+ - be set via $%s
+ - explicitly set via git.refresh()
+ """) % cls._git_exec_env_var
+
+ # revert to whatever the old_git was
+ cls.GIT_PYTHON_GIT_EXECUTABLE = old_git
+
+ if old_git is None:
+ # on the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is
+ # None) we only are quiet, warn, or error depending on the
+ # GIT_PYTHON_REFRESH value
+
+ # determine what the user wants to happen during the initial
+ # refresh we expect GIT_PYTHON_REFRESH to either be unset or
+ # be one of the following values:
+ # 0|q|quiet|s|silence
+ # 1|w|warn|warning
+ # 2|r|raise|e|error
+
+ mode = os.environ.get(cls._refresh_env_var, "raise").lower()
+
+ quiet = ["quiet", "q", "silence", "s", "none", "n", "0"]
+ warn = ["warn", "w", "warning", "1"]
+ error = ["error", "e", "raise", "r", "2"]
+
+ if mode in quiet:
+ pass
+ elif mode in warn or mode in error:
+ err = dedent("""\
+ %s
+ All git commands will error until this is rectified.
+
+ This initial warning can be silenced or aggravated in the future by setting the
+ $%s environment variable. Use one of the following values:
+ - %s: for no warning or exception
+ - %s: for a printed warning
+ - %s: for a raised exception
+
+ Example:
+ export %s=%s
+ """) % (
+ err,
+ cls._refresh_env_var,
+ "|".join(quiet),
+ "|".join(warn),
+ "|".join(error),
+ cls._refresh_env_var,
+ quiet[0])
+
+ if mode in warn:
+ print("WARNING: %s" % err)
+ else:
+ raise ImportError(err)
+ else:
+ err = dedent("""\
+ %s environment variable has been set but it has been set with an invalid value.
+
+ Use only the following values:
+ - %s: for no warning or exception
+ - %s: for a printed warning
+ - %s: for a raised exception
+ """) % (
+ cls._refresh_env_var,
+ "|".join(quiet),
+ "|".join(warn),
+ "|".join(error))
+ raise ImportError(err)
+
+ # we get here if this was the init refresh and the refresh mode
+ # was not error, go ahead and set the GIT_PYTHON_GIT_EXECUTABLE
+ # such that we discern the difference between a first import
+ # and a second import
+ cls.GIT_PYTHON_GIT_EXECUTABLE = cls.git_exec_name
+ else:
+ # after the first refresh (when GIT_PYTHON_GIT_EXECUTABLE
+ # is no longer None) we raise an exception
+ raise GitCommandNotFound("git", err)
+
+ return has_git
+
@classmethod
def is_cygwin(cls):
return is_cygwin_git(cls.GIT_PYTHON_GIT_EXECUTABLE)
@@ -828,13 +954,13 @@ class Git(LazyMixin):
- "command options" to be converted by :meth:`transform_kwargs()`;
- the `'insert_kwargs_after'` key which its value must match one of ``*args``,
and any cmd-options will be appended after the matched arg.
-
+
Examples::
-
+
git.rev_list('master', max_count=10, header=True)
-
+
turns into::
-
+
git rev-list max-count 10 --header master
:return: Same as ``execute``"""
diff --git a/git/remote.py b/git/remote.py
index 81719f4b..7261be81 100644
--- a/git/remote.py
+++ b/git/remote.py
@@ -211,17 +211,38 @@ class FetchInfo(object):
_re_fetch_result = re.compile(r'^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([^\s]+)( \(.*\)?$)?')
- _flag_map = {'!': ERROR,
- '+': FORCED_UPDATE,
- '*': 0,
- '=': HEAD_UPTODATE,
- ' ': FAST_FORWARD}
+ _flag_map = {
+ '!': ERROR,
+ '+': FORCED_UPDATE,
+ '*': 0,
+ '=': HEAD_UPTODATE,
+ ' ': FAST_FORWARD,
+ '-': TAG_UPDATE,
+ }
- v = Git().version_info[:2]
- if v >= (2, 10):
- _flag_map['t'] = TAG_UPDATE
- else:
- _flag_map['-'] = TAG_UPDATE
+ @classmethod
+ def refresh(cls):
+ """This gets called by the refresh function (see the top level
+ __init__).
+ """
+ # clear the old values in _flag_map
+ try:
+ del cls._flag_map["t"]
+ except KeyError:
+ pass
+
+ try:
+ del cls._flag_map["-"]
+ except KeyError:
+ pass
+
+ # set the value given the git version
+ if Git().version_info[:2] >= (2, 10):
+ cls._flag_map["t"] = cls.TAG_UPDATE
+ else:
+ cls._flag_map["-"] = cls.TAG_UPDATE
+
+ return True
def __init__(self, ref, flags, note='', old_commit=None, remote_ref_path=None):
"""
diff --git a/git/test/test_git.py b/git/test/test_git.py
index 3c8b6f82..00577e33 100644
--- a/git/test/test_git.py
+++ b/git/test/test_git.py
@@ -10,6 +10,7 @@ import sys
from git import (
Git,
+ refresh,
GitCommandError,
GitCommandNotFound,
Repo,
@@ -156,6 +157,14 @@ class TestGit(TestBase):
type(self.git).GIT_PYTHON_GIT_EXECUTABLE = prev_cmd
# END undo adjustment
+ def test_refresh(self):
+ # test a bad git path refresh
+ self.assertRaises(GitCommandNotFound, refresh, "yada")
+
+ # test a good path refresh
+ path = os.popen("which git").read().strip()
+ refresh(path)
+
def test_options_are_passed_to_git(self):
# This work because any command after git --version is ignored
git_version = self.git(version=True).NoOp()